Beispiel #1
0
    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)
Beispiel #2
0
    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),
        )
Beispiel #3
0
    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)
Beispiel #4
0
    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))
Beispiel #5
0
    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))
Beispiel #6
0
    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),
        )
Beispiel #7
0
 def register(name: str, queue: Sized) -> None:
     LaterGauge(
         "synapse_federation_send_queue_%s_size" % (queue_name, ),
         "",
         [],
         lambda: len(queue),
     )
Beispiel #8
0
    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,
            )
Beispiel #9
0
    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,
            )
Beispiel #11
0
    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,
        )
Beispiel #12
0
    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()
Beispiel #13
0
    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()
Beispiel #14
0
    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
Beispiel #15
0
    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()
Beispiel #16
0
 def register(name, queue):
     LaterGauge(
         "synapse_federation_send_queue_%s_size" % (queue_name, ), "",
         [], lambda: len(queue))
Beispiel #17
0
        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",
Beispiel #18
0
        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"],
Beispiel #19
0
    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()
Beispiel #20
0
    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))
Beispiel #21
0
    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()
Beispiel #22
0
    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
Beispiel #23
0
    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")