def __init__(self, hs: "synapse.server.HomeServer"): self.hs = hs self.server_name = hs.hostname self.store = hs.get_datastore() self.state = hs.get_state_handler() self.clock = hs.get_clock() self.is_mine_id = hs.is_mine_id self._transaction_manager = TransactionManager(hs) # map from destination to PerDestinationQueue self._per_destination_queues = { } # type: Dict[str, PerDestinationQueue] LaterGauge( "synapse_federation_transaction_queue_pending_destinations", "", [], lambda: sum(1 for d in self._per_destination_queues.values() if d.transmission_loop_running), ) # Map of user_id -> UserPresenceState for all the pending presence # to be sent out by user_id. Entries here get processed and put in # pending_presence_by_dest self.pending_presence = {} # type: Dict[str, UserPresenceState] LaterGauge( "synapse_federation_transaction_queue_pending_pdus", "", [], lambda: sum(d.pending_pdu_count() for d in self._per_destination_queues.values()), ) LaterGauge( "synapse_federation_transaction_queue_pending_edus", "", [], lambda: sum(d.pending_edu_count() for d in self._per_destination_queues.values()), ) self._order = 1 self._is_processing = False self._last_poked_id = -1 self._processing_pending_presence = False # map from room_id to a set of PerDestinationQueues which we believe are # awaiting a call to flush_read_receipts_for_room. The presence of an entry # here for a given room means that we are rate-limiting RR flushes to that room, # and that there is a pending call to _flush_rrs_for_room in the system. self._queues_awaiting_rr_flush_by_room = ( {}) # type: Dict[str, Set[PerDestinationQueue]] self._rr_txn_interval_per_room_ms = ( 1000.0 / hs.config.federation_rr_transactions_per_room_per_second)
def __init__( self, name: str, clock: Clock, process_batch_callback: Callable[[List[V]], Awaitable[R]], ): self._name = name self._clock = clock # The set of keys currently being processed. self._processing_keys = set() # type: Set[Hashable] # The currently pending batch of values by key, with a Deferred to call # with the result of the corresponding `_process_batch_callback` call. self._next_values = {} # type: Dict[Hashable, List[Tuple[V, defer.Deferred]]] # The function to call with batches of values. self._process_batch_callback = process_batch_callback LaterGauge( "synapse_util_batching_queue_number_queued", "The number of items waiting in the queue across all keys", labels=("name",), caller=lambda: sum(len(v) for v in self._next_values.values()), ) LaterGauge( "synapse_util_batching_queue_number_of_keys", "The number of distinct keys that have items queued", labels=("name",), caller=lambda: len(self._next_values), )
def __init__(self, hs): self.store = hs.get_datastore() self.presence_handler = hs.get_presence_handler() self.clock = hs.get_clock() self.notifier = hs.get_notifier() self._server_notices_sender = hs.get_server_notices_sender() self._replication_torture_level = hs.config.replication_torture_level # Current connections. self.connections = [] # type: List[ServerReplicationStreamProtocol] LaterGauge( "synapse_replication_tcp_resource_total_connections", "", [], lambda: len(self.connections), ) # List of streams that clients can subscribe to. # We only support federation stream if federation sending hase been # disabled on the master. self.streams = [ stream(hs) for stream in itervalues(STREAMS_MAP) if stream != FederationStream or not hs.config.send_federation ] self.streams_by_name = {stream.NAME: stream for stream in self.streams} LaterGauge( "synapse_replication_tcp_resource_connections_per_stream", "", ["stream_name"], lambda: { (stream_name,): len( [ conn for conn in self.connections if stream_name in conn.replication_streams ] ) for stream_name in self.streams_by_name }, ) self.federation_sender = None if not hs.config.send_federation: self.federation_sender = hs.get_federation_sender() self.notifier.add_replication_callback(self.on_notifier_poke) self.notifier.add_remote_server_up_callback(self.send_remote_server_up) # Keeps track of whether we are currently checking for updates self.is_looping = False self.pending_updates = False hs.get_reactor().addSystemEventTrigger("before", "shutdown", self.on_shutdown)
def __init__(self, hs: "synapse.server.HomeServer"): self.user_to_user_stream = {} # type: Dict[str, _NotifierUserStream] self.room_to_user_streams = { } # type: Dict[str, Set[_NotifierUserStream]] self.hs = hs self.storage = hs.get_storage() self.event_sources = hs.get_event_sources() self.store = hs.get_datastore() self.pending_new_room_events = ( [] ) # type: List[Tuple[PersistedEventPosition, EventBase, Collection[UserID]]] # Called when there are new things to stream over replication self.replication_callbacks = [] # type: List[Callable[[], None]] # Called when remote servers have come back online after having been # down. self.remote_server_up_callbacks = [ ] # type: List[Callable[[str], None]] self.clock = hs.get_clock() self.appservice_handler = hs.get_application_service_handler() self._pusher_pool = hs.get_pusherpool() self.federation_sender = None if hs.should_send_federation(): self.federation_sender = hs.get_federation_sender() self.state_handler = hs.get_state_handler() self.clock.looping_call(self.remove_expired_streams, self.UNUSED_STREAM_EXPIRY_MS) # This is not a very cheap test to perform, but it's only executed # when rendering the metrics page, which is likely once per minute at # most when scraping it. def count_listeners(): all_user_streams = set() # type: Set[_NotifierUserStream] for streams in list(self.room_to_user_streams.values()): all_user_streams |= streams for stream in list(self.user_to_user_stream.values()): all_user_streams.add(stream) return sum(stream.count_listeners() for stream in all_user_streams) LaterGauge("synapse_notifier_listeners", "", [], count_listeners) LaterGauge( "synapse_notifier_rooms", "", [], lambda: count(bool, list(self.room_to_user_streams.values())), ) LaterGauge("synapse_notifier_users", "", [], lambda: len(self.user_to_user_stream))
def __init__(self, hs: "HomeServer"): self.user_to_user_stream: Dict[str, _NotifierUserStream] = {} self.room_to_user_streams: Dict[str, Set[_NotifierUserStream]] = {} self.hs = hs self._storage_controllers = hs.get_storage_controllers() self.event_sources = hs.get_event_sources() self.store = hs.get_datastores().main self.pending_new_room_events: List[_PendingRoomEventEntry] = [] # Called when there are new things to stream over replication self.replication_callbacks: List[Callable[[], None]] = [] self._federation_client = hs.get_federation_http_client() self._third_party_rules = hs.get_third_party_event_rules() self.clock = hs.get_clock() self.appservice_handler = hs.get_application_service_handler() self._pusher_pool = hs.get_pusherpool() self.federation_sender = None if hs.should_send_federation(): self.federation_sender = hs.get_federation_sender() self.state_handler = hs.get_state_handler() self.clock.looping_call(self.remove_expired_streams, self.UNUSED_STREAM_EXPIRY_MS) # This is not a very cheap test to perform, but it's only executed # when rendering the metrics page, which is likely once per minute at # most when scraping it. def count_listeners() -> int: all_user_streams: Set[_NotifierUserStream] = set() for streams in list(self.room_to_user_streams.values()): all_user_streams |= streams for stream in list(self.user_to_user_stream.values()): all_user_streams.add(stream) return sum(stream.count_listeners() for stream in all_user_streams) LaterGauge("synapse_notifier_listeners", "", [], count_listeners) LaterGauge( "synapse_notifier_rooms", "", [], lambda: count(bool, list(self.room_to_user_streams.values())), ) LaterGauge("synapse_notifier_users", "", [], lambda: len(self.user_to_user_stream))
def __init__(self, hs): self.user_to_user_stream = {} self.room_to_user_streams = {} self.hs = hs self.event_sources = hs.get_event_sources() self.store = hs.get_datastore() self.pending_new_room_events = [] self.replication_callbacks = [] self.clock = hs.get_clock() self.appservice_handler = hs.get_application_service_handler() if hs.should_send_federation(): self.federation_sender = hs.get_federation_sender() else: self.federation_sender = None self.state_handler = hs.get_state_handler() self.clock.looping_call(self.remove_expired_streams, self.UNUSED_STREAM_EXPIRY_MS) self.replication_deferred = ObservableDeferred(defer.Deferred()) # This is not a very cheap test to perform, but it's only executed # when rendering the metrics page, which is likely once per minute at # most when scraping it. def count_listeners(): all_user_streams = set() for x in self.room_to_user_streams.values(): all_user_streams |= x for x in self.user_to_user_stream.values(): all_user_streams.add(x) return sum(stream.count_listeners() for stream in all_user_streams) LaterGauge("synapse_notifier_listeners", "", [], count_listeners) LaterGauge( "synapse_notifier_rooms", "", [], lambda: count(bool, self.room_to_user_streams.values()), ) LaterGauge( "synapse_notifier_users", "", [], lambda: len(self.user_to_user_stream), )
def register(name: str, queue: Sized) -> None: LaterGauge( "synapse_federation_send_queue_%s_size" % (queue_name, ), "", [], lambda: len(queue), )
def __init__(self, database: DatabasePool, db_conn, hs): super().__init__(database, db_conn, hs) # Used by `_get_joined_hosts` to ensure only one thing mutates the cache # at a time. Keyed by room_id. self._joined_host_linearizer = Linearizer("_JoinedHostsCache") # Is the current_state_events.membership up to date? Or is the # background update still running? self._current_state_events_membership_up_to_date = False txn = db_conn.cursor( txn_name="_check_safe_current_state_events_membership_updated") self._check_safe_current_state_events_membership_updated_txn(txn) txn.close() if (self.hs.config.worker.run_background_tasks and self.hs.config.metrics.metrics_flags.known_servers): self._known_servers_count = 1 self.hs.get_clock().looping_call( self._count_known_servers, 60 * 1000, ) self.hs.get_clock().call_later( 1, self._count_known_servers, ) LaterGauge( "synapse_federation_known_servers", "", [], lambda: self._known_servers_count, )
def __init__(self, db_conn, hs): super(RoomMemberWorkerStore, self).__init__(db_conn, hs) # Is the current_state_events.membership up to date? Or is the # background update still running? self._current_state_events_membership_up_to_date = False txn = LoggingTransaction( db_conn.cursor(), name="_check_safe_current_state_events_membership_updated", database_engine=self.database_engine, ) self._check_safe_current_state_events_membership_updated_txn(txn) txn.close() if self.hs.config.metrics_flags.known_servers: self._known_servers_count = 1 self.hs.get_clock().looping_call( run_as_background_process, 60 * 1000, "_count_known_servers", self._count_known_servers, ) self.hs.get_clock().call_later( 1000, run_as_background_process, "_count_known_servers", self._count_known_servers, ) LaterGauge( "synapse_federation_known_servers", "", [], lambda: self._known_servers_count, )
def __init__(self, database: DatabasePool, db_conn, hs): super().__init__(database, db_conn, hs) # Is the current_state_events.membership up to date? Or is the # background update still running? self._current_state_events_membership_up_to_date = False txn = db_conn.cursor( txn_name="_check_safe_current_state_events_membership_updated") self._check_safe_current_state_events_membership_updated_txn(txn) txn.close() if (self.hs.config.run_background_tasks and self.hs.config.metrics_flags.known_servers): self._known_servers_count = 1 self.hs.get_clock().looping_call( self._count_known_servers, 60 * 1000, ) self.hs.get_clock().call_later( 1000, self._count_known_servers, ) LaterGauge( "synapse_federation_known_servers", "", [], lambda: self._known_servers_count, )
def __init__(self, _hs): self.hs = _hs self.pusher_factory = PusherFactory(_hs) self._should_start_pushers = _hs.config.start_pushers self.store = self.hs.get_datastore() self.clock = self.hs.get_clock() # map from user id to app_id:pushkey to pusher self.pushers = { } # type: Dict[str, Dict[str, Union[HttpPusher, EmailPusher]]] # a lock for the pushers dict, since `count_pushers` is called from an different # and we otherwise get concurrent modification errors self._pushers_lock = Lock() def count_pushers(): results = defaultdict(int) # type: Dict[Tuple[str, str], int] with self._pushers_lock: for pushers in self.pushers.values(): for pusher in pushers.values(): k = (type(pusher).__name__, pusher.app_id) results[k] += 1 return results LaterGauge( name="synapse_pushers", desc="the number of active pushers", labels=["kind", "app_id"], caller=count_pushers, )
def __init__(self, hs): self._replication_data_handler = hs.get_replication_data_handler() self._presence_handler = hs.get_presence_handler() self._store = hs.get_datastore() self._notifier = hs.get_notifier() self._clock = hs.get_clock() self._instance_id = hs.get_instance_id() self._instance_name = hs.get_instance_name() self._streams = { stream.NAME: stream(hs) for stream in STREAMS_MAP.values() } # type: Dict[str, Stream] self._position_linearizer = Linearizer("replication_position", clock=self._clock) # Map of stream to batched updates. See RdataCommand for info on how # batching works. self._pending_batches = {} # type: Dict[str, List[Any]] # The factory used to create connections. self._factory = None # type: Optional[ReconnectingClientFactory] # The currently connected connections. (The list of places we need to send # outgoing replication commands to.) self._connections = [] # type: List[AbstractConnection] # For each connection, the incoming streams that are coming from that connection self._streams_by_connection = { } # type: Dict[AbstractConnection, Set[str]] LaterGauge( "synapse_replication_tcp_resource_total_connections", "", [], lambda: len(self._connections), ) self._is_master = hs.config.worker_app is None self._federation_sender = None if self._is_master and not hs.config.send_federation: self._federation_sender = hs.get_federation_sender() self._server_notices_sender = None if self._is_master: self._server_notices_sender = hs.get_server_notices_sender()
def __init__(self, hs): self._replication_data_handler = hs.get_replication_data_handler() self._presence_handler = hs.get_presence_handler() self._store = hs.get_datastore() self._notifier = hs.get_notifier() self._clock = hs.get_clock() self._instance_id = hs.get_instance_id() self._instance_name = hs.get_instance_name() self._streams = { stream.NAME: stream(hs) for stream in STREAMS_MAP.values() } # type: Dict[str, Stream] # List of streams that this instance is the source of self._streams_to_replicate = [] # type: List[Stream] for stream in self._streams.values(): if stream.NAME == CachesStream.NAME: # All workers can write to the cache invalidation stream. self._streams_to_replicate.append(stream) continue if isinstance(stream, (EventsStream, BackfillStream)): # Only add EventStream and BackfillStream as a source on the # instance in charge of event persistence. if hs.get_instance_name() in hs.config.worker.writers.events: self._streams_to_replicate.append(stream) continue if isinstance(stream, TypingStream): # Only add TypingStream as a source on the instance in charge of # typing. if hs.config.worker.writers.typing == hs.get_instance_name(): self._streams_to_replicate.append(stream) continue # Only add any other streams if we're on master. if hs.config.worker_app is not None: continue if stream.NAME == FederationStream.NAME and hs.config.send_federation: # We only support federation stream if federation sending # has been disabled on the master. continue self._streams_to_replicate.append(stream) # Map of stream name to batched updates. See RdataCommand for info on # how batching works. self._pending_batches = {} # type: Dict[str, List[Any]] # The factory used to create connections. self._factory = None # type: Optional[ReconnectingClientFactory] # The currently connected connections. (The list of places we need to send # outgoing replication commands to.) self._connections = [] # type: List[AbstractConnection] LaterGauge( "synapse_replication_tcp_resource_total_connections", "", [], lambda: len(self._connections), ) # When POSITION or RDATA commands arrive, we stick them in a queue and process # them in order in a separate background process. # the streams which are currently being processed by _unsafe_process_queue self._processing_streams = set() # type: Set[str] # for each stream, a queue of commands that are awaiting processing, and the # connection that they arrived on. self._command_queues_by_stream = { stream_name: _StreamCommandQueue() for stream_name in self._streams } # For each connection, the incoming stream names that have received a POSITION # from that connection. self._streams_by_connection = { } # type: Dict[AbstractConnection, Set[str]] LaterGauge( "synapse_replication_tcp_command_queue", "Number of inbound RDATA/POSITION commands queued for processing", ["stream_name"], lambda: {(stream_name, ): len(queue) for stream_name, queue in self._command_queues_by_stream. items()}, ) self._is_master = hs.config.worker_app is None self._federation_sender = None if self._is_master and not hs.config.send_federation: self._federation_sender = hs.get_federation_sender() self._server_notices_sender = None if self._is_master: self._server_notices_sender = hs.get_server_notices_sender()
def __init__(self, hs: "synapse.server.HomeServer"): super().__init__(hs) self.hs = hs self.is_mine_id = hs.is_mine_id self.server_name = hs.hostname self.wheel_timer = WheelTimer() self.notifier = hs.get_notifier() self.federation = hs.get_federation_sender() self.state = hs.get_state_handler() federation_registry = hs.get_federation_registry() federation_registry.register_edu_handler("m.presence", self.incoming_presence) LaterGauge( "synapse_handlers_presence_user_to_current_state_size", "", [], lambda: len(self.user_to_current_state), ) now = self.clock.time_msec() for state in self.user_to_current_state.values(): self.wheel_timer.insert( now=now, obj=state.user_id, then=state.last_active_ts + IDLE_TIMER ) self.wheel_timer.insert( now=now, obj=state.user_id, then=state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT, ) if self.is_mine_id(state.user_id): self.wheel_timer.insert( now=now, obj=state.user_id, then=state.last_federation_update_ts + FEDERATION_PING_INTERVAL, ) else: self.wheel_timer.insert( now=now, obj=state.user_id, then=state.last_federation_update_ts + FEDERATION_TIMEOUT, ) # Set of users who have presence in the `user_to_current_state` that # have not yet been persisted self.unpersisted_users_changes = set() # type: Set[str] hs.get_reactor().addSystemEventTrigger( "before", "shutdown", run_as_background_process, "presence.on_shutdown", self._on_shutdown, ) self._next_serial = 1 # Keeps track of the number of *ongoing* syncs on this process. While # this is non zero a user will never go offline. self.user_to_num_current_syncs = {} # type: Dict[str, int] # Keeps track of the number of *ongoing* syncs on other processes. # While any sync is ongoing on another process the user will never # go offline. # Each process has a unique identifier and an update frequency. If # no update is received from that process within the update period then # we assume that all the sync requests on that process have stopped. # Stored as a dict from process_id to set of user_id, and a dict of # process_id to millisecond timestamp last updated. self.external_process_to_current_syncs = {} # type: Dict[int, Set[str]] self.external_process_last_updated_ms = {} # type: Dict[int, int] self.external_sync_linearizer = Linearizer(name="external_sync_linearizer") # Start a LoopingCall in 30s that fires every 5s. # The initial delay is to allow disconnected clients a chance to # reconnect before we treat them as offline. def run_timeout_handler(): return run_as_background_process( "handle_presence_timeouts", self._handle_timeouts ) self.clock.call_later(30, self.clock.looping_call, run_timeout_handler, 5000) def run_persister(): return run_as_background_process( "persist_presence_changes", self._persist_unpersisted_changes ) self.clock.call_later(60, self.clock.looping_call, run_persister, 60 * 1000) LaterGauge( "synapse_handlers_presence_wheel_timer_size", "", [], lambda: len(self.wheel_timer), ) # Used to handle sending of presence to newly joined users/servers if hs.config.use_presence: self.notifier.add_replication_callback(self.notify_new_event) # Presence is best effort and quickly heals itself, so lets just always # stream from the current state when we restart. self._event_pos = self.store.get_current_events_token() self._event_processing = False
for rm in reqs: rm.update_metrics() # Map from (method, name) -> int, the number of in flight requests of that # type counts = {} for rm in reqs: key = (rm.method, rm.name) counts[key] = counts.get(key, 0) + 1 return counts LaterGauge( "synapse_http_server_in_flight_requests_count", "", ["method", "servlet"], _get_in_flight_counts, ) class RequestMetrics(object): def start(self, time_sec, name, method): self.start = time_sec self.start_context = LoggingContext.current_context() self.name = name self.method = method # _request_stats records resource usage that we have already added # to the "in flight" metrics. self._request_stats = self.start_context.get_resource_usage()
def register(name, queue): LaterGauge( "synapse_federation_send_queue_%s_size" % (queue_name, ), "", [], lambda: len(queue))
self.streams_connecting.add(stream_name) self.send_command(ReplicateCommand(stream_name, token)) def on_connection_closed(self): BaseReplicationStreamProtocol.on_connection_closed(self) self.handler.update_connection(None) # The following simply registers metrics for the replication connections pending_commands = LaterGauge( "synapse_replication_tcp_protocol_pending_commands", "", ["name"], lambda: {(p.name, ): len(p.pending_commands) for p in connected_connections}, ) def transport_buffer_size(protocol): if protocol.transport: size = len( protocol.transport.dataBuffer) + protocol.transport._tempDataLen return size return 0 transport_send_buffer = LaterGauge( "synapse_replication_tcp_protocol_transport_send_buffer",
if cmd.data != self.server_name: logger.error("[%s] Connected to wrong remote: %r", self.id(), cmd.data) self.send_error("Wrong remote") def replicate(self): """Send the subscription request to the server""" logger.info("[%s] Subscribing to replication streams", self.id()) self.send_command(ReplicateCommand()) # The following simply registers metrics for the replication connections pending_commands = LaterGauge( "synapse_replication_tcp_protocol_pending_commands", "", ["name"], lambda: {(p.name,): len(p.pending_commands) for p in connected_connections}, ) def transport_buffer_size(protocol): if protocol.transport: size = len(protocol.transport.dataBuffer) + protocol.transport._tempDataLen return size return 0 transport_send_buffer = LaterGauge( "synapse_replication_tcp_protocol_transport_send_buffer", "", ["name"],
def __init__(self, hs: "HomeServer"): self.hs = hs self.server_name = hs.hostname self.store = hs.get_datastore() self.state = hs.get_state_handler() self.clock = hs.get_clock() self.is_mine_id = hs.is_mine_id self._transaction_manager = TransactionManager(hs) self._instance_name = hs.get_instance_name() self._federation_shard_config = hs.config.worker.federation_shard_config # map from destination to PerDestinationQueue self._per_destination_queues = { } # type: Dict[str, PerDestinationQueue] LaterGauge( "synapse_federation_transaction_queue_pending_destinations", "", [], lambda: sum(1 for d in self._per_destination_queues.values() if d.transmission_loop_running), ) # Map of user_id -> UserPresenceState for all the pending presence # to be sent out by user_id. Entries here get processed and put in # pending_presence_by_dest self.pending_presence = {} # type: Dict[str, UserPresenceState] LaterGauge( "synapse_federation_transaction_queue_pending_pdus", "", [], lambda: sum(d.pending_pdu_count() for d in self._per_destination_queues.values()), ) LaterGauge( "synapse_federation_transaction_queue_pending_edus", "", [], lambda: sum(d.pending_edu_count() for d in self._per_destination_queues.values()), ) self._is_processing = False self._last_poked_id = -1 self._processing_pending_presence = False # map from room_id to a set of PerDestinationQueues which we believe are # awaiting a call to flush_read_receipts_for_room. The presence of an entry # here for a given room means that we are rate-limiting RR flushes to that room, # and that there is a pending call to _flush_rrs_for_room in the system. self._queues_awaiting_rr_flush_by_room = ( {}) # type: Dict[str, Set[PerDestinationQueue]] self._rr_txn_interval_per_room_ms = ( 1000.0 / hs.config.federation_rr_transactions_per_room_per_second) # wake up destinations that have outstanding PDUs to be caught up self._catchup_after_startup_timer = self.clock.call_later( CATCH_UP_STARTUP_DELAY_SEC, run_as_background_process, "wake_destinations_needing_catchup", self._wake_destinations_needing_catchup, ) self._external_cache = hs.get_external_cache()
def __init__(self, hs): """ Args: hs (synapse.server.HomeServer): """ self.hs = hs self.is_mine = hs.is_mine self.is_mine_id = hs.is_mine_id self.clock = hs.get_clock() self.store = hs.get_datastore() self.wheel_timer = WheelTimer() self.notifier = hs.get_notifier() self.federation = hs.get_federation_sender() self.state = hs.get_state_handler() federation_registry = hs.get_federation_registry() federation_registry.register_edu_handler( "m.presence", self.incoming_presence ) federation_registry.register_edu_handler( "m.presence_invite", lambda origin, content: self.invite_presence( observed_user=UserID.from_string(content["observed_user"]), observer_user=UserID.from_string(content["observer_user"]), ) ) federation_registry.register_edu_handler( "m.presence_accept", lambda origin, content: self.accept_presence( observed_user=UserID.from_string(content["observed_user"]), observer_user=UserID.from_string(content["observer_user"]), ) ) federation_registry.register_edu_handler( "m.presence_deny", lambda origin, content: self.deny_presence( observed_user=UserID.from_string(content["observed_user"]), observer_user=UserID.from_string(content["observer_user"]), ) ) distributor = hs.get_distributor() distributor.observe("user_joined_room", self.user_joined_room) active_presence = self.store.take_presence_startup_info() # A dictionary of the current state of users. This is prefilled with # non-offline presence from the DB. We should fetch from the DB if # we can't find a users presence in here. self.user_to_current_state = { state.user_id: state for state in active_presence } LaterGauge( "synapse_handlers_presence_user_to_current_state_size", "", [], lambda: len(self.user_to_current_state) ) now = self.clock.time_msec() for state in active_presence: self.wheel_timer.insert( now=now, obj=state.user_id, then=state.last_active_ts + IDLE_TIMER, ) self.wheel_timer.insert( now=now, obj=state.user_id, then=state.last_user_sync_ts + SYNC_ONLINE_TIMEOUT, ) if self.is_mine_id(state.user_id): self.wheel_timer.insert( now=now, obj=state.user_id, then=state.last_federation_update_ts + FEDERATION_PING_INTERVAL, ) else: self.wheel_timer.insert( now=now, obj=state.user_id, then=state.last_federation_update_ts + FEDERATION_TIMEOUT, ) # Set of users who have presence in the `user_to_current_state` that # have not yet been persisted self.unpersisted_users_changes = set() hs.get_reactor().addSystemEventTrigger("before", "shutdown", self._on_shutdown) self.serial_to_user = {} self._next_serial = 1 # Keeps track of the number of *ongoing* syncs on this process. While # this is non zero a user will never go offline. self.user_to_num_current_syncs = {} # Keeps track of the number of *ongoing* syncs on other processes. # While any sync is ongoing on another process the user will never # go offline. # Each process has a unique identifier and an update frequency. If # no update is received from that process within the update period then # we assume that all the sync requests on that process have stopped. # Stored as a dict from process_id to set of user_id, and a dict of # process_id to millisecond timestamp last updated. self.external_process_to_current_syncs = {} self.external_process_last_updated_ms = {} self.external_sync_linearizer = Linearizer(name="external_sync_linearizer") # Start a LoopingCall in 30s that fires every 5s. # The initial delay is to allow disconnected clients a chance to # reconnect before we treat them as offline. self.clock.call_later( 30, self.clock.looping_call, self._handle_timeouts, 5000, ) self.clock.call_later( 60, self.clock.looping_call, self._persist_unpersisted_changes, 60 * 1000, ) LaterGauge("synapse_handlers_presence_wheel_timer_size", "", [], lambda: len(self.wheel_timer))
def __init__(self, hs): self._replication_data_handler = hs.get_replication_data_handler() self._presence_handler = hs.get_presence_handler() self._store = hs.get_datastore() self._notifier = hs.get_notifier() self._clock = hs.get_clock() self._instance_id = hs.get_instance_id() self._instance_name = hs.get_instance_name() self._streams = { stream.NAME: stream(hs) for stream in STREAMS_MAP.values() } # type: Dict[str, Stream] # List of streams that this instance is the source of self._streams_to_replicate = [] # type: List[Stream] for stream in self._streams.values(): if stream.NAME == CachesStream.NAME: # All workers can write to the cache invalidation stream. self._streams_to_replicate.append(stream) continue if isinstance(stream, (EventsStream, BackfillStream)): # Only add EventStream and BackfillStream as a source on the # instance in charge of event persistence. if hs.config.worker.writers.events == hs.get_instance_name(): self._streams_to_replicate.append(stream) continue # Only add any other streams if we're on master. if hs.config.worker_app is not None: continue if stream.NAME == FederationStream.NAME and hs.config.send_federation: # We only support federation stream if federation sending # has been disabled on the master. continue self._streams_to_replicate.append(stream) self._position_linearizer = Linearizer( "replication_position", clock=self._clock ) # Map of stream name to batched updates. See RdataCommand for info on # how batching works. self._pending_batches = {} # type: Dict[str, List[Any]] # The factory used to create connections. self._factory = None # type: Optional[ReconnectingClientFactory] # The currently connected connections. (The list of places we need to send # outgoing replication commands to.) self._connections = [] # type: List[AbstractConnection] # For each connection, the incoming stream names that are coming from # that connection. self._streams_by_connection = {} # type: Dict[AbstractConnection, Set[str]] LaterGauge( "synapse_replication_tcp_resource_total_connections", "", [], lambda: len(self._connections), ) self._is_master = hs.config.worker_app is None self._federation_sender = None if self._is_master and not hs.config.send_federation: self._federation_sender = hs.get_federation_sender() self._server_notices_sender = None if self._is_master: self._server_notices_sender = hs.get_server_notices_sender()
def __init__(self, hs): self.server_name = hs.hostname self.store = hs.get_datastore() self.state = hs.get_state_handler() self.transaction_actions = TransactionActions(self.store) self.transport_layer = hs.get_federation_transport_client() self.clock = hs.get_clock() self.is_mine_id = hs.is_mine_id # Is a mapping from destinations -> deferreds. Used to keep track # of which destinations have transactions in flight and when they are # done self.pending_transactions = {} LaterGauge( "synapse_federation_transaction_queue_pending_destinations", "", [], lambda: len(self.pending_transactions), ) # Is a mapping from destination -> list of # tuple(pending pdus, deferred, order) self.pending_pdus_by_dest = pdus = {} # destination -> list of tuple(edu, deferred) self.pending_edus_by_dest = edus = {} # Map of user_id -> UserPresenceState for all the pending presence # to be sent out by user_id. Entries here get processed and put in # pending_presence_by_dest self.pending_presence = {} # Map of destination -> user_id -> UserPresenceState of pending presence # to be sent to each destinations self.pending_presence_by_dest = presence = {} # Pending EDUs by their "key". Keyed EDUs are EDUs that get clobbered # based on their key (e.g. typing events by room_id) # Map of destination -> (edu_type, key) -> Edu self.pending_edus_keyed_by_dest = edus_keyed = {} LaterGauge( "synapse_federation_transaction_queue_pending_pdus", "", [], lambda: sum(map(len, pdus.values())), ) LaterGauge( "synapse_federation_transaction_queue_pending_edus", "", [], lambda: (sum(map(len, edus.values())) + sum(map(len, presence.values())) + sum(map(len, edus_keyed.values()))), ) # destination -> list of tuple(failure, deferred) self.pending_failures_by_dest = {} # destination -> stream_id of last successfully sent to-device message. # NB: may be a long or an int. self.last_device_stream_id_by_dest = {} # destination -> stream_id of last successfully sent device list # update. self.last_device_list_stream_id_by_dest = {} # HACK to get unique tx id self._next_txn_id = int(self.clock.time_msec()) self._order = 1 self._is_processing = False self._last_poked_id = -1 self._processing_pending_presence = False
def __init__(self, hs: "HomeServer"): self._replication_data_handler = hs.get_replication_data_handler() self._presence_handler = hs.get_presence_handler() self._store = hs.get_datastores().main self._notifier = hs.get_notifier() self._clock = hs.get_clock() self._instance_id = hs.get_instance_id() self._instance_name = hs.get_instance_name() # Additional Redis channel suffixes to subscribe to. self._channels_to_subscribe_to: List[str] = [] self._is_presence_writer = ( hs.get_instance_name() in hs.config.worker.writers.presence ) self._streams: Dict[str, Stream] = { stream.NAME: stream(hs) for stream in STREAMS_MAP.values() } # List of streams that this instance is the source of self._streams_to_replicate: List[Stream] = [] for stream in self._streams.values(): if hs.config.redis.redis_enabled and stream.NAME == CachesStream.NAME: # All workers can write to the cache invalidation stream when # using redis. self._streams_to_replicate.append(stream) continue if isinstance(stream, (EventsStream, BackfillStream)): # Only add EventStream and BackfillStream as a source on the # instance in charge of event persistence. if hs.get_instance_name() in hs.config.worker.writers.events: self._streams_to_replicate.append(stream) continue if isinstance(stream, ToDeviceStream): # Only add ToDeviceStream as a source on instances in charge of # sending to device messages. if hs.get_instance_name() in hs.config.worker.writers.to_device: self._streams_to_replicate.append(stream) continue if isinstance(stream, TypingStream): # Only add TypingStream as a source on the instance in charge of # typing. if hs.get_instance_name() in hs.config.worker.writers.typing: self._streams_to_replicate.append(stream) continue if isinstance(stream, (AccountDataStream, TagAccountDataStream)): # Only add AccountDataStream and TagAccountDataStream as a source on the # instance in charge of account_data persistence. if hs.get_instance_name() in hs.config.worker.writers.account_data: self._streams_to_replicate.append(stream) continue if isinstance(stream, ReceiptsStream): # Only add ReceiptsStream as a source on the instance in charge of # receipts. if hs.get_instance_name() in hs.config.worker.writers.receipts: self._streams_to_replicate.append(stream) continue if isinstance(stream, (PresenceStream, PresenceFederationStream)): # Only add PresenceStream as a source on the instance in charge # of presence. if self._is_presence_writer: self._streams_to_replicate.append(stream) continue # Only add any other streams if we're on master. if hs.config.worker.worker_app is not None: continue if ( stream.NAME == FederationStream.NAME and hs.config.worker.send_federation ): # We only support federation stream if federation sending # has been disabled on the master. continue self._streams_to_replicate.append(stream) # Map of stream name to batched updates. See RdataCommand for info on # how batching works. self._pending_batches: Dict[str, List[Any]] = {} # The factory used to create connections. self._factory: Optional[ReconnectingClientFactory] = None # The currently connected connections. (The list of places we need to send # outgoing replication commands to.) self._connections: List[IReplicationConnection] = [] LaterGauge( "synapse_replication_tcp_resource_total_connections", "", [], lambda: len(self._connections), ) # When POSITION or RDATA commands arrive, we stick them in a queue and process # them in order in a separate background process. # the streams which are currently being processed by _unsafe_process_queue self._processing_streams: Set[str] = set() # for each stream, a queue of commands that are awaiting processing, and the # connection that they arrived on. self._command_queues_by_stream = { stream_name: _StreamCommandQueue() for stream_name in self._streams } # For each connection, the incoming stream names that have received a POSITION # from that connection. self._streams_by_connection: Dict[IReplicationConnection, Set[str]] = {} LaterGauge( "synapse_replication_tcp_command_queue", "Number of inbound RDATA/POSITION commands queued for processing", ["stream_name"], lambda: { (stream_name,): len(queue) for stream_name, queue in self._command_queues_by_stream.items() }, ) self._is_master = hs.config.worker.worker_app is None self._federation_sender = None if self._is_master and not hs.config.worker.send_federation: self._federation_sender = hs.get_federation_sender() self._server_notices_sender = None if self._is_master: self._server_notices_sender = hs.get_server_notices_sender() if hs.config.redis.redis_enabled: # If we're using Redis, it's the background worker that should # receive USER_IP commands and store the relevant client IPs. self._should_insert_client_ips = hs.config.worker.run_background_tasks else: # If we're NOT using Redis, this must be handled by the master self._should_insert_client_ips = hs.get_instance_name() == "master" if self._is_master or self._should_insert_client_ips: self.subscribe_to_channel("USER_IP")