Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit fae81f2

Browse files
Add a storage method for returning all current presence from all users (#9650)
Split off from #9491 Adds a storage method for getting the current presence of all local users, optionally excluding those that are offline. This will be used by the code in #9491 when a PresenceRouter module informs Synapse that a given user should have `"ALL"` user presence updates routed to them. Specifically, it is used here: https://github.com/matrix-org/synapse/blob/b588f16e391d664b11f43257eabf70663f0c6d59/synapse/handlers/presence.py#L1131-L1133 Note that there is a `get_all_presence_updates` function just above. That function is intended to walk up the table through stream IDs, and is primarily used by the presence replication stream. I could possibly make use of it in the PresenceRouter-related code, but it would be a bit of a bodge.
1 parent c602ba8 commit fae81f2

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

changelog.d/9650.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a storage method for pulling all current user presence state from the database.

synapse/storage/database.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,6 +1906,7 @@ def simple_select_list_paginate_txn(
19061906
retcols: Iterable[str],
19071907
filters: Optional[Dict[str, Any]] = None,
19081908
keyvalues: Optional[Dict[str, Any]] = None,
1909+
exclude_keyvalues: Optional[Dict[str, Any]] = None,
19091910
order_direction: str = "ASC",
19101911
) -> List[Dict[str, Any]]:
19111912
"""
@@ -1929,7 +1930,10 @@ def simple_select_list_paginate_txn(
19291930
apply a WHERE ? LIKE ? clause.
19301931
keyvalues:
19311932
column names and values to select the rows with, or None to not
1932-
apply a WHERE clause.
1933+
apply a WHERE key = value clause.
1934+
exclude_keyvalues:
1935+
column names and values to exclude rows with, or None to not
1936+
apply a WHERE key != value clause.
19331937
order_direction: Whether the results should be ordered "ASC" or "DESC".
19341938
19351939
Returns:
@@ -1938,7 +1942,7 @@ def simple_select_list_paginate_txn(
19381942
if order_direction not in ["ASC", "DESC"]:
19391943
raise ValueError("order_direction must be one of 'ASC' or 'DESC'.")
19401944

1941-
where_clause = "WHERE " if filters or keyvalues else ""
1945+
where_clause = "WHERE " if filters or keyvalues or exclude_keyvalues else ""
19421946
arg_list = [] # type: List[Any]
19431947
if filters:
19441948
where_clause += " AND ".join("%s LIKE ?" % (k,) for k in filters)
@@ -1947,6 +1951,9 @@ def simple_select_list_paginate_txn(
19471951
if keyvalues:
19481952
where_clause += " AND ".join("%s = ?" % (k,) for k in keyvalues)
19491953
arg_list += list(keyvalues.values())
1954+
if exclude_keyvalues:
1955+
where_clause += " AND ".join("%s != ?" % (k,) for k in exclude_keyvalues)
1956+
arg_list += list(exclude_keyvalues.values())
19501957

19511958
sql = "SELECT %s FROM %s %s ORDER BY %s %s LIMIT ? OFFSET ?" % (
19521959
", ".join(retcols),

synapse/storage/databases/main/presence.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
from typing import List, Tuple
16+
from typing import Dict, List, Tuple
1717

1818
from synapse.api.presence import UserPresenceState
1919
from synapse.storage._base import SQLBaseStore, make_in_list_sql_clause
@@ -157,5 +157,63 @@ async def get_presence_for_users(self, user_ids):
157157

158158
return {row["user_id"]: UserPresenceState(**row) for row in rows}
159159

160+
async def get_presence_for_all_users(
161+
self,
162+
include_offline: bool = True,
163+
) -> Dict[str, UserPresenceState]:
164+
"""Retrieve the current presence state for all users.
165+
166+
Note that the presence_stream table is culled frequently, so it should only
167+
contain the latest presence state for each user.
168+
169+
Args:
170+
include_offline: Whether to include offline presence states
171+
172+
Returns:
173+
A dict of user IDs to their current UserPresenceState.
174+
"""
175+
users_to_state = {}
176+
177+
exclude_keyvalues = None
178+
if not include_offline:
179+
# Exclude offline presence state
180+
exclude_keyvalues = {"state": "offline"}
181+
182+
# This may be a very heavy database query.
183+
# We paginate in order to not block a database connection.
184+
limit = 100
185+
offset = 0
186+
while True:
187+
rows = await self.db_pool.runInteraction(
188+
"get_presence_for_all_users",
189+
self.db_pool.simple_select_list_paginate_txn,
190+
"presence_stream",
191+
orderby="stream_id",
192+
start=offset,
193+
limit=limit,
194+
exclude_keyvalues=exclude_keyvalues,
195+
retcols=(
196+
"user_id",
197+
"state",
198+
"last_active_ts",
199+
"last_federation_update_ts",
200+
"last_user_sync_ts",
201+
"status_msg",
202+
"currently_active",
203+
),
204+
order_direction="ASC",
205+
)
206+
207+
for row in rows:
208+
users_to_state[row["user_id"]] = UserPresenceState(**row)
209+
210+
# We've run out of updates to query
211+
if len(rows) < limit:
212+
break
213+
214+
offset += limit
215+
216+
return users_to_state
217+
160218
def get_current_presence_token(self):
161219
return self._presence_id_gen.get_current_token()

0 commit comments

Comments
 (0)