def _attempt_new_transaction(self, destination): """Try to start a new transaction to this destination If there is already a transaction in progress to this destination, returns immediately. Otherwise kicks off the process of sending a transaction in the background. Args: destination (str): Returns: None """ # list of (pending_pdu, deferred, order) if destination in self.pending_transactions: # XXX: pending_transactions can get stuck on by a never-ending # request at which point pending_pdus_by_dest just keeps growing. # we need application-layer timeouts of some flavour of these # requests logger.debug( "TX [%s] Transaction already in progress", destination ) return logger.debug("TX [%s] Starting transaction loop", destination) run_as_background_process( "federation_transaction_transmission_loop", self._transaction_transmission_loop, destination, )
def start(self): """Starts the pushers off in a background process. """ if not self._should_start_pushers: logger.info("Not starting pushers because they are disabled in the config") return run_as_background_process("start_pushers", self._start_pushers)
def handle_event(event): # Gather interested services services = yield self._get_services_for_event(event) if len(services) == 0: return # no services need notifying # Do we know this user exists? If not, poke the user # query API for all services which match that user regex. # This needs to block as these user queries need to be # made BEFORE pushing the event. yield self._check_user_exists(event.sender) if event.type == EventTypes.Member: yield self._check_user_exists(event.state_key) if not self.started_scheduler: def start_scheduler(): return self.scheduler.start().addErrback( log_failure, "Application Services Failure", ) run_as_background_process("as_scheduler", start_scheduler) self.started_scheduler = True # Fork off pushes to these services for service in services: self.scheduler.submit_event_for_as(service, event)
def _start_user_parting(self): """ Start the process that goes through the table of users pending deactivation, if it isn't already running. Returns: None """ if not self._user_parter_running: run_as_background_process("user_parter_loop", self._user_parter_loop)
def enqueue(self, service, event): self.queued_events.setdefault(service.id, []).append(event) # start a sender for this appservice if we don't already have one if service.id in self.requests_in_flight: return run_as_background_process( "as-sender-%s" % (service.id, ), self._send_request, service, )
def _enqueue_events(self, events, check_redacted=True, allow_rejected=False): """Fetches events from the database using the _event_fetch_list. This allows batch and bulk fetching of events - it allows us to fetch events without having to create a new transaction for each request for events. """ if not events: defer.returnValue({}) events_d = defer.Deferred() with self._event_fetch_lock: self._event_fetch_list.append( (events, events_d) ) self._event_fetch_lock.notify() if self._event_fetch_ongoing < EVENT_QUEUE_THREADS: self._event_fetch_ongoing += 1 should_start = True else: should_start = False if should_start: run_as_background_process( "fetch_events", self.runWithConnection, self._do_fetch, ) logger.debug("Loading %d events", len(events)) with PreserveLoggingContext(): rows = yield events_d logger.debug("Loaded %d events (%d rows)", len(events), len(rows)) if not allow_rejected: rows[:] = [r for r in rows if not r["rejects"]] res = yield make_deferred_yieldable(defer.gatherResults( [ run_in_background( self._get_event_from_row, row["internal_metadata"], row["json"], row["redacts"], rejected_reason=row["rejects"], ) for row in rows ], consumeErrors=True )) defer.returnValue({ e.event.event_id: e for e in res if e })
def fire(self, name, *args, **kwargs): """Dispatches the given signal to the registered observers. Runs the observers as a background process. Does not return a deferred. """ if name not in self.signals: raise KeyError("%r does not have a signal named %s" % (self, name)) run_as_background_process( name, self.signals[name].fire, *args, **kwargs )
def notify_new_events(self, current_id): """This gets called when we have some new events we might want to send out to other servers. """ self._last_poked_id = max(current_id, self._last_poked_id) if self._is_processing: return # fire off a processing loop in the background run_as_background_process( "process_event_queue_for_federation", self._process_event_queue_loop, )
def _find_stream_orderings_for_times(self): return run_as_background_process( "event_push_action_stream_orderings", self.runInteraction, "_find_stream_orderings_for_times", self._find_stream_orderings_for_times_txn, )
def process_replication_rows(self, stream_name, token, rows): # The federation stream contains things that we want to send out, e.g. # presence, typing, etc. if stream_name == "federation": send_queue.process_rows_for_federation(self.federation_sender, rows) run_in_background(self.update_token, token) # We also need to poke the federation sender when new events happen elif stream_name == "events": self.federation_sender.notify_new_events(token) # ... and when new receipts happen elif stream_name == ReceiptsStream.NAME: run_as_background_process( "process_receipts_for_federation", self._on_new_receipts, rows, )
def notify_new_event(self): """Called when there may be more deltas to process """ if not self.update_user_directory: return if self._is_processing: return @defer.inlineCallbacks def process(): try: yield self._unsafe_process() finally: self._is_processing = False self._is_processing = True run_as_background_process("user_directory.notify_new_event", process)
def notify_new_event(self): """Called when new events have happened. Handles users and servers joining rooms and require being sent presence. """ if self._event_processing: return @defer.inlineCallbacks def _process_presence(): assert not self._event_processing self._event_processing = True try: yield self._unsafe_process() finally: self._event_processing = False run_as_background_process("presence.notify_new_event", _process_presence)
def _on_new_room_event(self, event, room_stream_id, extra_users=[]): """Notify any user streams that are interested in this room event""" # poke any interested application service. run_as_background_process( "notify_app_services", self._notify_app_services, room_stream_id, ) if self.federation_sender: self.federation_sender.notify_new_events(room_stream_id) if event.type == EventTypes.Member and event.membership == Membership.JOIN: self._user_joined_room(event.state_key, event.room_id) self.on_new_event( "room_key", room_stream_id, users=extra_users, rooms=[event.room_id], )
def lineReceived(self, line): """Called when we've received a line """ if line.strip() == "": # Ignore blank lines return line = line.decode("utf-8") cmd_name, rest_of_line = line.split(" ", 1) if cmd_name not in self.VALID_INBOUND_COMMANDS: logger.error("[%s] invalid command %s", self.id(), cmd_name) self.send_error("invalid command: %s", cmd_name) return self.last_received_command = self.clock.time_msec() self.inbound_commands_counter[cmd_name] = ( self.inbound_commands_counter[cmd_name] + 1) cmd_cls = COMMAND_MAP[cmd_name] try: cmd = cmd_cls.from_line(rest_of_line) except Exception as e: logger.exception( "[%s] failed to parse line %r: %r", self.id(), cmd_name, rest_of_line ) self.send_error( "failed to parse line for %r: %r (%r):" % (cmd_name, e, rest_of_line) ) return # Now lets try and call on_<CMD_NAME> function try: run_as_background_process( "replication-" + cmd.get_logcontext_id(), getattr(self, "on_%s" % (cmd_name,)), cmd, ) except Exception: logger.exception("[%s] Failed to handle line: %r", self.id(), line)
def on_notifier_poke(self): """Checks if there is actually any new data and sends it to the connections if there are. This should get called each time new data is available, even if it is currently being executed, so that nothing gets missed """ if not self.connections: # Don't bother if nothing is listening. We still need to advance # the stream tokens otherwise they'll fall beihind forever for stream in self.streams: stream.discard_updates_and_advance() return self.pending_updates = True if self.is_looping: logger.debug("Notifier poke loop already running") return run_as_background_process("replication_notifier", self._run_notifier_loop)
def __init__(self, hs): self.hs = hs self.auth = hs.get_auth() self.store = hs.get_datastore() self.state = hs.get_state_handler() self.clock = hs.get_clock() self.validator = EventValidator() self.profile_handler = hs.get_profile_handler() self.event_builder_factory = hs.get_event_builder_factory() self.server_name = hs.hostname self.ratelimiter = hs.get_ratelimiter() self.notifier = hs.get_notifier() self.config = hs.config self.require_membership_for_aliases = hs.config.require_membership_for_aliases self.send_event_to_master = ReplicationSendEventRestServlet.make_client( hs) # This is only used to get at ratelimit function, and maybe_kick_guest_users self.base_handler = BaseHandler(hs) self.pusher_pool = hs.get_pusherpool() # We arbitrarily limit concurrent event creation for a room to 5. # This is to stop us from diverging history *too* much. self.limiter = Linearizer(max_count=5, name="room_event_creation_limit") self.action_generator = hs.get_action_generator() self.spam_checker = hs.get_spam_checker() self.third_party_event_rules = hs.get_third_party_event_rules() self._block_events_without_consent_error = ( self.config.block_events_without_consent_error) # we need to construct a ConsentURIBuilder here, as it checks that the necessary # config options, but *only* if we have a configuration for which we are # going to need it. if self._block_events_without_consent_error: self._consent_uri_builder = ConsentURIBuilder(self.config) if (not self.config.worker_app and self.config.cleanup_extremities_with_dummy_events): self.clock.looping_call( lambda: run_as_background_process( "send_dummy_events_to_fill_extremities", self._send_dummy_events_to_fill_extremities, ), 5 * 60 * 1000, )
def _prune_old_outbound_device_pokes(self): """Delete old entries out of the device_lists_outbound_pokes to ensure that we don't fill up due to dead servers. We keep one entry per (destination, user_id) tuple to ensure that the prev_ids remain correct if the server does come back. """ yesterday = self._clock.time_msec() - 24 * 60 * 60 * 1000 def _prune_txn(txn): select_sql = """ SELECT destination, user_id, max(stream_id) as stream_id FROM device_lists_outbound_pokes GROUP BY destination, user_id HAVING min(ts) < ? AND count(*) > 1 """ txn.execute(select_sql, (yesterday,)) rows = txn.fetchall() if not rows: return delete_sql = """ DELETE FROM device_lists_outbound_pokes WHERE ts < ? AND destination = ? AND user_id = ? AND stream_id < ? """ txn.executemany( delete_sql, ( (yesterday, row[0], row[1], row[2]) for row in rows ) ) # Since we've deleted unsent deltas, we need to remove the entry # of last successful sent so that the prev_ids are correctly set. sql = """ DELETE FROM device_lists_outbound_last_success WHERE destination = ? AND user_id = ? """ txn.executemany(sql, ((row[0], row[1]) for row in rows)) logger.info("Pruned %d device list outbound pokes", txn.rowcount) return run_as_background_process( "prune_old_outbound_device_pokes", self.runInteraction, "_prune_old_outbound_device_pokes", _prune_txn, )
def lineReceived(self, line): """Called when we've received a line """ if line.strip() == "": # Ignore blank lines return line = line.decode("utf-8") cmd_name, rest_of_line = line.split(" ", 1) if cmd_name not in self.VALID_INBOUND_COMMANDS: logger.error("[%s] invalid command %s", self.id(), cmd_name) self.send_error("invalid command: %s", cmd_name) return self.last_received_command = self.clock.time_msec() self.inbound_commands_counter[cmd_name] = ( self.inbound_commands_counter[cmd_name] + 1) cmd_cls = COMMAND_MAP[cmd_name] try: cmd = cmd_cls.from_line(rest_of_line) except Exception as e: logger.exception( "[%s] failed to parse line %r: %r", self.id(), cmd_name, rest_of_line ) self.send_error( "failed to parse line for %r: %r (%r):" % (cmd_name, e, rest_of_line) ) return # Now lets try and call on_<CMD_NAME> function run_as_background_process( "replication-" + cmd.get_logcontext_id(), self.handle_command, cmd, )
async def handle_event(event: EventBase) -> None: # Gather interested services services = await self._get_services_for_event(event) if len(services) == 0: return # no services need notifying # Do we know this user exists? If not, poke the user # query API for all services which match that user regex. # This needs to block as these user queries need to be # made BEFORE pushing the event. await self._check_user_exists(event.sender) if event.type == EventTypes.Member: await self._check_user_exists(event.state_key) if not self.started_scheduler: async def start_scheduler() -> None: try: await self.scheduler.start() except Exception: logger.error( "Application Services Failure") run_as_background_process("as_scheduler", start_scheduler) self.started_scheduler = True # Fork off pushes to these services for service in services: self.scheduler.enqueue_for_appservice( service, events=[event]) now = self.clock.time_msec() ts = await self.store.get_received_ts(event.event_id) assert ts is not None synapse.metrics.event_processing_lag_by_event.labels( "appservice_sender").observe((now - ts) / 1000)
def handle_command(self, cmd: Command) -> None: """Handle a command we have received over the replication stream. First calls `self.on_<COMMAND>` if it exists, then calls `self.command_handler.on_<COMMAND>` if it exists (which can optionally return an Awaitable). This allows for protocol level handling of commands (e.g. PINGs), before delegating to the handler. Args: cmd: received command """ handled = False # First call any command handlers on this instance. These are for TCP # specific handling. cmd_func = getattr(self, "on_%s" % (cmd.NAME, ), None) if cmd_func: cmd_func(cmd) handled = True # Then call out to the handler. cmd_func = getattr(self.command_handler, "on_%s" % (cmd.NAME, ), None) if cmd_func: res = cmd_func(self, cmd) # the handler might be a coroutine: fire it off as a background process # if so. if isawaitable(res): run_as_background_process( "replication-" + cmd.get_logcontext_id(), lambda: res) handled = True if not handled: logger.warning("Unhandled command: %r", cmd)
def attempt_new_transaction(self): """Try to start a new transaction to this destination If there is already a transaction in progress to this destination, returns immediately. Otherwise kicks off the process of sending a transaction in the background. """ # list of (pending_pdu, deferred, order) if self.transmission_loop_running: # XXX: this can get stuck on by a never-ending # request at which point pending_pdus just keeps growing. # we need application-layer timeouts of some flavour of these # requests logger.debug("TX [%s] Transaction already in progress", self._destination) return logger.debug("TX [%s] Starting transaction loop", self._destination) run_as_background_process( "federation_transaction_transmission_loop", self._transaction_transmission_loop, )
def _update_client_ips_batch(self): # If the DB pool has already terminated, don't try updating if not self.hs.get_db_pool().running: return def update(): to_update = self._batch_row_update self._batch_row_update = {} return self.runInteraction( "_update_client_ips_batch", self._update_client_ips_batch_txn, to_update ) return run_as_background_process("update_client_ips", update)
def _update_client_ips_batch(self): # If the DB pool has already terminated, don't try updating if not self.hs.get_db_pool().running: return def update(): to_update = self._batch_row_update self._batch_row_update = {} return self.runInteraction("_update_client_ips_batch", self._update_client_ips_batch_txn, to_update) return run_as_background_process("update_client_ips", update)
def handle_command(self, cmd: Command) -> None: """Handle a command we have received over the replication stream. Delegates to `self.handler.on_<COMMAND>` (which can optionally return an Awaitable). Args: cmd: received command """ cmd_func = getattr(self.synapse_handler, "on_%s" % (cmd.NAME, ), None) if not cmd_func: logger.warning("Unhandled command: %r", cmd) return res = cmd_func(self, cmd) # the handler might be a coroutine: fire it off as a background process # if so. if isawaitable(res): run_as_background_process("replication-" + cmd.get_logcontext_id(), lambda: res)
def _enqueue_events(self, events): """Fetches events from the database using the _event_fetch_list. This allows batch and bulk fetching of events - it allows us to fetch events without having to create a new transaction for each request for events. Args: events (Iterable[str]): events to be fetched. Returns: Deferred[Dict[str, Dict]]: map from event id to row data from the database. May contain events that weren't requested. """ events_d = defer.Deferred() with self._event_fetch_lock: self._event_fetch_list.append((events, events_d)) self._event_fetch_lock.notify() if self._event_fetch_ongoing < EVENT_QUEUE_THREADS: self._event_fetch_ongoing += 1 should_start = True else: should_start = False if should_start: run_as_background_process("fetch_events", self.db.runWithConnection, self._do_fetch) logger.debug("Loading %d events: %s", len(events), events) with PreserveLoggingContext(): row_map = yield events_d logger.debug("Loaded %d events (%d rows)", len(events), len(row_map)) return row_map
def send_command(self, cmd: Command): """Send a command if connection has been established. Args: cmd (Command) """ string = "%s %s" % (cmd.NAME, cmd.to_line()) if "\n" in string: raise Exception("Unexpected newline in command: %r", string) encoded_string = string.encode("utf-8") # We use "redis" as the name here as we don't have 1:1 connections to # remote instances. tcp_outbound_commands_counter.labels(cmd.NAME, "redis").inc() async def _send(): with PreserveLoggingContext(): # Note that we use the other connection as we can't send # commands using the subscription connection. await self.outbound_redis_connection.publish( self.stream_name, encoded_string) run_as_background_process("send-cmd", _send)
def messageReceived(self, pattern: str, channel: str, message: str): """Received a message from redis. """ if message.strip() == "": # Ignore blank lines return try: cmd = parse_command_from_line(message) except Exception: logger.exception( "[%s] failed to parse line: %r", message, ) return # We use "redis" as the name here as we don't have 1:1 connections to # remote instances. tcp_inbound_commands_counter.labels(cmd.NAME, "redis").inc() # Now lets try and call on_<CMD_NAME> function run_as_background_process( "replication-" + cmd.get_logcontext_id(), self.handle_command, cmd )
def __init__(self, config, api): # The plug-in API. self._api = api # The table containing links. Configured in homeserver.yaml # as `spam_checker.config.links_table`. self._links_table = config["links_table"] logger.info("Using links table %s", self._links_table) # The table containing md5 hashes. Configured in homeserver.yaml # as `spam_checker.config.links_table`. self._md5_table = config["md5_table"] logger.info("Using md5 table %s", self._md5_table) # How often we should check for updates in the database. # Configured in homeserver.yaml # as `spam_checker.config.pull_from_db_every_sec`. pull_from_db_every_sec = int(config["pull_from_db_every_sec"]) logger.info("Rechecking database every %s seconds", pull_from_db_every_sec) # A ahocorasick.Automaton used to recognize bad links. self._link_automaton = None # Start the loop to update links. api.looping_background_call( f=self._update_links_automaton, msec=pull_from_db_every_sec * 1000, desc="Background update list of bad links", run_on_all_instances=True, ) # As soon as we can, run the first fetch. # Note that we have no guarantee that this is finished # by the time we receive the first message, so we need # a fallback in `_get_links_automaton()`. reactor.callWhenRunning(lambda: defer.ensureDeferred( run_as_background_process( func=self._update_links_automaton, desc="Background initial pull list of bad links"))) # And don't forget to register as a spam checker. api.register_spam_checker_callbacks( check_event_for_spam=self.check_event_for_spam, check_media_file_for_spam=self.check_media_file_for_spam)
def _delete_old_forward_extrem_cache(self): def _delete_old_forward_extrem_cache_txn(txn): # Delete entries older than a month, while making sure we don't delete # the only entries for a room. sql = """ DELETE FROM stream_ordering_to_exterm WHERE room_id IN ( SELECT room_id FROM stream_ordering_to_exterm WHERE stream_ordering > ? ) AND stream_ordering < ? """ txn.execute(sql, (self.stream_ordering_month_ago, self.stream_ordering_month_ago)) return run_as_background_process( "delete_old_forward_extrem_cache", self.runInteraction, "_delete_old_forward_extrem_cache", _delete_old_forward_extrem_cache_txn, )
def _delete_old_forward_extrem_cache(self): def _delete_old_forward_extrem_cache_txn(txn): # Delete entries older than a month, while making sure we don't delete # the only entries for a room. sql = """ DELETE FROM stream_ordering_to_exterm WHERE room_id IN ( SELECT room_id FROM stream_ordering_to_exterm WHERE stream_ordering > ? ) AND stream_ordering < ? """ txn.execute( sql, (self.stream_ordering_month_ago, self.stream_ordering_month_ago) ) return run_as_background_process( "delete_old_forward_extrem_cache", self.runInteraction, "_delete_old_forward_extrem_cache", _delete_old_forward_extrem_cache_txn, )
def _prune_old_outbound_device_pokes(self, prune_age=24 * 60 * 60 * 1000): """Delete old entries out of the device_lists_outbound_pokes to ensure that we don't fill up due to dead servers. Normally, we try to send device updates as a delta since a previous known point: this is done by setting the prev_id in the m.device_list_update EDU. However, for that to work, we have to have a complete record of each change to each device, which can add up to quite a lot of data. An alternative mechanism is that, if the remote server sees that it has missed an entry in the stream_id sequence for a given user, it will request a full list of that user's devices. Hence, we can reduce the amount of data we have to store (and transmit in some future transaction), by clearing almost everything for a given destination out of the database, and having the remote server resync. All we need to do is make sure we keep at least one row for each (user, destination) pair, to remind us to send a m.device_list_update EDU for that user when the destination comes back. It doesn't matter which device we keep. """ yesterday = self._clock.time_msec() - prune_age def _prune_txn(txn): # look for (user, destination) pairs which have an update older than # the cutoff. # # For each pair, we also need to know the most recent stream_id, and # an arbitrary device_id at that stream_id. select_sql = """ SELECT dlop1.destination, dlop1.user_id, MAX(dlop1.stream_id) AS stream_id, (SELECT MIN(dlop2.device_id) AS device_id FROM device_lists_outbound_pokes dlop2 WHERE dlop2.destination = dlop1.destination AND dlop2.user_id=dlop1.user_id AND dlop2.stream_id=MAX(dlop1.stream_id) ) FROM device_lists_outbound_pokes dlop1 GROUP BY destination, user_id HAVING min(ts) < ? AND count(*) > 1 """ txn.execute(select_sql, (yesterday,)) rows = txn.fetchall() if not rows: return logger.info( "Pruning old outbound device list updates for %i users/destinations: %s", len(rows), shortstr((row[0], row[1]) for row in rows), ) # we want to keep the update with the highest stream_id for each user. # # there might be more than one update (with different device_ids) with the # same stream_id, so we also delete all but one rows with the max stream id. delete_sql = """ DELETE FROM device_lists_outbound_pokes WHERE destination = ? AND user_id = ? AND ( stream_id < ? OR (stream_id = ? AND device_id != ?) ) """ count = 0 for (destination, user_id, stream_id, device_id) in rows: txn.execute( delete_sql, (destination, user_id, stream_id, stream_id, device_id) ) count += txn.rowcount # Since we've deleted unsent deltas, we need to remove the entry # of last successful sent so that the prev_ids are correctly set. sql = """ DELETE FROM device_lists_outbound_last_success WHERE destination = ? AND user_id = ? """ txn.executemany(sql, ((row[0], row[1]) for row in rows)) logger.info("Pruned %d device list outbound pokes", count) return run_as_background_process( "prune_old_outbound_device_pokes", self.db.runInteraction, "_prune_old_outbound_device_pokes", _prune_txn, )
def _start_processing(self): if self._is_processing: return run_as_background_process("emailpush.process", self._process)
def __init__(self, hs: "HomeServer"): self.hs = hs self.auth = hs.get_auth() self.store = hs.get_datastore() self.storage = hs.get_storage() self.state = hs.get_state_handler() self.clock = hs.get_clock() self.validator = EventValidator() self.profile_handler = hs.get_profile_handler() self.event_builder_factory = hs.get_event_builder_factory() self.server_name = hs.hostname self.notifier = hs.get_notifier() self.config = hs.config self.require_membership_for_aliases = hs.config.require_membership_for_aliases self._is_event_writer = ( self.config.worker.writers.events == hs.get_instance_name()) self.room_invite_state_types = self.hs.config.room_invite_state_types self.send_event = ReplicationSendEventRestServlet.make_client(hs) # This is only used to get at ratelimit function, and maybe_kick_guest_users self.base_handler = BaseHandler(hs) self.pusher_pool = hs.get_pusherpool() # We arbitrarily limit concurrent event creation for a room to 5. # This is to stop us from diverging history *too* much. self.limiter = Linearizer(max_count=5, name="room_event_creation_limit") self.action_generator = hs.get_action_generator() self.spam_checker = hs.get_spam_checker() self.third_party_event_rules = hs.get_third_party_event_rules() self._block_events_without_consent_error = ( self.config.block_events_without_consent_error) # Rooms which should be excluded from dummy insertion. (For instance, # those without local users who can send events into the room). # # map from room id to time-of-last-attempt. # self._rooms_to_exclude_from_dummy_event_insertion = { } # type: dict[str, int] # we need to construct a ConsentURIBuilder here, as it checks that the necessary # config options, but *only* if we have a configuration for which we are # going to need it. if self._block_events_without_consent_error: self._consent_uri_builder = ConsentURIBuilder(self.config) if (not self.config.worker_app and self.config.cleanup_extremities_with_dummy_events): self.clock.looping_call( lambda: run_as_background_process( "send_dummy_events_to_fill_extremities", self._send_dummy_events_to_fill_extremities, ), 5 * 60 * 1000, ) self._message_handler = hs.get_message_handler() self._ephemeral_events_enabled = hs.config.enable_ephemeral_messages self._dummy_events_threshold = hs.config.dummy_events_threshold
def _start_update_recently_accessed(self): return run_as_background_process( "update_recently_accessed_media", self._update_recently_accessed, )
def _start_update_remote_profile_cache(self): return run_as_background_process( "Update remote profile", self._update_remote_profile_cache, )
def run_timeout_handler(): return run_as_background_process( "handle_presence_timeouts", self._handle_timeouts )
def _start_renew_attestations(self): return run_as_background_process("renew_attestations", self._renew_attestations)
def _start_apply_media_retention_rules(self) -> Deferred: return run_as_background_process("apply_media_retention_rules", self._apply_media_retention_rules)
def start_phone_stats_home(): return run_as_background_process("phone_stats_home", phone_stats_home)
def start_doing_background_updates(self): run_as_background_process("background_updates", self._run_background_updates)
def _start_expire_url_cache_data(self): return run_as_background_process( "expire_url_cache_data", self._expire_url_cache_data, )
def start_generate_monthly_active_users(): return run_as_background_process( "generate_monthly_active_users", generate_monthly_active_users, )
def reap_monthly_active_users(): return run_as_background_process( "reap_monthly_active_users", hs.get_datastore().reap_monthly_active_users, )
def generate_user_daily_visit_stats(): return run_as_background_process( "generate_user_daily_visits", hs.get_datastore().generate_user_daily_visits, )
def _retry(): run_as_background_process( "as-recoverer-%s" % (self.service.id,), self.retry, )
def _start_cleanup_transactions(self): return run_as_background_process( "cleanup_transactions", self._cleanup_transactions )
def _start_expire_url_cache_data(self): return run_as_background_process("expire_url_cache_data", self._expire_url_cache_data)
def run_persister(): return run_as_background_process( "persist_presence_changes", self._persist_unpersisted_changes )
def _start_cleanup_transactions(self): return run_as_background_process( "cleanup_transactions", self._cleanup_transactions, )
def f(): return run_as_background_process( "prune_cache_%s" % self._cache_name, self._prune_cache, )
def _start_renew_attestations(self) -> "Deferred[None]": return run_as_background_process("renew_attestations", self._renew_attestations)
def start_doing_background_updates(self): run_as_background_process("background_updates", self.run_background_updates)
def _retry(): run_as_background_process("as-recoverer-%s" % (self.service.id, ), self.retry)
def _start_processing(self) -> None: if self._is_processing: return run_as_background_process("httppush.process", self._process)
def on_new_receipts(self, min_stream_id, max_stream_id): # Note that the min here shouldn't be relied upon to be accurate. # We could check the receipts are actually m.read receipts here, # but currently that's the only type of receipt anyway... run_as_background_process("http_pusher.on_new_receipts", self._update_badge)
def _start_rotate_notifs(self): return run_as_background_process("rotate_notifs", self._rotate_notifs)
def _start_processing(self): if self._is_processing: return run_as_background_process("httppush.process", self._process)