Skip to content

Commit d96c8f2

Browse files
author
Rob Winch
committedJan 8, 2015
Clarify Redis expirations and cleanup task
1 parent 57c361b commit d96c8f2

File tree

2 files changed

+42
-8
lines changed

2 files changed

+42
-8
lines changed
 

‎docs/src/docs/asciidoc/index.adoc

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,19 @@ For example:
294294

295295
EXPIRE spring:session:sessions:<session-id> 1800
296296

297-
Each session expiration is also tracked to the nearest minute.
298-
This allows a background task to cleanup expired sessions in a deterministic fashion.
297+
Spring Session relies on the expired and delete http://redis.io/topics/notifications[keyspace notifications] from Redis to fire a <<SessionDestroyedEvent>>.
298+
It is the `SessionDestroyedEvent` that ensures resources associated with the Session are cleaned up.
299+
For example, when using Spring Session's WebSocket support the Redis expired or delete event is what triggers any WebSocket connections associated with the session to be closed.
300+
301+
One problem with this approach is that Redis makes no guarantee of when the expired event will be fired if they key has not been accessed.
302+
Specifically the background task that Redis uses to clean up expired keys is a low priority task and may not trigger the key expiration.
303+
For additional details see http://redis.io/topics/notifications[Timing of expired events] section in the Redis documentation.
304+
305+
To circumvent the fact that expired events are not guaranteed to happen we can ensure that each key is accessed when it is expected to expire.
306+
This means that if the TTL is expired on the key, Redis will remove the key and fire the expired event when we try to access they key.
307+
308+
For this reason, each session expiration is also tracked to the nearest minute.
309+
This allows a background task to access the potentially expired sessions to ensure that Redis expired events are fired in a more deterministic fashion.
299310
For example:
300311

301312
SADD spring:session:expirations:<expire-rounded-up-to-nearest-minute> <session-id>
@@ -304,7 +315,9 @@ For example:
304315
The background task will then use these mappings to explicitly request each key.
305316
By accessing they key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
306317

307-
The Redis expiration is still placed on each key to ensure that if the server is down when the session expires, it is still cleaned up.
318+
NOTE: We do not explicitly delete the keys since in some instances there may be a race condition that incorrectly identifies a key as expired when it is not.
319+
Short of using distributed locks (which would kill our performance) there is no way to ensure the consistency of the expiration mapping.
320+
By simply accessing the key, we ensure that the key is only removed if the TTL on that key is expired.
308321

309322
[[api-redisoperationssessionrepository-writes]]
310323
===== Optimized Writes

‎spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,29 @@
9595
* EXPIRE spring:session:sessions:&lt;session-id&gt; 1800
9696
* </pre>
9797
*
98-
* Each session expiration is also tracked to the nearest minute. This allows a
99-
* background task to cleanup expired sessions in a deterministic fashion. For
100-
* example:
98+
* <p>
99+
* Spring Session relies on the expired and delete <a href="http://redis.io/topics/notifications">keyspace notifications</a> from Redis to fire a <<SessionDestroyedEvent>>.
100+
* It is the `SessionDestroyedEvent` that ensures resources associated with the Session are cleaned up.
101+
* For example, when using Spring Session's WebSocket support the Redis expired or delete event is what triggers any
102+
* WebSocket connections associated with the session to be closed.
103+
* </p>
104+
*
105+
* <p>
106+
* One problem with this approach is that Redis makes no guarantee of when the expired event will be fired if they key has not been accessed.
107+
* Specifically the background task that Redis uses to clean up expired keys is a low priority task and may not trigger the key expiration.
108+
* For additional details see <a href="http://redis.io/topics/notifications">Timing of expired events</a> section in the Redis documentation.
109+
* </p>
110+
*
111+
* <p>
112+
* To circumvent the fact that expired events are not guaranteed to happen we can ensure that each key is accessed when it is expected to expire.
113+
* This means that if the TTL is expired on the key, Redis will remove the key and fire the expired event when we try to access they key.
114+
* </p>
115+
*
116+
* <p>
117+
* For this reason, each session expiration is also tracked to the nearest minute.
118+
* This allows a background task to access the potentially expired sessions to ensure that Redis expired events are fired in a more deterministic fashion.
119+
* For example:
120+
* </p>
101121
*
102122
* <pre>
103123
* SADD spring:session:expirations:&lt;expire-rounded-up-to-nearest-minute&gt; &lt;session-id&gt;
@@ -109,8 +129,9 @@
109129
* By accessing they key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
110130
* </p>
111131
* <p>
112-
* The Redis expiration is still placed on each key to ensure that if the server
113-
* is down when the session expires, it is still cleaned up.
132+
* <b>NOTE</b>: We do not explicitly delete the keys since in some instances there may be a race condition that incorrectly identifies a key as expired when it is not.
133+
* Short of using distributed locks (which would kill our performance) there is no way to ensure the consistency of the expiration mapping.
134+
* By simply accessing the key, we ensure that the key is only removed if the TTL on that key is expired.
114135
* </p>
115136
*
116137
* @since 1.0

0 commit comments

Comments
 (0)
Please sign in to comment.