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

Commit 3e803be

Browse files
committed
Reduce DB load of /sync when using presence
While the query was fast, we were calling it *a lot*.
1 parent 49f0686 commit 3e803be

File tree

1 file changed

+43
-27
lines changed

1 file changed

+43
-27
lines changed

synapse/storage/databases/main/presence.py

+43-27
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Tuple, cast
15+
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, cast
1616

1717
from synapse.api.presence import PresenceState, UserPresenceState
1818
from synapse.replication.tcp.streams import PresenceStream
@@ -22,6 +22,7 @@
2222
LoggingDatabaseConnection,
2323
LoggingTransaction,
2424
)
25+
from synapse.storage.databases.main.cache import CacheInvalidationWorkerStore
2526
from synapse.storage.engines import PostgresEngine
2627
from synapse.storage.types import Connection
2728
from synapse.storage.util.id_generators import (
@@ -56,7 +57,7 @@ def __init__(
5657
)
5758

5859

59-
class PresenceStore(PresenceBackgroundUpdateStore):
60+
class PresenceStore(PresenceBackgroundUpdateStore, CacheInvalidationWorkerStore):
6061
def __init__(
6162
self,
6263
database: DatabasePool,
@@ -281,20 +282,25 @@ async def should_user_receive_full_presence_with_token(
281282
True if the user should have full presence sent to them, False otherwise.
282283
"""
283284

284-
def _should_user_receive_full_presence_with_token_txn(
285-
txn: LoggingTransaction,
286-
) -> bool:
287-
sql = """
288-
SELECT 1 FROM users_to_send_full_presence_to
289-
WHERE user_id = ?
290-
AND presence_stream_id >= ?
291-
"""
292-
txn.execute(sql, (user_id, from_token))
293-
return bool(txn.fetchone())
285+
token = await self._get_when_user_should_receive_full_presence(user_id)
286+
if token is None:
287+
return False
294288

295-
return await self.db_pool.runInteraction(
296-
"should_user_receive_full_presence_with_token",
297-
_should_user_receive_full_presence_with_token_txn,
289+
return from_token <= token
290+
291+
@cached()
292+
async def _get_when_user_should_receive_full_presence(
293+
self, user_id: str
294+
) -> Optional[int]:
295+
"""Return the presence stream token, if any, which should trigger the
296+
user to receive full presence.
297+
"""
298+
return await self.db_pool.simple_select_one_onecol(
299+
table="users_to_send_full_presence_to",
300+
keyvalues={"user_id": user_id},
301+
retcol="presence_stream_id",
302+
allow_none=True,
303+
desc="_get_when_user_should_receive_full_presence",
298304
)
299305

300306
async def add_users_to_send_full_presence_to(self, user_ids: Iterable[str]) -> None:
@@ -307,18 +313,28 @@ async def add_users_to_send_full_presence_to(self, user_ids: Iterable[str]) -> N
307313
# Add user entries to the table, updating the presence_stream_id column if the user already
308314
# exists in the table.
309315
presence_stream_id = self._presence_id_gen.get_current_token()
310-
await self.db_pool.simple_upsert_many(
311-
table="users_to_send_full_presence_to",
312-
key_names=("user_id",),
313-
key_values=[(user_id,) for user_id in user_ids],
314-
value_names=("presence_stream_id",),
315-
# We save the current presence stream ID token along with the user ID entry so
316-
# that when a user /sync's, even if they syncing multiple times across separate
317-
# devices at different times, each device will receive full presence once - when
318-
# the presence stream ID in their sync token is less than the one in the table
319-
# for their user ID.
320-
value_values=[(presence_stream_id,) for _ in user_ids],
321-
desc="add_users_to_send_full_presence_to",
316+
317+
def _add_users_to_send_full_presence_to(txn: LoggingTransaction) -> None:
318+
self.db_pool.simple_upsert_many_txn(
319+
txn,
320+
table="users_to_send_full_presence_to",
321+
key_names=("user_id",),
322+
key_values=[(user_id,) for user_id in user_ids],
323+
value_names=("presence_stream_id",),
324+
# We save the current presence stream ID token along with the user ID entry so
325+
# that when a user /sync's, even if they syncing multiple times across separate
326+
# devices at different times, each device will receive full presence once - when
327+
# the presence stream ID in their sync token is less than the one in the table
328+
# for their user ID.
329+
value_values=[(presence_stream_id,) for _ in user_ids],
330+
)
331+
for user_id in user_ids:
332+
self._invalidate_cache_and_stream(
333+
txn, self._get_when_user_should_receive_full_presence, (user_id,)
334+
)
335+
336+
return await self.db_pool.runInteraction(
337+
"add_users_to_send_full_presence_to", _add_users_to_send_full_presence_to
322338
)
323339

324340
async def get_presence_for_all_users(

0 commit comments

Comments
 (0)