class PusherSlaveStore( SlavedEventStore, SlavedPusherStore, SlavedReceiptsStore, SlavedAccountDataStore, RoomStore, ): update_pusher_last_stream_ordering_and_success = __func__( DataStore.update_pusher_last_stream_ordering_and_success ) update_pusher_failing_since = __func__(DataStore.update_pusher_failing_since) update_pusher_last_stream_ordering = __func__( DataStore.update_pusher_last_stream_ordering ) get_throttle_params_by_room = __func__(DataStore.get_throttle_params_by_room) set_throttle_params = __func__(DataStore.set_throttle_params) get_time_of_last_push_action_before = __func__( DataStore.get_time_of_last_push_action_before ) get_profile_displayname = __func__(DataStore.get_profile_displayname)
class GenericWorkerPresence(object): def __init__(self, hs): self.hs = hs self.is_mine_id = hs.is_mine_id self.http_client = hs.get_simple_http_client() self.store = hs.get_datastore() self.user_to_num_current_syncs = {} self.clock = hs.get_clock() self.notifier = hs.get_notifier() active_presence = self.store.take_presence_startup_info() self.user_to_current_state = { state.user_id: state for state in active_presence } # user_id -> last_sync_ms. Lists the users that have stopped syncing # but we haven't notified the master of that yet self.users_going_offline = {} self._send_stop_syncing_loop = self.clock.looping_call( self.send_stop_syncing, UPDATE_SYNCING_USERS_MS) self.process_id = random_string(16) logger.info("Presence process_id is %r", self.process_id) def send_user_sync(self, user_id, is_syncing, last_sync_ms): if self.hs.config.use_presence: self.hs.get_tcp_replication().send_user_sync( user_id, is_syncing, last_sync_ms) def mark_as_coming_online(self, user_id): """A user has started syncing. Send a UserSync to the master, unless they had recently stopped syncing. Args: user_id (str) """ going_offline = self.users_going_offline.pop(user_id, None) if not going_offline: # Safe to skip because we haven't yet told the master they were offline self.send_user_sync(user_id, True, self.clock.time_msec()) def mark_as_going_offline(self, user_id): """A user has stopped syncing. We wait before notifying the master as its likely they'll come back soon. This allows us to avoid sending a stopped syncing immediately followed by a started syncing notification to the master Args: user_id (str) """ self.users_going_offline[user_id] = self.clock.time_msec() def send_stop_syncing(self): """Check if there are any users who have stopped syncing a while ago and haven't come back yet. If there are poke the master about them. """ now = self.clock.time_msec() for user_id, last_sync_ms in list(self.users_going_offline.items()): if now - last_sync_ms > UPDATE_SYNCING_USERS_MS: self.users_going_offline.pop(user_id, None) self.send_user_sync(user_id, False, last_sync_ms) def set_state(self, user, state, ignore_status_msg=False): # TODO Hows this supposed to work? return defer.succeed(None) get_states = __func__(PresenceHandler.get_states) get_state = __func__(PresenceHandler.get_state) current_state_for_users = __func__(PresenceHandler.current_state_for_users) def user_syncing(self, user_id, affect_presence): if affect_presence: curr_sync = self.user_to_num_current_syncs.get(user_id, 0) self.user_to_num_current_syncs[user_id] = curr_sync + 1 # If we went from no in flight sync to some, notify replication if self.user_to_num_current_syncs[user_id] == 1: self.mark_as_coming_online(user_id) def _end(): # We check that the user_id is in user_to_num_current_syncs because # user_to_num_current_syncs may have been cleared if we are # shutting down. if affect_presence and user_id in self.user_to_num_current_syncs: self.user_to_num_current_syncs[user_id] -= 1 # If we went from one in flight sync to non, notify replication if self.user_to_num_current_syncs[user_id] == 0: self.mark_as_going_offline(user_id) @contextlib.contextmanager def _user_syncing(): try: yield finally: _end() return defer.succeed(_user_syncing()) @defer.inlineCallbacks def notify_from_replication(self, states, stream_id): parties = yield get_interested_parties(self.store, states) room_ids_to_states, users_to_states = parties self.notifier.on_new_event( "presence_key", stream_id, rooms=room_ids_to_states.keys(), users=users_to_states.keys(), ) @defer.inlineCallbacks def process_replication_rows(self, token, rows): states = [ UserPresenceState( row.user_id, row.state, row.last_active_ts, row.last_federation_update_ts, row.last_user_sync_ts, row.status_msg, row.currently_active, ) for row in rows ] for state in states: self.user_to_current_state[state.user_id] = state stream_id = token yield self.notify_from_replication(states, stream_id) def get_currently_syncing_users(self): if self.hs.config.use_presence: return [ user_id for user_id, count in self.user_to_num_current_syncs.items() if count > 0 ] else: return set()