diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc
index e335da46..d9c63439 100644
--- a/docs/src/docs/asciidoc/index.adoc
+++ b/docs/src/docs/asciidoc/index.adoc
@@ -294,8 +294,19 @@ For example:
EXPIRE spring:session:sessions: 1800
-Each session expiration is also tracked to the nearest minute.
-This allows a background task to cleanup expired sessions in a deterministic fashion.
+Spring Session relies on the expired and delete http://redis.io/topics/notifications[keyspace notifications] from Redis to fire a <>.
+It is the `SessionDestroyedEvent` that ensures resources associated with the Session are cleaned up.
+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.
+
+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.
+Specifically the background task that Redis uses to clean up expired keys is a low priority task and may not trigger the key expiration.
+For additional details see http://redis.io/topics/notifications[Timing of expired events] section in the Redis documentation.
+
+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.
+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.
+
+For this reason, each session expiration is also tracked to the nearest minute.
+This allows a background task to access the potentially expired sessions to ensure that Redis expired events are fired in a more deterministic fashion.
For example:
SADD spring:session:expirations:
@@ -304,7 +315,9 @@ For example:
The background task will then use these mappings to explicitly request each key.
By accessing they key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
-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.
+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.
+Short of using distributed locks (which would kill our performance) there is no way to ensure the consistency of the expiration mapping.
+By simply accessing the key, we ensure that the key is only removed if the TTL on that key is expired.
[[api-redisoperationssessionrepository-writes]]
===== Optimized Writes
diff --git a/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java b/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java
index 14737f66..3b064479 100644
--- a/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java
+++ b/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java
@@ -95,9 +95,29 @@ import org.springframework.util.Assert;
* EXPIRE spring:session:sessions:<session-id> 1800
*
*
- * Each session expiration is also tracked to the nearest minute. This allows a
- * background task to cleanup expired sessions in a deterministic fashion. For
- * example:
+ *
+ * Spring Session relies on the expired and delete keyspace notifications from Redis to fire a <>.
+ * It is the `SessionDestroyedEvent` that ensures resources associated with the Session are cleaned up.
+ * 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.
+ *
+ *
+ *
+ * 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.
+ * Specifically the background task that Redis uses to clean up expired keys is a low priority task and may not trigger the key expiration.
+ * For additional details see Timing of expired events section in the Redis documentation.
+ *
+ *
+ *
+ * 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.
+ * 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.
+ *
+ *
+ *
+ * For this reason, each session expiration is also tracked to the nearest minute.
+ * This allows a background task to access the potentially expired sessions to ensure that Redis expired events are fired in a more deterministic fashion.
+ * For example:
+ *
*
*
* SADD spring:session:expirations:<expire-rounded-up-to-nearest-minute> <session-id>
@@ -109,8 +129,9 @@ import org.springframework.util.Assert;
* By accessing they key, rather than deleting it, we ensure that Redis deletes the key for us only if the TTL is expired.
*
*
- * 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.
+ * 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.
+ * Short of using distributed locks (which would kill our performance) there is no way to ensure the consistency of the expiration mapping.
+ * By simply accessing the key, we ensure that the key is only removed if the TTL on that key is expired.
*
*
* @since 1.0