def _wrapped(*args, **kwargs): # If we're passed a cache_context then we'll want to call its invalidate() # whenever we are invalidated invalidate_callback = kwargs.pop("on_invalidate", None) cache_key = get_cache_key(args, kwargs) # Add our own `cache_context` to argument list if the wrapped function # has asked for one if self.add_cache_context: kwargs["cache_context"] = _CacheContext.get_instance(cache, cache_key) try: cached_result_d = cache.get(cache_key, callback=invalidate_callback) if isinstance(cached_result_d, ObservableDeferred): observer = cached_result_d.observe() else: observer = defer.succeed(cached_result_d) except KeyError: ret = defer.maybeDeferred( preserve_fn(self.function_to_call), obj, *args, **kwargs ) def onErr(f): cache.invalidate(cache_key) return f ret.addErrback(onErr) result_d = cache.set(cache_key, ret, callback=invalidate_callback) observer = result_d.observe() return make_deferred_yieldable(observer)
def _wrapped(*args: Any, **kwargs: Any) -> Any: # If we're passed a cache_context then we'll want to call its invalidate() # whenever we are invalidated invalidate_callback = kwargs.pop("on_invalidate", None) cache_key = get_cache_key(args, kwargs) try: ret = cache.get(cache_key, callback=invalidate_callback) except KeyError: # Add our own `cache_context` to argument list if the wrapped function # has asked for one if self.add_cache_context: kwargs["cache_context"] = _CacheContext.get_instance( cache, cache_key) 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) return make_deferred_yieldable(ret)
def _verify_objects( self, verify_requests: Iterable[VerifyJsonRequest] ) -> List[defer.Deferred]: """Does the work of verify_json_[objects_]for_server Args: verify_requests: Iterable of verification requests. Returns: List<Deferred[None]>: for each input item, a deferred indicating success or failure to verify each json object's signature for the given server_name. The deferreds run their callbacks in the sentinel logcontext. """ # a list of VerifyJsonRequests which are awaiting a key lookup key_lookups = [] handle = preserve_fn(_handle_key_deferred) def process(verify_request: VerifyJsonRequest) -> defer.Deferred: """Process an entry in the request list Adds a key request to key_lookups, and returns a deferred which will complete or fail (in the sentinel context) when verification completes. """ if not verify_request.key_ids: return defer.fail( SynapseError( 400, "Not signed by %s" % (verify_request.server_name,), Codes.UNAUTHORIZED, ) ) logger.debug( "Verifying %s for %s with key_ids %s, min_validity %i", verify_request.request_name, verify_request.server_name, verify_request.key_ids, verify_request.minimum_valid_until_ts, ) # add the key request to the queue, but don't start it off yet. key_lookups.append(verify_request) # now run _handle_key_deferred, which will wait for the key request # to complete and then do the verification. # # We want _handle_key_request to log to the right context, so we # wrap it with preserve_fn (aka run_in_background) return handle(verify_request) results = [process(r) for r in verify_requests] if key_lookups: run_in_background(self._start_key_lookups, key_lookups) return results
def wrapped(*args, **kwargs): # If we're passed a cache_context then we'll want to call its invalidate() # whenever we are invalidated invalidate_callback = kwargs.pop("on_invalidate", None) cache_key = get_cache_key(args, kwargs) # Add our own `cache_context` to argument list if the wrapped function # has asked for one if self.add_cache_context: kwargs["cache_context"] = _CacheContext(cache, cache_key) try: cached_result_d = cache.get(cache_key, callback=invalidate_callback) if isinstance(cached_result_d, ObservableDeferred): observer = cached_result_d.observe() else: observer = cached_result_d except KeyError: ret = defer.maybeDeferred(preserve_fn(self.function_to_call), obj, *args, **kwargs) def onErr(f): cache.invalidate(cache_key) return f ret.addErrback(onErr) # If our cache_key is a string on py2, try to convert to ascii # to save a bit of space in large caches. Py3 does this # internally automatically. if six.PY2 and isinstance(cache_key, string_types): cache_key = to_ascii(cache_key) result_d = ObservableDeferred(ret, consumeErrors=True) cache.set(cache_key, result_d, callback=invalidate_callback) observer = result_d.observe() if isinstance(observer, defer.Deferred): return make_deferred_yieldable(observer) else: return observer
def wrap_async_request_handler(h): """Wraps an async request handler so that it calls request.processing. This helps ensure that work done by the request handler after the request is completed is correctly recorded against the request metrics/logs. The handler method must have a signature of "handle_foo(self, request)", where "request" must be a SynapseRequest. The handler may return a deferred, in which case the completion of the request isn't logged until the deferred completes. """ async def wrapped_async_request_handler(self, request): with request.processing(): await h(self, request) # we need to preserve_fn here, because the synchronous render method won't yield for # us (obviously) return preserve_fn(wrapped_async_request_handler)
def _wrapped(*args, **kwargs): # If we're passed a cache_context then we'll want to call its invalidate() # whenever we are invalidated invalidate_callback = kwargs.pop("on_invalidate", None) cache_key = get_cache_key(args, kwargs) try: ret = cache.get(cache_key, callback=invalidate_callback) except KeyError: # Add our own `cache_context` to argument list if the wrapped function # has asked for one if self.add_cache_context: kwargs["cache_context"] = _CacheContext.get_instance( cache, cache_key) ret = defer.maybeDeferred(preserve_fn(self.orig), obj, *args, **kwargs) ret = cache.set(cache_key, ret, callback=invalidate_callback) return make_deferred_yieldable(ret)
def wrapped(*args, **kwargs): # If we're passed a cache_context then we'll want to call its # invalidate() whenever we are invalidated invalidate_callback = kwargs.pop("on_invalidate", None) arg_dict = inspect.getcallargs(self.orig, obj, *args, **kwargs) keyargs = [arg_dict[arg_nm] for arg_nm in self.arg_names] list_args = arg_dict[self.list_name] results = {} def update_results_dict(res, arg): results[arg] = res # list of deferreds to wait for cached_defers = [] missing = set() # If the cache takes a single arg then that is used as the key, # otherwise a tuple is used. if num_args == 1: def arg_to_cache_key(arg): return arg else: keylist = list(keyargs) def arg_to_cache_key(arg): keylist[self.list_pos] = arg return tuple(keylist) for arg in list_args: try: res = cache.get(arg_to_cache_key(arg), callback=invalidate_callback) if not isinstance(res, ObservableDeferred): results[arg] = res elif not res.has_succeeded(): res = res.observe() res.addCallback(update_results_dict, arg) cached_defers.append(res) else: results[arg] = res.get_result() except KeyError: missing.add(arg) if missing: # we need a deferred for each entry in the list, # which we put in the cache. Each deferred resolves with the # relevant result for that key. deferreds_map = {} for arg in missing: deferred = defer.Deferred() deferreds_map[arg] = deferred key = arg_to_cache_key(arg) cache.set(key, deferred, callback=invalidate_callback) def complete_all(res): # the wrapped function has completed. It returns a # a dict. We can now resolve the observable deferreds in # the cache and update our own result map. for e in missing: val = res.get(e, None) deferreds_map[e].callback(val) results[e] = val def errback(f): # the wrapped function has failed. Invalidate any cache # entries we're supposed to be populating, and fail # their deferreds. for e in missing: key = arg_to_cache_key(e) cache.invalidate(key) deferreds_map[e].errback(f) # return the failure, to propagate to our caller. return f args_to_call = dict(arg_dict) args_to_call[self.list_name] = list(missing) cached_defers.append( defer.maybeDeferred(preserve_fn(self.function_to_call), **args_to_call).addCallbacks( complete_all, errback)) if cached_defers: d = defer.gatherResults(cached_defers, consumeErrors=True).addCallbacks( lambda _: results, unwrapFirstError) return make_deferred_yieldable(d) else: return defer.succeed(results)
def _check_sigs_and_hash_and_fetch( self, origin, pdus, room_version, outlier=False, include_none=False ): """Takes a list of PDUs and checks the signatures and hashs of each one. If a PDU fails its signature check then we check if we have it in the database and if not then request if from the originating server of that PDU. If a PDU fails its content hash check then it is redacted. The given list of PDUs are not modified, instead the function returns a new list. Args: origin (str) pdu (list) room_version (str) outlier (bool): Whether the events are outliers or not include_none (str): Whether to include None in the returned list for events that have failed their checks Returns: Deferred : A list of PDUs that have valid signatures and hashes. """ deferreds = self._check_sigs_and_hashes(room_version, pdus) @defer.inlineCallbacks def handle_check_result(pdu, deferred): try: res = yield make_deferred_yieldable(deferred) except SynapseError: res = None if not res: # Check local db. res = yield self.store.get_event( pdu.event_id, allow_rejected=True, allow_none=True ) if not res and pdu.origin != origin: try: res = yield self.get_pdu( destinations=[pdu.origin], event_id=pdu.event_id, room_version=room_version, outlier=outlier, timeout=10000, ) except SynapseError: pass if not res: logger.warning( "Failed to find copy of %s with valid signature", pdu.event_id ) return res handle = preserve_fn(handle_check_result) deferreds2 = [handle(pdu, deferred) for pdu, deferred in zip(pdus, deferreds)] valid_pdus = yield make_deferred_yieldable( defer.gatherResults(deferreds2, consumeErrors=True) ).addErrback(unwrapFirstError) if include_none: return valid_pdus else: return [p for p in valid_pdus if p]
def wrapped(*args: Any, **kwargs: Any) -> "defer.Deferred[Dict]": # If we're passed a cache_context then we'll want to call its # invalidate() whenever we are invalidated invalidate_callback = kwargs.pop("on_invalidate", None) arg_dict = inspect.getcallargs(self.orig, obj, *args, **kwargs) keyargs = [arg_dict[arg_nm] for arg_nm in self.arg_names] list_args = arg_dict[self.list_name] results = {} def update_results_dict(res: Any, arg: Hashable) -> None: results[arg] = res # list of deferreds to wait for cached_defers = [] missing = set() # If the cache takes a single arg then that is used as the key, # otherwise a tuple is used. if num_args == 1: def arg_to_cache_key(arg: Hashable) -> Hashable: return arg else: keylist = list(keyargs) def arg_to_cache_key(arg: Hashable) -> Hashable: keylist[self.list_pos] = arg return tuple(keylist) for arg in list_args: try: res = cache.get(arg_to_cache_key(arg), callback=invalidate_callback) if not res.called: res.addCallback(update_results_dict, arg) cached_defers.append(res) else: results[arg] = res.result except KeyError: missing.add(arg) if missing: # we need a deferred for each entry in the list, # which we put in the cache. Each deferred resolves with the # relevant result for that key. deferreds_map = {} for arg in missing: deferred: "defer.Deferred[Any]" = defer.Deferred() deferreds_map[arg] = deferred key = arg_to_cache_key(arg) cached_defers.append( cache.set(key, deferred, callback=invalidate_callback)) def complete_all(res: Dict[Hashable, Any]) -> None: # the wrapped function has completed. It returns a dict. # We can now update our own result map, and then resolve the # observable deferreds in the cache. for e, d1 in deferreds_map.items(): val = res.get(e, None) # make sure we update the results map before running the # deferreds, because as soon as we run the last deferred, the # gatherResults() below will complete and return the result # dict to our caller. results[e] = val d1.callback(val) def errback_all(f: Failure) -> None: # the wrapped function has failed. Propagate the failure into # the cache, which will invalidate the entry, and cause the # relevant cached_deferreds to fail, which will propagate the # failure to our caller. for d1 in deferreds_map.values(): d1.errback(f) args_to_call = dict(arg_dict) args_to_call[self.list_name] = missing # dispatch the call, and attach the two handlers defer.maybeDeferred(preserve_fn(self.orig), **args_to_call).addCallbacks( complete_all, errback_all) if cached_defers: 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) return make_deferred_yieldable(d) else: return defer.succeed(results)