Reduce DB load of /sync when using presence (#12885)
While the query was fast, we were calling it *a lot*.pull/12895/head
parent
5e17922ef7
commit
c8684e6792
|
@ -0,0 +1 @@
|
||||||
|
Reduce database load of `/sync` when presence is enabled.
|
|
@ -12,7 +12,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Tuple, cast
|
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, cast
|
||||||
|
|
||||||
from synapse.api.presence import PresenceState, UserPresenceState
|
from synapse.api.presence import PresenceState, UserPresenceState
|
||||||
from synapse.replication.tcp.streams import PresenceStream
|
from synapse.replication.tcp.streams import PresenceStream
|
||||||
|
@ -22,6 +22,7 @@ from synapse.storage.database import (
|
||||||
LoggingDatabaseConnection,
|
LoggingDatabaseConnection,
|
||||||
LoggingTransaction,
|
LoggingTransaction,
|
||||||
)
|
)
|
||||||
|
from synapse.storage.databases.main.cache import CacheInvalidationWorkerStore
|
||||||
from synapse.storage.engines import PostgresEngine
|
from synapse.storage.engines import PostgresEngine
|
||||||
from synapse.storage.types import Connection
|
from synapse.storage.types import Connection
|
||||||
from synapse.storage.util.id_generators import (
|
from synapse.storage.util.id_generators import (
|
||||||
|
@ -56,7 +57,7 @@ class PresenceBackgroundUpdateStore(SQLBaseStore):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PresenceStore(PresenceBackgroundUpdateStore):
|
class PresenceStore(PresenceBackgroundUpdateStore, CacheInvalidationWorkerStore):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
database: DatabasePool,
|
database: DatabasePool,
|
||||||
|
@ -281,20 +282,30 @@ class PresenceStore(PresenceBackgroundUpdateStore):
|
||||||
True if the user should have full presence sent to them, False otherwise.
|
True if the user should have full presence sent to them, False otherwise.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _should_user_receive_full_presence_with_token_txn(
|
token = await self._get_full_presence_stream_token_for_user(user_id)
|
||||||
txn: LoggingTransaction,
|
if token is None:
|
||||||
) -> bool:
|
return False
|
||||||
sql = """
|
|
||||||
SELECT 1 FROM users_to_send_full_presence_to
|
|
||||||
WHERE user_id = ?
|
|
||||||
AND presence_stream_id >= ?
|
|
||||||
"""
|
|
||||||
txn.execute(sql, (user_id, from_token))
|
|
||||||
return bool(txn.fetchone())
|
|
||||||
|
|
||||||
return await self.db_pool.runInteraction(
|
return from_token <= token
|
||||||
"should_user_receive_full_presence_with_token",
|
|
||||||
_should_user_receive_full_presence_with_token_txn,
|
@cached()
|
||||||
|
async def _get_full_presence_stream_token_for_user(
|
||||||
|
self, user_id: str
|
||||||
|
) -> Optional[int]:
|
||||||
|
"""Get the presence token corresponding to the last full presence update
|
||||||
|
for this user.
|
||||||
|
|
||||||
|
If the user presents a sync token with a presence stream token at least
|
||||||
|
as old as the result, then we need to send them a full presence update.
|
||||||
|
|
||||||
|
If this user has never needed a full presence update, returns `None`.
|
||||||
|
"""
|
||||||
|
return await self.db_pool.simple_select_one_onecol(
|
||||||
|
table="users_to_send_full_presence_to",
|
||||||
|
keyvalues={"user_id": user_id},
|
||||||
|
retcol="presence_stream_id",
|
||||||
|
allow_none=True,
|
||||||
|
desc="_get_full_presence_stream_token_for_user",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def add_users_to_send_full_presence_to(self, user_ids: Iterable[str]) -> None:
|
async def add_users_to_send_full_presence_to(self, user_ids: Iterable[str]) -> None:
|
||||||
|
@ -307,18 +318,28 @@ class PresenceStore(PresenceBackgroundUpdateStore):
|
||||||
# Add user entries to the table, updating the presence_stream_id column if the user already
|
# Add user entries to the table, updating the presence_stream_id column if the user already
|
||||||
# exists in the table.
|
# exists in the table.
|
||||||
presence_stream_id = self._presence_id_gen.get_current_token()
|
presence_stream_id = self._presence_id_gen.get_current_token()
|
||||||
await self.db_pool.simple_upsert_many(
|
|
||||||
table="users_to_send_full_presence_to",
|
def _add_users_to_send_full_presence_to(txn: LoggingTransaction) -> None:
|
||||||
key_names=("user_id",),
|
self.db_pool.simple_upsert_many_txn(
|
||||||
key_values=[(user_id,) for user_id in user_ids],
|
txn,
|
||||||
value_names=("presence_stream_id",),
|
table="users_to_send_full_presence_to",
|
||||||
# We save the current presence stream ID token along with the user ID entry so
|
key_names=("user_id",),
|
||||||
# that when a user /sync's, even if they syncing multiple times across separate
|
key_values=[(user_id,) for user_id in user_ids],
|
||||||
# devices at different times, each device will receive full presence once - when
|
value_names=("presence_stream_id",),
|
||||||
# the presence stream ID in their sync token is less than the one in the table
|
# We save the current presence stream ID token along with the user ID entry so
|
||||||
# for their user ID.
|
# that when a user /sync's, even if they syncing multiple times across separate
|
||||||
value_values=[(presence_stream_id,) for _ in user_ids],
|
# devices at different times, each device will receive full presence once - when
|
||||||
desc="add_users_to_send_full_presence_to",
|
# the presence stream ID in their sync token is less than the one in the table
|
||||||
|
# for their user ID.
|
||||||
|
value_values=[(presence_stream_id,) for _ in user_ids],
|
||||||
|
)
|
||||||
|
for user_id in user_ids:
|
||||||
|
self._invalidate_cache_and_stream(
|
||||||
|
txn, self._get_full_presence_stream_token_for_user, (user_id,)
|
||||||
|
)
|
||||||
|
|
||||||
|
return await self.db_pool.runInteraction(
|
||||||
|
"add_users_to_send_full_presence_to", _add_users_to_send_full_presence_to
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_presence_for_all_users(
|
async def get_presence_for_all_users(
|
||||||
|
|
Loading…
Reference in New Issue