Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return some room data in Sliding Sync /sync #17320

Merged
merged 75 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
079194c
Return some room timeline data in Sliding Sync
MadLittleMods Jun 17, 2024
3e0f759
Strip invite/knock event itself and avoid mutating event `unsigned`
MadLittleMods Jun 17, 2024
5e2fd4e
Add changelog
MadLittleMods Jun 17, 2024
8ce06f1
Fix sort being lost
MadLittleMods Jun 17, 2024
aa5f54a
Start on required_state
MadLittleMods Jun 18, 2024
5c175d5
Add some notes from pairing
MadLittleMods Jun 18, 2024
9089bfe
Remove required_state for now
MadLittleMods Jun 18, 2024
9427991
Clean up knock_state comments
MadLittleMods Jun 18, 2024
19b2297
Calculate `num_live`
MadLittleMods Jun 18, 2024
81d36f3
Add tests for `limited`
MadLittleMods Jun 18, 2024
9791209
Add more tests
MadLittleMods Jun 18, 2024
70ecd4d
Fix lint
MadLittleMods Jun 19, 2024
71eabe5
Make room name optional
MadLittleMods Jun 19, 2024
39b4f10
Update comments
MadLittleMods Jun 19, 2024
9883b0f
Add bundled aggregations
MadLittleMods Jun 19, 2024
1c06153
Determine limited before filtering
MadLittleMods Jun 19, 2024
57ba033
Merge branch 'develop' into madlittlemods/sliding-sync-room-data
MadLittleMods Jun 19, 2024
c81f300
Add better support for leave/ban
MadLittleMods Jun 19, 2024
d801db0
Fix lints
MadLittleMods Jun 19, 2024
6942b64
Merge branch 'develop' into madlittlemods/sliding-sync-room-data
MadLittleMods Jun 19, 2024
884b448
Update some wording
MadLittleMods Jun 19, 2024
0eb0294
Remove unused `IncludeOldRooms` class
MadLittleMods Jun 20, 2024
b1b4231
Merge branch 'develop' into madlittlemods/sliding-sync-room-data
MadLittleMods Jun 24, 2024
87fac19
Fix lints
MadLittleMods Jun 24, 2024
0e71a2f
Add TODO for filtering call invites in public rooms
MadLittleMods Jun 24, 2024
21ca02c
`newly_joined` vs `limited` already being tracked in a discussion
MadLittleMods Jun 24, 2024
3568311
Fix spelling typo
MadLittleMods Jun 25, 2024
7aea406
Just stripped_state for invite rooms
MadLittleMods Jun 25, 2024
e3e431f
Finish up stripped_state for invite rooms
MadLittleMods Jun 25, 2024
303d834
Add tracking discussion for not optional in the future
MadLittleMods Jun 25, 2024
4c22131
Start testing for the correct room membership (failing)
MadLittleMods Jun 25, 2024
83d6f76
Describe `current_state_delta_stream` better
MadLittleMods Jun 25, 2024
fbd92e1
Add `get_current_state_delta_membership_changes_for_user(...)` (using…
MadLittleMods Jun 26, 2024
6c791a8
WIP: Add back `newly_left`
MadLittleMods Jun 26, 2024
27d74b0
Iterate
MadLittleMods Jun 26, 2024
fb8fbd4
Just fetch full events for `get_current_state_delta_membership_change…
MadLittleMods Jun 26, 2024
d91aa00
Remove extras
MadLittleMods Jun 26, 2024
daa7e36
Add docstring
MadLittleMods Jun 26, 2024
cccbd15
Refactor back to not pulling out full events
MadLittleMods Jun 26, 2024
62c6a4e
Add `newly_joined` support to `get_sync_room_ids_for_user(...)`
MadLittleMods Jun 26, 2024
39259f6
Join both tables with stream_ordering
MadLittleMods Jun 26, 2024
5c21315
Merge branch 'develop' into madlittlemods/sliding-sync-room-data
MadLittleMods Jun 26, 2024
c60aca7
Fix clause change
MadLittleMods Jun 26, 2024
11db1be
Remove debug log
MadLittleMods Jun 26, 2024
7395e10
Fix `builtins.SyntaxError: EOL while scanning string literal (test_sy…
MadLittleMods Jun 26, 2024
2bf3923
Add some tests for `get_current_state_delta_membership_changes_for_us…
MadLittleMods Jun 26, 2024
ec2d8dc
Create events using helper
MadLittleMods Jun 26, 2024
0b9a903
Add test that remotely joins room
MadLittleMods Jun 27, 2024
48d0acf
Actually test `get_current_state_delta_membership_changes_for_user(..…
MadLittleMods Jun 27, 2024
2a944ff
Add state of the db in each situation
MadLittleMods Jun 27, 2024
8df39d1
Remove redundant `instance_name` column
MadLittleMods Jun 27, 2024
b7914e7
Add skipped test for state resets
MadLittleMods Jun 27, 2024
7eb1806
Fix lints
MadLittleMods Jun 27, 2024
935b98c
All `get_current_state_delta_membership_changes_for_user(...)` tests …
MadLittleMods Jun 27, 2024
f163fcf
Remove need for topological_ordering
MadLittleMods Jun 27, 2024
956f20e
(currently failing) Add test to make sure membership changes don't re…
MadLittleMods Jun 27, 2024
830e09d
Grab `prev_membership` to see whether the server left the room (fixes…
MadLittleMods Jun 27, 2024
15fcead
Slight clean-up
MadLittleMods Jun 27, 2024
81c06be
Detect state resets
MadLittleMods Jun 27, 2024
eb159c1
Don't worry about `state_reset` for now
MadLittleMods Jun 27, 2024
ba56350
Passing current tests
MadLittleMods Jun 27, 2024
f774032
Add better comments
MadLittleMods Jun 27, 2024
325856e
Inclusive ranges
MadLittleMods Jun 27, 2024
63c7b50
(doesn't work) Add test for batch persisting multiple member events f…
MadLittleMods Jun 27, 2024
1158058
Opt for tackling more batch scenarios in future PRs
MadLittleMods Jun 27, 2024
32b8b68
Add TODO to handle state resets
MadLittleMods Jun 27, 2024
6045e11
Merge branch 'develop' into madlittlemods/sliding-sync-room-data
MadLittleMods Jul 1, 2024
9e53336
Avoid fetching full events for `prev_event_ids`
MadLittleMods Jul 1, 2024
a4263bf
Update stream tests with prev event info
MadLittleMods Jul 1, 2024
10d78d6
Protect for non-existent prev events
MadLittleMods Jul 2, 2024
0061561
Merge branch 'develop' into madlittlemods/sliding-sync-room-data
MadLittleMods Jul 2, 2024
b8687e7
Select `to_key if to_key else from_key`
MadLittleMods Jul 2, 2024
7c9513c
Add missing test description
MadLittleMods Jul 2, 2024
8b73185
Trigger CI again
MadLittleMods Jul 2, 2024
126ce1e
Merge branch 'develop' into madlittlemods/sliding-sync-room-data
erikjohnston Jul 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/17320.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `rooms` data to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.
MadLittleMods marked this conversation as resolved.
Show resolved Hide resolved
MadLittleMods marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor Author

@MadLittleMods MadLittleMods Jul 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI keeps failing on building debs. It's unclear what's even going wrong or a change to cause this and I'm unable to merge with the failing status. It was building fine on the develop merge.

https://github.com/element-hq/synapse/actions/runs/9754628604/job/26922590117

...

Building wheels for collected packages: cffi, jaeger-client, markupsafe, opentracing, psycopg2, pyicu, systemd-python, threadloop, thrift, zope-interface
  Building wheel for cffi (pyproject.toml): started
  error: subprocess-exited-with-error
  
  × Building wheel for cffi (pyproject.toml) did not run successfully.
  │ exit code: 1
  ╰─> See above for output.
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  Building wheel for cffi (pyproject.toml): finished with status 'error'
  ERROR: Failed building wheel for cffi

....

Failed to build cffi
ERROR: ERROR: Failed to build installable wheels for some pyproject.toml based projects (cffi)
Traceback (most recent call last):
  File "/usr/bin/dh_virtualenv", line 111, in <module>
    sys.exit(main() or 0)
             ^^^^^^
  File "/usr/bin/dh_virtualenv", line 91, in main
    deploy.install_dependencies()
  File "/usr/lib/python3/dist-packages/dh_virtualenv/deployment.py", line 202, in install_dependencies
    subprocess.check_call(self.pip('-r', requirements_path))
  File "/usr/lib/python3.12/subprocess.py", line 413, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/synapse/build/debian/matrix-synapse-py3/opt/venvs/matrix-synapse/bin/python', '/synapse/build/debian/matrix-synapse-py3/opt/venvs/matrix-synapse/bin/pip', 'install', '--log=/tmp/tmp1je1k3qf', '--no-deps', '--no-cache-dir', '--compile', '-r', './exported_requirements.txt']' returned non-zero exit status 1.
make[1]: Leaving directory '/synapse/build'
make[1]: *** [debian/rules:57: override_dh_virtualenv] Error 1
make: *** [debian/rules:66: binary] Error 2
dpkg-buildpackage: error: debian/rules binary subprocess returned exit status 2
build of debian:sid failed: Command '['docker', 'run', '--rm', '--name', 'synapse_build_sid', '--volume=/home/runner/work/synapse/synapse/src:/synapse/source:ro', '--volume=/home/runner/work/synapse/synapse/src/../debs:/debs', '-e', 'TARGET_USERID=1001', '-e', 'TARGET_GROUPID=127', '-e', 'DEB_BUILD_OPTIONS=', 'dh-venv-builder:sid']' returned non-zero exit status 2.
Traceback (most recent call last):
  File "/home/runner/work/synapse/synapse/./src/scripts-dev/build_debian_packages.py", line 225, in <module>
    run_builds(
  File "/home/runner/work/synapse/synapse/./src/scripts-dev/build_debian_packages.py", line 182, in run_builds
    for _ in res:
  File "/opt/hostedtoolcache/Python/3.12.4/x64/lib/python3.12/concurrent/futures/_base.py", line 619, in result_iterator
    yield _result_or_cancel(fs.pop())
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.4/x64/lib/python3.12/concurrent/futures/_base.py", line 317, in _result_or_cancel
    return fut.result(timeout)
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.4/x64/lib/python3.12/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.4/x64/lib/python3.12/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/opt/hostedtoolcache/Python/3.12.4/x64/lib/python3.12/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/synapse/synapse/./src/scripts-dev/build_debian_packages.py", line 179, in <lambda>
    res = e.map(lambda dist: builder.run_build(dist, skip_tests), dists)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/synapse/synapse/./src/scripts-dev/build_debian_packages.py", line 70, in run_build
    self._inner_build(dist, skip_tests)
  File "/home/runner/work/synapse/synapse/./src/scripts-dev/build_debian_packages.py", line 123, in _inner_build
    subprocess.check_call(
  File "/opt/hostedtoolcache/Python/3.12.4/x64/lib/python3.12/subprocess.py", line 413, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['docker', 'run', '--rm', '--name', 'synapse_build_sid', '--volume=/home/runner/work/synapse/synapse/src:/synapse/source:ro', '--volume=/home/runner/work/synapse/synapse/src/../debs:/debs', '-e', 'TARGET_USERID=1001', '-e', 'TARGET_GROUPID=127', '-e', 'DEB_BUILD_OPTIONS=', 'dh-venv-builder:sid']' returned non-zero exit status 2.

If I run scripts-dev/build_debian_packages.py locally, I see a different error (even on develop) which is a bit confusing because I'd assume it should behaving about the same given it's all running in Docker.

Local build output
...
Successfully installed matrix-synapse-1.110.0rc2
/ /synapse/build
Running 0 tests.
E
===============================================================================
[ERROR]
Traceback (most recent call last):
  File "/synapse/build/debian/matrix-synapse-py3/opt/venvs/matrix-synapse/lib/python3.9/site-packages/twisted/trial/runner.py", line 711, in loadByName
    return self.suiteFactory([self.findByName(name, recurse=recurse)])
  File "/synapse/build/debian/matrix-synapse-py3/opt/venvs/matrix-synapse/lib/python3.9/site-packages/twisted/trial/runner.py", line 474, in findByName
    obj = reflect.namedModule(searchName)
  File "/synapse/build/debian/matrix-synapse-py3/opt/venvs/matrix-synapse/lib/python3.9/site-packages/twisted/python/reflect.py", line 156, in namedModule
    topLevel = __import__(name)
  File "/tmp/tmp.DNj5v3i4RS/tests/__init__.py", line 24, in <module>
    from synapse.util.patch_inline_callbacks import do_patch
  File "/synapse/build/debian/matrix-synapse-py3/opt/venvs/matrix-synapse/lib/python3.9/site-packages/synapse/__init__.py", line 32, in <module>
    from synapse.util.rust import check_rust_lib_up_to_date
  File "/synapse/build/debian/matrix-synapse-py3/opt/venvs/matrix-synapse/lib/python3.9/site-packages/synapse/util/rust.py", line 27, in <module>
    from synapse.synapse_rust import get_rust_file_digest
builtins.ImportError: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by /synapse/build/debian/matrix-synapse-py3/opt/venvs/matrix-synapse/lib/python3.9/site-packages/synapse/synapse_rust.abi3.so)

tests
-------------------------------------------------------------------------------
Ran 1 tests in 0.000s

FAILED (errors=1)
make[1]: *** [debian/rules:57: override_dh_virtualenv] Error 1
make[1]: Leaving directory '/synapse/build'
make: *** [debian/rules:66: binary] Error 2
dpkg-buildpackage: error: debian/rules binary subprocess returned exit status 2
build of debian:bullseye failed: Command '['docker', 'run', '--rm', '--name', 'synapse_build_bullseye', '--volume=/home/eric/Documents/github/element/synapse:/synapse/source:ro', '--volume=/home/eric/Documents/github/element/synapse/../debs:/debs', '-e', 'TARGET_USERID=1000', '-e', 'TARGET_GROUPID=1000', '-e', 'DEB_BUILD_OPTIONS=', 'dh-venv-builder:bullseye']' returned non-zero exit status 2.
not building debian:bookworm due to earlier failure
not building debian:sid due to earlier failure
not building ubuntu:focal due to earlier failure
not building ubuntu:jammy due to earlier failure
not building ubuntu:lunar due to earlier failure
not building ubuntu:mantic due to earlier failure
not building debian:trixie due to earlier failure
Traceback (most recent call last):
  File "/home/eric/Documents/github/element/synapse/scripts-dev/build_debian_packages.py", line 225, in <module>
    run_builds(
  File "/home/eric/Documents/github/element/synapse/scripts-dev/build_debian_packages.py", line 182, in run_builds
    for _ in res:
  File "/usr/lib/python3.12/concurrent/futures/_base.py", line 619, in result_iterator
    yield _result_or_cancel(fs.pop())
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/concurrent/futures/_base.py", line 317, in _result_or_cancel
    return fut.result(timeout)
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.12/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/eric/Documents/github/element/synapse/scripts-dev/build_debian_packages.py", line 179, in <lambda>
    res = e.map(lambda dist: builder.run_build(dist, skip_tests), dists)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/eric/Documents/github/element/synapse/scripts-dev/build_debian_packages.py", line 70, in run_build
    self._inner_build(dist, skip_tests)
  File "/home/eric/Documents/github/element/synapse/scripts-dev/build_debian_packages.py", line 123, in _inner_build
    subprocess.check_call(
  File "/usr/lib/python3.12/subprocess.py", line 413, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['docker', 'run', '--rm', '--name', 'synapse_build_bullseye', '--volume=/home/eric/Documents/github/element/synapse:/synapse/source:ro', '--volume=/home/eric/Documents/github/element/synapse/../debs:/debs', '-e', 'TARGET_USERID=1000', '-e', 'TARGET_GROUPID=1000', '-e', 'DEB_BUILD_OPTIONS=', 'dh-venv-builder:bullseye']' returned non-zero exit status 2.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just rerun this on develop and it looks like its broken there too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#17389 was the issue in CI

#17390 was the issue running locally

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing up the deb build issues @erikjohnston!

For the first one, the fact that it was building a wheel at all is the hint as it should be downloading one normally.

For the second one, it makes more sense but if we expect a clean checkout for the Docker builds, it would be nice to remove things according to the .gitignore.

18 changes: 18 additions & 0 deletions synapse/events/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,3 +836,21 @@ def maybe_upsert_event_field(
del container[key]

return upsert_okay


def strip_event(event: EventBase) -> JsonDict:
"""
Used for "stripped state" events which provide a simplified view of the state of a
room intended to help a potential joiner identify the room (relevant when the user
is invited or knocked).

Stripped state events can only have the `sender`, `type`, `state_key` and `content`
properties present.
"""

return {
"type": event.type,
"state_key": event.state_key,
"content": event.content,
"sender": event.sender,
}
234 changes: 223 additions & 11 deletions synapse/handlers/sliding_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,27 @@
#
#
import logging
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple

import attr
from immutabledict import immutabledict

from synapse.api.constants import AccountDataTypes, EventTypes, Membership
from synapse.api.constants import AccountDataTypes, Direction, EventTypes, Membership
from synapse.events import EventBase
from synapse.events.utils import strip_event
from synapse.storage.roommember import RoomsForUser
from synapse.types import (
JsonDict,
PersistedEventPosition,
Requester,
RoomStreamToken,
StreamKeyType,
StreamToken,
UserID,
)
from synapse.types.handlers import OperationType, SlidingSyncConfig, SlidingSyncResult
from synapse.types.state import StateFilter
from synapse.types.state import StateFilter, StateKey
from synapse.visibility import filter_events_for_client

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand Down Expand Up @@ -82,6 +87,24 @@ def filter_membership_for_sync(*, membership: str, user_id: str, sender: str) ->
return membership != Membership.LEAVE or sender != user_id


# We can't freeze this class because we want to update it in place with the
# de-duplicated data.
@attr.s(slots=True, auto_attribs=True)
class RoomSyncConfig:
"""
Holds the config for what data we should fetch for a room in the sync response.

Attributes:
timeline_limit: The maximum number of events to return in the timeline.
required_state: The minimum set of state events requested for the room. The
values are close to `StateKey` but actually use a syntax where you can provide
`*` and `$LAZY` as the state key part of the tuple (type, state_key).
"""

timeline_limit: int
required_state: Set[Tuple[str, str]]


class SlidingSyncHandler:
def __init__(self, hs: "HomeServer"):
self.clock = hs.get_clock()
Expand Down Expand Up @@ -201,6 +224,7 @@ async def current_sync_for_user(

# Assemble sliding window lists
lists: Dict[str, SlidingSyncResult.SlidingWindowList] = {}
relevant_room_map: Dict[str, RoomSyncConfig] = {}
if sync_config.lists:
# Get all of the room IDs that the user should be able to see in the sync
# response
Expand All @@ -225,29 +249,66 @@ async def current_sync_for_user(
ops: List[SlidingSyncResult.SlidingWindowList.Operation] = []
if list_config.ranges:
for range in list_config.ranges:
sliced_room_ids = [
room_id
for room_id, _ in sorted_room_info[range[0] : range[1]]
]

ops.append(
SlidingSyncResult.SlidingWindowList.Operation(
op=OperationType.SYNC,
range=range,
room_ids=[
room_id
for room_id, _ in sorted_room_info[
range[0] : range[1]
]
],
room_ids=sliced_room_ids,
)
)

# Update the relevant room map
for room_id in sliced_room_ids:
if relevant_room_map.get(room_id) is not None:
# Take the highest timeline limit
if (
relevant_room_map[room_id].timeline_limit
< list_config.timeline_limit
):
relevant_room_map[room_id].timeline_limit = (
list_config.timeline_limit
)

# Union the required state
relevant_room_map[room_id].required_state.update(
list_config.required_state
)
else:
relevant_room_map[room_id] = RoomSyncConfig(
timeline_limit=list_config.timeline_limit,
required_state=set(list_config.required_state),
)

lists[list_key] = SlidingSyncResult.SlidingWindowList(
count=len(sorted_room_info),
ops=ops,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handling state resets

It's not clear to me what feedback we should give to the client in a state reset scenario where the user is removed from the room. We can't send a leave event down to them as there is no event to send.

Do we just give a HTTP 400 "connection expired" so clients start over?

Do we send the room down because we consider it newly_left with initial: true and empty required_state? (seems like the answer will be more on this track)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right, but let's just do whatever is easier and add a TODO/issue to follow up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a TODO ⏩

)

# TODO: if (sync_config.room_subscriptions):

# Fetch room data
rooms: Dict[str, SlidingSyncResult.RoomResult] = {}
for room_id, room_sync_config in relevant_room_map.items():
room_sync_result = await self.get_room_sync_data(
user=sync_config.user,
room_id=room_id,
room_sync_config=room_sync_config,
rooms_for_user_membership_at_to_token=sync_room_map[room_id],
from_token=from_token,
to_token=to_token,
)

rooms[room_id] = room_sync_result

return SlidingSyncResult(
next_pos=to_token,
lists=lists,
# TODO: Gather room data for rooms in lists and `sync_config.room_subscriptions`
rooms={},
rooms=rooms,
extensions={},
)

Expand Down Expand Up @@ -665,3 +726,154 @@ async def sort_rooms(
# We want descending order
reverse=True,
)

async def get_room_sync_data(
self,
user: UserID,
room_id: str,
room_sync_config: RoomSyncConfig,
rooms_for_user_membership_at_to_token: RoomsForUser,
from_token: Optional[StreamToken],
to_token: StreamToken,
) -> SlidingSyncResult.RoomResult:
"""
Fetch room data for a room.

We fetch data according to the token range (> `from_token` and <= `to_token`).

Args:
user: User to fetch data for
room_id: The room ID to fetch data for
room_sync_config: Config for what data we should fetch for a room in the
sync response.
rooms_for_user_membership_at_to_token: Membership information for the user
in the room at the time of `to_token`.
from_token: The point in the stream to sync from.
to_token: The point in the stream to sync up to.
"""

timeline_events: List[EventBase] = []
limited = False
# We want to use `to_token` (vs `from_token`) because we look backwards from the
# `to_token` up to the `timeline_limit` and we might not reach `from_token`
# before we hit the limit. We will update the room stream position once we've
# fetched the events.
prev_batch_token = to_token
if room_sync_config.timeline_limit > 0:
timeline_events, new_room_key = await self.store.paginate_room_events(
room_id=room_id,
# We're going to paginate backwards from the `to_token`
from_key=to_token.room_key,
# We should always return historical messages (outside token range) in
# these cases because clients want to be able to show a basic screen of
# information:
# - Initial sync (because no `from_token`)
# - When users newly_join
# - TODO: For incremental sync where we haven't sent it down this
# connection before
to_key=from_token.room_key if from_token is not None else None,
direction=Direction.BACKWARDS,
# We add one so we can determine if there are enough events to saturate
# the limit or not (see `limited`)
limit=room_sync_config.timeline_limit + 1,
event_filter=None,
)

# We want to return the events in ascending order (the last event is the
# most recent).
timeline_events.reverse()

timeline_events = await filter_events_for_client(
self.storage_controllers,
user.to_string(),
timeline_events,
is_peeking=rooms_for_user_membership_at_to_token.membership
!= Membership.JOIN,
filter_send_to_client=True,
)

# Determine our `limited` status
if len(timeline_events) > room_sync_config.timeline_limit:
limited = True
# Get rid of that extra "+ 1" event because we only used it to determine
# if we hit the limit or not
timeline_events = timeline_events[-room_sync_config.timeline_limit :]
assert timeline_events[0].internal_metadata.stream_ordering
new_room_key = RoomStreamToken(
stream=timeline_events[0].internal_metadata.stream_ordering - 1
)

prev_batch_token = prev_batch_token.copy_and_replace(
StreamKeyType.ROOM, new_room_key
)

# Figure out any stripped state events for invite/knocks
stripped_state: List[JsonDict] = []
if rooms_for_user_membership_at_to_token.membership in {
Membership.INVITE,
Membership.KNOCK,
}:
invite_or_knock_event = await self.store.get_event(
rooms_for_user_membership_at_to_token.event_id
)

stripped_state = []
if invite_or_knock_event.membership == Membership.INVITE:
stripped_state.extend(
invite_or_knock_event.unsigned.get("invite_room_state", [])
)
elif invite_or_knock_event.membership == Membership.KNOCK:
stripped_state.extend(
invite_or_knock_event.unsigned.get("knock_room_state", [])
)

stripped_state.append(strip_event(invite_or_knock_event))

required_state = []
if len(room_sync_config.required_state) > 0:
await self.storage_controllers.state.get_current_state(
room_id,
state_filter=StateFilter.from_types(TODO),
await_full_state=False,
)

# TODO: rewind

# required_state = await self.storage_controllers.state.get_state_at(
# room_id,
# to_token,
# state_filter=StateFilter.from_types(TODO),
# )

return SlidingSyncResult.RoomResult(
# TODO: Dummy value
# TODO: Make this optional because a computed name doesn't make sense for translated cases
name="TODO",
# TODO: Dummy value
avatar=None,
# TODO: Dummy value
heroes=None,
# Since we can't determine whether we've already sent a room down this
# Sliding Sync connection before (we plan to add this optimization in the
# future), we're always returning the requested room state instead of
# updates.
initial=True,
# TODO: Dummy value
required_state=[],
timeline=timeline_events,
# TODO: Dummy value
is_dm=False,
stripped_state=stripped_state,
prev_batch=prev_batch_token,
limited=limited,
# TODO: Dummy values
joined_count=0,
invited_count=0,
# TODO: These are just dummy values. We could potentially just remove these
# since notifications can only really be done correctly on the client anyway
# (encrypted rooms).
notification_count=0,
highlight_count=0,
# TODO: Dummy value
num_live=0,
)
Loading
Loading