Skip to content

Commit

Permalink
Use a new token format for sliding sync (#17452)
Browse files Browse the repository at this point in the history
This is in preparation for adding per-connection state.

---------

Co-authored-by: Eric Eastwood <[email protected]>
  • Loading branch information
erikjohnston and MadLittleMods authored Jul 24, 2024
1 parent 4b9f4c2 commit 8bbc98e
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 208 deletions.
1 change: 1 addition & 0 deletions changelog.d/17452.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Change sliding sync to use their own token format in preparation for storing per-connection state.
30 changes: 18 additions & 12 deletions synapse/handlers/sliding_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
PersistedEventPosition,
Requester,
RoomStreamToken,
SlidingSyncStreamToken,
StateMap,
StreamKeyType,
StreamToken,
Expand Down Expand Up @@ -362,7 +363,7 @@ async def wait_for_sync_for_user(
self,
requester: Requester,
sync_config: SlidingSyncConfig,
from_token: Optional[StreamToken] = None,
from_token: Optional[SlidingSyncStreamToken] = None,
timeout_ms: int = 0,
) -> SlidingSyncResult:
"""
Expand Down Expand Up @@ -393,7 +394,7 @@ async def wait_for_sync_for_user(
# this returns false, it means we timed out waiting, and we should
# just return an empty response.
before_wait_ts = self.clock.time_msec()
if not await self.notifier.wait_for_stream_token(from_token):
if not await self.notifier.wait_for_stream_token(from_token.stream_token):
logger.warning(
"Timed out waiting for worker to catch up. Returning empty response"
)
Expand Down Expand Up @@ -431,7 +432,7 @@ async def current_sync_callback(
sync_config.user.to_string(),
timeout_ms,
current_sync_callback,
from_token=from_token,
from_token=from_token.stream_token,
)

return result
Expand All @@ -440,7 +441,7 @@ async def current_sync_for_user(
self,
sync_config: SlidingSyncConfig,
to_token: StreamToken,
from_token: Optional[StreamToken] = None,
from_token: Optional[SlidingSyncStreamToken] = None,
) -> SlidingSyncResult:
"""
Generates the response body of a Sliding Sync result, represented as a
Expand Down Expand Up @@ -473,7 +474,7 @@ async def current_sync_for_user(
await self.get_room_membership_for_user_at_to_token(
user=sync_config.user,
to_token=to_token,
from_token=from_token,
from_token=from_token.stream_token if from_token else None,
)
)

Expand Down Expand Up @@ -631,8 +632,11 @@ async def handle_room(room_id: str) -> None:
to_token=to_token,
)

# TODO: Update this when we implement per-connection state
connection_token = 0

return SlidingSyncResult(
next_pos=to_token,
next_pos=SlidingSyncStreamToken(to_token, connection_token),
lists=lists,
rooms=rooms,
extensions=extensions,
Expand Down Expand Up @@ -1367,7 +1371,7 @@ async def get_room_sync_data(
room_id: str,
room_sync_config: RoomSyncConfig,
room_membership_for_user_at_to_token: _RoomMembershipForUser,
from_token: Optional[StreamToken],
from_token: Optional[SlidingSyncStreamToken],
to_token: StreamToken,
) -> SlidingSyncResult.RoomResult:
"""
Expand Down Expand Up @@ -1431,7 +1435,7 @@ async def get_room_sync_data(
# - TODO: For an incremental sync where we haven't sent it down this
# connection before
to_bound = (
from_token.room_key
from_token.stream_token.room_key
if from_token is not None
and not room_membership_for_user_at_to_token.newly_joined
else None
Expand Down Expand Up @@ -1498,7 +1502,9 @@ async def get_room_sync_data(
instance_name=timeline_event.internal_metadata.instance_name,
stream=timeline_event.internal_metadata.stream_ordering,
)
if persisted_position.persisted_after(from_token.room_key):
if persisted_position.persisted_after(
from_token.stream_token.room_key
):
num_live += 1
else:
# Since we're iterating over the timeline events in
Expand Down Expand Up @@ -1786,7 +1792,7 @@ async def get_extensions_response(
self,
sync_config: SlidingSyncConfig,
to_token: StreamToken,
from_token: Optional[StreamToken],
from_token: Optional[SlidingSyncStreamToken],
) -> SlidingSyncResult.Extensions:
"""Handle extension requests.
Expand Down Expand Up @@ -1900,7 +1906,7 @@ async def get_e2ee_extension_response(
sync_config: SlidingSyncConfig,
e2ee_request: SlidingSyncConfig.Extensions.E2eeExtension,
to_token: StreamToken,
from_token: Optional[StreamToken],
from_token: Optional[SlidingSyncStreamToken],
) -> Optional[SlidingSyncResult.Extensions.E2eeExtension]:
"""Handle E2EE device extension (MSC3884)
Expand All @@ -1922,7 +1928,7 @@ async def get_e2ee_extension_response(
# TODO: This should take into account the `from_token` and `to_token`
device_list_updates = await self.device_handler.get_user_ids_changed(
user_id=user_id,
from_token=from_token,
from_token=from_token.stream_token,
)

device_one_time_keys_count: Mapping[str, int] = {}
Expand Down
6 changes: 4 additions & 2 deletions synapse/rest/client/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
from synapse.http.site import SynapseRequest
from synapse.logging.opentracing import trace_with_opname
from synapse.rest.admin.experimental_features import ExperimentalFeature
from synapse.types import JsonDict, Requester, StreamToken
from synapse.types import JsonDict, Requester, SlidingSyncStreamToken, StreamToken
from synapse.types.rest.client import SlidingSyncBody
from synapse.util import json_decoder
from synapse.util.caches.lrucache import LruCache
Expand Down Expand Up @@ -889,7 +889,9 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:

from_token = None
if from_token_string is not None:
from_token = await StreamToken.from_string(self.store, from_token_string)
from_token = await SlidingSyncStreamToken.from_string(
self.store, from_token_string
)

# TODO: We currently don't know whether we're going to use sticky params or
# maybe some filters like sync v2 where they are built up once and referenced
Expand Down
43 changes: 43 additions & 0 deletions synapse/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,49 @@ def __str__(self) -> str:
)


@attr.s(slots=True, frozen=True, auto_attribs=True)
class SlidingSyncStreamToken:
"""The same as a `StreamToken`, but includes an extra field at the start for
the sliding sync connection token (separated by a '/'). This is used to
store per-connection state.
This then looks something like:
5/s2633508_17_338_6732159_1082514_541479_274711_265584_1_379
Attributes:
stream_token: Token representing the position of all the standard
streams.
connection_position: Token used by sliding sync to track updates to any
per-connection state stored by Synapse.
"""

stream_token: StreamToken
connection_position: int

@staticmethod
@cancellable
async def from_string(store: "DataStore", string: str) -> "SlidingSyncStreamToken":
"""Creates a SlidingSyncStreamToken from its textual representation."""
try:
connection_position_str, stream_token_str = string.split("/", 1)
connection_position = int(connection_position_str)
stream_token = await StreamToken.from_string(store, stream_token_str)

return SlidingSyncStreamToken(
stream_token=stream_token,
connection_position=connection_position,
)
except CancelledError:
raise
except Exception:
raise SynapseError(400, "Invalid stream token")

async def to_string(self, store: "DataStore") -> str:
"""Serializes the token to a string"""
stream_token_str = await self.stream_token.to_string(store)
return f"{self.connection_position}/{stream_token_str}"


@attr.s(slots=True, frozen=True, auto_attribs=True)
class PersistedPosition:
"""Position of a newly persisted row with instance that persisted it."""
Expand Down
13 changes: 10 additions & 3 deletions synapse/types/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@
from pydantic import Extra

from synapse.events import EventBase
from synapse.types import DeviceListUpdates, JsonDict, JsonMapping, StreamToken, UserID
from synapse.types import (
DeviceListUpdates,
JsonDict,
JsonMapping,
SlidingSyncStreamToken,
StreamToken,
UserID,
)
from synapse.types.rest.client import SlidingSyncBody

if TYPE_CHECKING:
Expand Down Expand Up @@ -329,7 +336,7 @@ def __bool__(self) -> bool:
def __bool__(self) -> bool:
return bool(self.to_device or self.e2ee)

next_pos: StreamToken
next_pos: SlidingSyncStreamToken
lists: Dict[str, SlidingWindowList]
rooms: Dict[str, RoomResult]
extensions: Extensions
Expand All @@ -342,7 +349,7 @@ def __bool__(self) -> bool:
return bool(self.lists or self.rooms or self.extensions)

@staticmethod
def empty(next_pos: StreamToken) -> "SlidingSyncResult":
def empty(next_pos: SlidingSyncStreamToken) -> "SlidingSyncResult":
"Return a new empty result"
return SlidingSyncResult(
next_pos=next_pos,
Expand Down
Loading

0 comments on commit 8bbc98e

Please sign in to comment.