Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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/19390.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Provide remote servers a way to find out about an event created during the remote join handshake. Contributed by @FrenchGithubUser and @jason-famedly @ Famedly.
26 changes: 26 additions & 0 deletions synapse/federation/federation_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,10 @@ async def on_send_join_request(
event, context = await self._on_send_membership_event(
origin, content, Membership.JOIN, room_id
)
# Collect this now, the internal metadata of event(which should have it) doesn't
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This comment is quite difficult to read.

And why not query this close to where it's used, below?

stream_ordering_of_join = (
await self.store.get_current_room_stream_token_for_room_id(room_id)
Comment on lines +761 to +763
)

prev_state_ids = await context.get_prev_state_ids()

Expand Down Expand Up @@ -798,6 +802,28 @@ async def on_send_join_request(
"members_omitted": caller_supports_partial_state,
}

# Check the forward extremities for the room here. If there is more than one, it
# is likely that another event was created in the room during the
# make_join/send_join handshake. The joining server is likely to thus miss this event
# until a second event is created when references it - which could be some time.
# In that case, we proactively send a dummy extensible event that ties these
# forward extremities together. The remote server will then attempt to backfill
# the missing event on its own.
#
# By not sending the 'missing event' directly, but instead having the joining
# homeserver backfill it, the stream ordering for the missing event will be
# "before" the join (which is what we expect).

forward_extremities = await self.store._get_forward_extremeties_for_room(
room_id, stream_ordering_of_join.get_max_stream_pos()
)

if len(forward_extremities) > 1:
# The likelihood of this being used is extremely low, thus only build the handler
# when necessary.
_creation_handler = self.hs.get_event_creation_handler()
await _creation_handler._send_dummy_event_after_room_join(room_id)
Comment on lines +817 to +825

Comment on lines +821 to +826
Comment on lines +805 to +826
if servers_in_room is not None:
resp["servers_in_room"] = list(servers_in_room)

Expand Down
37 changes: 34 additions & 3 deletions synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -2220,7 +2220,39 @@ async def _send_dummy_events_to_fill_extremities(self) -> None:
now = self.clock.time_msec()
self._rooms_to_exclude_from_dummy_event_insertion[room_id] = now

async def _send_dummy_event_for_room(self, room_id: str) -> bool:
async def _send_dummy_event_after_room_join(self, room_id: str) -> None:
"""
Creates and sends a dummy event into the given room, referencing the
current forward extremities (via `prev_events`).
This should only be triggered when handling a remote join while there was
events sent during the make_join/send_join handshake. The joining
homeserver would otherwise not immediately know to backfill this event,
and would "miss it".
Comment on lines +2227 to +2230
"""
async with self._worker_lock_handler.acquire_read_write_lock(
NEW_EVENT_DURING_PURGE_LOCK_NAME, room_id, write=False
):
dummy_event_sent = await self._send_dummy_event_for_room(
room_id, proactively_send=True
)

if not dummy_event_sent:
# Did not find a valid user in the room, so remove from future attempts
# Exclusion is time limited, so the room will be rechecked in the future
# dependent on _DUMMY_EVENT_ROOM_EXCLUSION_EXPIRY
logger.info(
"Failed to send dummy event into room %s. Will exclude it from "
"future attempts until cache expires",
room_id,
)
# This mapping is room_id -> time of last attempt(in ms)
self._rooms_to_exclude_from_dummy_event_insertion[room_id] = (
self.clock.time_msec()
)
Comment on lines +2239 to +2251
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it worth waiting an trying again if the room will move on in the meantime, and new events will likely be sent?


async def _send_dummy_event_for_room(
self, room_id: str, proactively_send: bool = False
) -> bool:
"""Attempt to send a dummy event for the given room.

Args:
Expand Down Expand Up @@ -2252,8 +2284,7 @@ async def _send_dummy_event_for_room(self, room_id: str) -> bool:
},
)
context = await unpersisted_context.persist(event)

event.internal_metadata.proactively_send = False
event.internal_metadata.proactively_send = proactively_send

# Since this is a dummy-event it is OK if it is sent by a
# shadow-banned user.
Expand Down
Loading