Unveiling Spring Session’s Redis Data Structures: From A to C Keys Explained

This article dissects Spring Session’s Redis implementation, explaining the three key types (A, B, C), their TTL settings, how they enable session sharing, the pitfalls of Redis expiration, concurrency challenges, and the sophisticated cleanup and notification mechanisms Spring Session employs to ensure reliable session management.

Programmer DD
Programmer DD
Programmer DD
Unveiling Spring Session’s Redis Data Structures: From A to C Keys Explained

Spring Session Redis Data Model

Spring Session stores each HTTP session in Redis using three key families, all prefixed with spring:session:

A‑type key : spring:session:sessions:{sessionId} – a hash that holds system fields ( creationTime, lastAccessedTime, maxInactiveInterval) and user attributes ( sessionAttr:name, …). Default TTL is 35 minutes.

B‑type key : spring:session:expirations:{timestamp} – a set that contains the IDs of sessions that should expire during the minute represented by {timestamp}. Default TTL is 30 minutes.

C‑type key : spring:session:sessions:expires:{sessionId} – an empty key whose sole purpose is to trigger a Redis key‑space expiration event. Its TTL is also 30 minutes.

Why Not Rely Only on Redis Expiration?

Redis removes expired keys lazily; the background cleanup task runs with low priority, so a key may remain 1–2 minutes after its TTL. Two practical consequences arise:

Accessing an expired key forces Redis to delete it.

If a key is never accessed, many expired keys can accumulate, causing noticeable delay.

Round 1 – Introducing B‑type Buckets

To guarantee timely removal, Spring Session schedules a task that runs each minute. It computes the bucket timestamp by rounding the session’s expiration time up to the next minute, retrieves the corresponding B‑type set, deletes the set, and “touches” each session key (calls hasKey) so that Redis deletes any already‑expired A‑type keys.

static long roundUpToNextMinute(long timeInMs) {
    Calendar date = Calendar.getInstance();
    date.setTimeInMillis(timeInMs);
    date.add(Calendar.MINUTE, 1);
    date.clear(Calendar.SECOND);
    date.clear(Calendar.MILLISECOND);
    return date.getTimeInMillis();
}
@Scheduled(cron = "${spring.session.cleanup.cron.expression:0 * * * * *}")
public void cleanupExpiredSessions() {
    long now = System.currentTimeMillis();
    long prevMin = roundDownMinute(now);
    String expirationKey = getExpirationKey(prevMin);
    Set<Object> sessionsToExpire = redis.boundSetOps(expirationKey).members();
    redis.delete(expirationKey);
    for (Object session : sessionsToExpire) {
        String sessionKey = getSessionKey((String) session);
        touch(sessionKey); // only accesses the key to trigger Redis deletion if needed
    }
}

Round 2 – Concurrency Issue with B‑type Buckets

If multiple threads renew the same session within the same minute, they may move the session ID between different B‑type buckets out of order, leaving a stale entry. The stale bucket can cause the session to be deleted a minute earlier than intended.

Spring Session avoids this by never deleting the session hash directly; it only accesses the key ( redis.hasKey(key)) and lets Redis decide whether the key should be removed.

Round 3 – Adding C‑type Keys for Reliable Expiration Events

The C‑type key is an empty reference that expires exactly when the session should be considered invalid. Because it holds no data, it adds negligible storage overhead. Its expiration fires a key‑space notification; Spring Session configures ConfigureNotifyKeyspaceEventsAction to listen for these events and publish a SessionExpiredEvent. This allows the application to react even after the A‑type hash has already been removed (the 5‑minute grace period between A‑type TTL and actual expiration).

Practical Takeaways

Fast session lookup via the A‑type hash.

Deterministic expiration handling using B‑type minute buckets and a scheduled cleanup task.

Accurate expiration notifications through C‑type keys, decoupling storage from expiry.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaconcurrencySession ManagementSpring SessionExpiration
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.