Skip to content

Commit 9872c3c

Browse files
fix: provide remote servers a way to find out about an event created during the remote join handshake (#71)
### TLDR Use a "dummy" event to tie together forward extremities, and proactively send it to all servers in the room. This allows recently joined servers to become aware of recent events that would otherwise have "slipped through the cracks" and thus not be retrievable. NOTE: While this does send the "dummy" event to all servers in the room, regardless of if they should care or not, at some point a new event will reference this dummy event and require it's retrieval. Since it was proactively sent, this will now not be necessary. This assists in preventing forks in the DAG ### Alternatives Unlike #51 which 'pushes' the missing event directly, this causes the event to be 'pulled' by referencing it as a `prev_event` of a dummy event. Since the 'dummy event' does not get passed into the client, it is effectively invisible. Draw-backs of #51 meant it was not always certain if the 'pushed event' would show up in `/sync` or in `/messages`, but usually was in `/sync`. This method always has the 'missing event' show up in `/messages`, which I feel is more technically correct as that event was(albeit just barely) created before the 'join event' is persisted. ### The Process The order of events: 1. `make_join` from remote server, response sent 2. Message A sent from local server 3. `send_join` from remote server, response from local server. Message A is not in this(as it is not state and is not referenced in any events that are included). Join event is persisted on local server. 4. Local server realizes there are two forward extremities just after persisting the join event. A. Creates a `org.matrix.dummy_event` that has `prev_events` containing both the join and message A. B. Sends this dummy event to all servers in the room. 5. Remote server receives the dummy event via it's `/send` endpoint, saves it in a queue until the partial state join begins syncing additional room state
2 parents d9feade + f1bd70e commit 9872c3c

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

synapse/federation/federation_server.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,10 @@ async def on_send_join_request(
766766
event, context = await self._on_send_membership_event(
767767
origin, content, Membership.JOIN, room_id
768768
)
769+
# Collect this now, the internal metadata of event(which should have it) doesn't
770+
stream_ordering_of_join = (
771+
await self.store.get_current_room_stream_token_for_room_id(room_id)
772+
)
769773

770774
prev_state_ids = await context.get_prev_state_ids()
771775

@@ -806,6 +810,27 @@ async def on_send_join_request(
806810
"members_omitted": send_partial_state_response,
807811
}
808812

813+
# Check the forward extremities for the room here. If there is more than one, it
814+
# is likely that another event was created in the room during the
815+
# make_join/send_join handshake. Without being able to determine how long until
816+
# the next event will be created that references this 'missing event',
817+
# proactively send a dummy extensible event that ties these forward extremities
818+
# together. The remote server should search out this missing event on its own.
819+
#
820+
# By not sending the 'missing event' directly, the stream ordering for it will
821+
# be consistent between servers(in that it technically was created before the
822+
# join itself).
823+
824+
forward_extremities = await self.store._get_forward_extremeties_for_room(
825+
room_id, stream_ordering_of_join.get_max_stream_pos()
826+
)
827+
828+
if len(forward_extremities) > 1:
829+
# I do not feel it is necessary to set this onto the FederationServer class
830+
# itself. Its likelihood of being used is extremely low. Make it on-demand
831+
_creation_handler = self.hs.get_event_creation_handler()
832+
await _creation_handler._send_dummy_events_to_patch_room(room_id)
833+
809834
if servers_in_room is not None:
810835
resp["servers_in_room"] = list(servers_in_room)
811836

synapse/handlers/message.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,7 +2210,36 @@ async def _send_dummy_events_to_fill_extremities(self) -> None:
22102210
now = self.clock.time_msec()
22112211
self._rooms_to_exclude_from_dummy_event_insertion[room_id] = now
22122212

2213-
async def _send_dummy_event_for_room(self, room_id: str) -> bool:
2213+
async def _send_dummy_events_to_patch_room(self, room_id: str) -> None:
2214+
"""
2215+
Send a dummy event into this room to patch in a missed forward extremity.
2216+
This should only be triggered during a remote join if there was a forward
2217+
extremity that occurred during the make_join/send_join handshake.
2218+
"""
2219+
async with self._worker_lock_handler.acquire_read_write_lock(
2220+
NEW_EVENT_DURING_PURGE_LOCK_NAME, room_id, write=False
2221+
):
2222+
dummy_event_sent = await self._send_dummy_event_for_room(
2223+
room_id, proactively_send=True
2224+
)
2225+
2226+
if not dummy_event_sent:
2227+
# Did not find a valid user in the room, so remove from future attempts
2228+
# Exclusion is time limited, so the room will be rechecked in the future
2229+
# dependent on _DUMMY_EVENT_ROOM_EXCLUSION_EXPIRY
2230+
logger.info(
2231+
"Failed to send dummy event into room %s. Will exclude it from "
2232+
"future attempts until cache expires",
2233+
room_id,
2234+
)
2235+
# This mapping is room_id -> time of last attempt(in ms)
2236+
self._rooms_to_exclude_from_dummy_event_insertion[room_id] = (
2237+
self.clock.time_msec()
2238+
)
2239+
2240+
async def _send_dummy_event_for_room(
2241+
self, room_id: str, proactively_send: bool = False
2242+
) -> bool:
22142243
"""Attempt to send a dummy event for the given room.
22152244
22162245
Args:
@@ -2242,8 +2271,7 @@ async def _send_dummy_event_for_room(self, room_id: str) -> bool:
22422271
},
22432272
)
22442273
context = await unpersisted_context.persist(event)
2245-
2246-
event.internal_metadata.proactively_send = False
2274+
event.internal_metadata.proactively_send = proactively_send
22472275

22482276
# Since this is a dummy-event it is OK if it is sent by a
22492277
# shadow-banned user.

0 commit comments

Comments
 (0)