summary refs log tree commit diff
diff options
context:
space:
mode:
authorSean Quah <seanq@element.io>2022-03-08 17:24:14 +0000
committerSean Quah <seanq@element.io>2022-03-08 17:42:09 +0000
commit93073a4b11b31e5c1af5b4c5946a1c1297c7c230 (patch)
tree9f8da76e7342bad8e109e39958a5fca042870d46
parentAdd `delay_cancellation` utility function (diff)
downloadsynapse-93073a4b11b31e5c1af5b4c5946a1c1297c7c230.tar.xz
Fix logcontexts when `@cached` and `@cachedList` lookups are cancelled
`@cached` and `@cachedList` must wait until the wrapped method has
completed before raising `CancelledError`s, otherwise the wrapped method
will continue running in the background with a logging context that has
been marked as finished.
-rw-r--r--synapse/util/caches/descriptors.py11
1 files changed, 11 insertions, 0 deletions
diff --git a/synapse/util/caches/descriptors.py b/synapse/util/caches/descriptors.py
index 1cdead02f1..89509ba9c5 100644
--- a/synapse/util/caches/descriptors.py
+++ b/synapse/util/caches/descriptors.py
@@ -40,6 +40,7 @@ from twisted.python.failure import Failure
 
 from synapse.logging.context import make_deferred_yieldable, preserve_fn
 from synapse.util import unwrapFirstError
+from synapse.util.async_helpers import delay_cancellation
 from synapse.util.caches.deferred_cache import DeferredCache
 from synapse.util.caches.lrucache import LruCache
 
@@ -322,6 +323,11 @@ class DeferredCacheDescriptor(_CacheDescriptorBase):
                 ret = defer.maybeDeferred(preserve_fn(self.orig), obj, *args, **kwargs)
                 ret = cache.set(cache_key, ret, callback=invalidate_callback)
 
+                # We started a new call to `self.orig`, so we must always wait for it to
+                # complete. Otherwise we might mark our current logging context as
+                # finished while `self.orig` is still using it in the background.
+                ret = delay_cancellation(ret, all=True)
+
             return make_deferred_yieldable(ret)
 
         wrapped = cast(_CachedFunction, _wrapped)
@@ -482,6 +488,11 @@ class DeferredCacheListDescriptor(_CacheDescriptorBase):
                 d = defer.gatherResults(cached_defers, consumeErrors=True).addCallbacks(
                     lambda _: results, unwrapFirstError
                 )
+                if missing:
+                    # We started a new call to `self.orig`, so we must always wait for it to
+                    # complete. Otherwise we might mark our current logging context as
+                    # finished while `self.orig` is still using it in the background.
+                    d = delay_cancellation(d, all=True)
                 return make_deferred_yieldable(d)
             else:
                 return defer.succeed(results)