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,
        )
Esempio n. 2
0
 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)
Esempio n. 3
0
                    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)
Esempio n. 4
0
    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)
Esempio n. 5
0
    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,
        )
Esempio n. 6
0
    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
        })
Esempio n. 7
0
    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
        )
Esempio n. 8
0
    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,
        )
Esempio n. 9
0
 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,
     )
Esempio n. 10
0
    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,
            )
Esempio n. 11
0
    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)
Esempio n. 12
0
    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)
Esempio n. 13
0
    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],
        )
Esempio n. 14
0
    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)
Esempio n. 15
0
    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)
Esempio n. 16
0
    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,
            )
Esempio n. 17
0
    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,
        )
Esempio n. 18
0
    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,
        )
Esempio n. 19
0
    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,
        )
Esempio n. 20
0
                    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)
Esempio n. 21
0
    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)
Esempio n. 22
0
    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,
        )
Esempio n. 23
0
    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)
Esempio n. 24
0
    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)
Esempio n. 25
0
    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)
Esempio n. 26
0
    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
Esempio n. 27
0
    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)
Esempio n. 28
0
    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)
Esempio n. 30
0
    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,
        )
Esempio n. 31
0
    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,
        )
Esempio n. 32
0
    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,
        )
Esempio n. 33
0
    def _start_processing(self):
        if self._is_processing:
            return

        run_as_background_process("emailpush.process", self._process)
Esempio n. 34
0
    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
Esempio n. 35
0
 def _start_update_recently_accessed(self):
     return run_as_background_process(
         "update_recently_accessed_media",
         self._update_recently_accessed,
     )
Esempio n. 36
0
 def _start_update_remote_profile_cache(self):
     return run_as_background_process(
         "Update remote profile", self._update_remote_profile_cache,
     )
Esempio n. 37
0
 def run_timeout_handler():
     return run_as_background_process(
         "handle_presence_timeouts", self._handle_timeouts
     )
Esempio n. 38
0
 def _start_renew_attestations(self):
     return run_as_background_process("renew_attestations",
                                      self._renew_attestations)
Esempio n. 39
0
 def _start_apply_media_retention_rules(self) -> Deferred:
     return run_as_background_process("apply_media_retention_rules",
                                      self._apply_media_retention_rules)
Esempio n. 40
0
 def start_phone_stats_home():
     return run_as_background_process("phone_stats_home", phone_stats_home)
Esempio n. 41
0
 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,
     )
Esempio n. 43
0
 def start_generate_monthly_active_users():
     return run_as_background_process(
         "generate_monthly_active_users",
         generate_monthly_active_users,
     )
Esempio n. 44
0
 def reap_monthly_active_users():
     return run_as_background_process(
         "reap_monthly_active_users",
         hs.get_datastore().reap_monthly_active_users,
     )
Esempio n. 45
0
 def generate_user_daily_visit_stats():
     return run_as_background_process(
         "generate_user_daily_visits",
         hs.get_datastore().generate_user_daily_visits,
     )
Esempio n. 46
0
 def _retry():
     run_as_background_process(
         "as-recoverer-%s" % (self.service.id,),
         self.retry,
     )
Esempio n. 47
0
 def _start_cleanup_transactions(self):
     return run_as_background_process(
         "cleanup_transactions", self._cleanup_transactions
     )
Esempio n. 48
0
 def _start_expire_url_cache_data(self):
     return run_as_background_process("expire_url_cache_data",
                                      self._expire_url_cache_data)
Esempio n. 49
0
 def start_generate_monthly_active_users():
     return run_as_background_process(
         "generate_monthly_active_users",
         generate_monthly_active_users,
     )
Esempio n. 50
0
 def run_persister():
     return run_as_background_process(
         "persist_presence_changes", self._persist_unpersisted_changes
     )
Esempio n. 51
0
 def _start_cleanup_transactions(self):
     return run_as_background_process(
         "cleanup_transactions",
         self._cleanup_transactions,
     )
Esempio n. 52
0
 def f():
     return run_as_background_process(
         "prune_cache_%s" % self._cache_name,
         self._prune_cache,
     )
Esempio n. 53
0
 def _start_renew_attestations(self) -> "Deferred[None]":
     return run_as_background_process("renew_attestations",
                                      self._renew_attestations)
Esempio n. 54
0
 def start_doing_background_updates(self):
     run_as_background_process("background_updates",
                               self.run_background_updates)
Esempio n. 55
0
 def _retry():
     run_as_background_process("as-recoverer-%s" % (self.service.id, ),
                               self.retry)
Esempio n. 56
0
    def _start_processing(self) -> None:
        if self._is_processing:
            return

        run_as_background_process("httppush.process", self._process)
Esempio n. 57
0
    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)
Esempio n. 58
0
 def _start_rotate_notifs(self):
     return run_as_background_process("rotate_notifs", self._rotate_notifs)
Esempio n. 59
0
    def _start_processing(self):
        if self._is_processing:
            return

        run_as_background_process("httppush.process", self._process)