summary refs log tree commit diff
path: root/synapse/util/caches/snapshot_cache.py
diff options
context:
space:
mode:
Diffstat (limited to 'synapse/util/caches/snapshot_cache.py')
-rw-r--r--synapse/util/caches/snapshot_cache.py24
1 files changed, 23 insertions, 1 deletions
diff --git a/synapse/util/caches/snapshot_cache.py b/synapse/util/caches/snapshot_cache.py
index 8a7ca47a86..09f00afbc5 100644
--- a/synapse/util/caches/snapshot_cache.py
+++ b/synapse/util/caches/snapshot_cache.py
@@ -17,8 +17,28 @@ from synapse.util.async import ObservableDeferred
 
 
 class SnapshotCache(object):
+    """Cache for snapshots like the response of /initialSync.
+    The response of initialSync only has to be a recent snapshot of the
+    server state. It shouldn't matter to clients if it is a few minutes out
+    of date.
 
-    DURATION_MS = 5 * 60 * 1000  # Cache results for 2 minutes.
+    This caches a deferred response. Until the deferred completes it will be
+    returned from the cache. This means that if the client retries the request
+    while the response is still being computed, that original response will be
+    used rather than trying to compute a new response.
+
+    Once the deferred completes it will removed from the cache after 5 minutes.
+    We delay removing it from the cache because a client retrying its request
+    could race with us finishing computing the response.
+
+    Rather than tracking precisely how long something has been in the cache we
+    keep two generations of completed responses. Every 5 minutes discard the
+    old generation, move the new generation to the old generation, and set the
+    new generation to be empty. This means that a result will be in the cache
+    somewhere between 5 and 10 minutes.
+    """
+
+    DURATION_MS = 5 * 60 * 1000  # Cache results for 5 minutes.
 
     def __init__(self):
         self.pending_result_cache = {}  # Request that haven't finished yet.
@@ -51,6 +71,8 @@ class SnapshotCache(object):
         result = self.pending_result_cache.get(key, result)
         if result is not None:
             return result.observe()
+        else:
+            return None
 
     def set(self, time_now_ms, key, deferred):
         self.rotate(time_now_ms)