Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit e4c46c1

Browse files
committed
Fix 'Unhandled error in Deferred'
Fixes a CRITICAL "Unhandled error in Deferred" log message which happened when a function wrapped with `@cachedList` failed
1 parent 6c0b44a commit e4c46c1

File tree

2 files changed

+23
-21
lines changed

2 files changed

+23
-21
lines changed

changelog.d/12089.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix occasional 'Unhandled error in Deferred' error message.

synapse/util/caches/descriptors.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -444,39 +444,40 @@ def arg_to_cache_key(arg: Hashable) -> Hashable:
444444
deferred: "defer.Deferred[Any]" = defer.Deferred()
445445
deferreds_map[arg] = deferred
446446
key = arg_to_cache_key(arg)
447-
cache.set(key, deferred, callback=invalidate_callback)
447+
cached_defers.append(
448+
cache.set(key, deferred, callback=invalidate_callback)
449+
)
448450

449451
def complete_all(res: Dict[Hashable, Any]) -> None:
450-
# the wrapped function has completed. It returns a
451-
# a dict. We can now resolve the observable deferreds in
452-
# the cache and update our own result map.
452+
# the wrapped function has completed. It returns a dict.
453+
# We can now update our own result map, and then resolve the
454+
# observable deferreds in the cache.
453455
for e in missing:
454456
val = res.get(e, None)
455-
deferreds_map[e].callback(val)
457+
# make sure we update the results map before running the
458+
# deferreds, because as soon as we run the last deferred, the
459+
# gatherResults() below will complete and return the result
460+
# dict to our caller.
456461
results[e] = val
462+
deferreds_map[e].callback(val)
457463

458-
def errback(f: Failure) -> Failure:
459-
# the wrapped function has failed. Invalidate any cache
460-
# entries we're supposed to be populating, and fail
461-
# their deferreds.
462-
for e in missing:
463-
key = arg_to_cache_key(e)
464-
cache.invalidate(key)
465-
deferreds_map[e].errback(f)
466-
467-
# return the failure, to propagate to our caller.
468-
return f
464+
def errback_all(f: Failure) -> None:
465+
# the wrapped function has failed. Propagate the failure into
466+
# the cache, which will invalidate the entry, and cause the
467+
# relevant cached_deferreds to fail, which will propagate the
468+
# failure to our caller.
469+
for d1 in deferreds_map.values():
470+
d1.errback(f)
469471

470472
args_to_call = dict(arg_dict)
471473
# copy the missing set before sending it to the callee, to guard against
472474
# modification.
473475
args_to_call[self.list_name] = tuple(missing)
474476

475-
cached_defers.append(
476-
defer.maybeDeferred(
477-
preserve_fn(self.orig), **args_to_call
478-
).addCallbacks(complete_all, errback)
479-
)
477+
# dispatch the call, and attach the two handlers
478+
defer.maybeDeferred(
479+
preserve_fn(self.orig), **args_to_call
480+
).addCallbacks(complete_all, errback_all)
480481

481482
if cached_defers:
482483
d = defer.gatherResults(cached_defers, consumeErrors=True).addCallbacks(

0 commit comments

Comments
 (0)