-
Notifications
You must be signed in to change notification settings - Fork 413
Fix performance regression related to delayed events processing #18926
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
Merged
Merged
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
fadb7eb
Add a storage function to pull out senders for event _ids
anoadragon453 e3ad18c
Stop calling `get_event` and use new storage function
anoadragon453 9f1c7e3
Only schedule next delayed event once
anoadragon453 76b885b
Bail out early if there are no delayed events to send
anoadragon453 a0faac9
newsfile
anoadragon453 8d97548
Note 100 row limit in docstrings
anoadragon453 d1974a2
Note that order of fetching stream orderings is important
anoadragon453 4ff8184
link MSC4140 in newsfile
anoadragon453 68fb552
More error handling
anoadragon453 1eb25f1
Revert "Only schedule next delayed event once"
anoadragon453 ef38b2e
Prevent potential infinite loop if we bypass the max
anoadragon453 a67b036
logger.error for missing sender
anoadragon453 b4877a6
state reset TODO
anoadragon453 6eaf31e
Add a test for `get_senders_for_event_ids`
anoadragon453 3d44c99
Handle the case where a room has been purged
anoadragon453 af7962d
Update logging and TODO to discuss state resets
anoadragon453 17d2845
Move `Sentinel` to util module
anoadragon453 c732bba
Replace ad-hoc Sentinel usage with utility module
anoadragon453 8c427f2
Update logs and comments to better cover cases
anoadragon453 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Fix a performance regression related to the experimental Delayed Events ([MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140)) feature. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,7 @@ | |
| from twisted.internet.interfaces import IDelayedCall | ||
|
|
||
| from synapse.api.constants import EventTypes | ||
| from synapse.api.errors import ShadowBanError | ||
| from synapse.api.errors import ShadowBanError, SynapseError | ||
| from synapse.api.ratelimiting import Ratelimiter | ||
| from synapse.config.workers import MAIN_PROCESS_INSTANCE_NAME | ||
| from synapse.logging.opentracing import set_tag | ||
|
|
@@ -146,10 +146,37 @@ async def process() -> None: | |
| ) | ||
|
|
||
| async def _unsafe_process_new_event(self) -> None: | ||
| # We purposefully fetch the current max room stream ordering before | ||
| # doing anything else, as it could increment duing processing of state | ||
| # deltas. We want to avoid updating `delayed_events_stream_pos` past | ||
| # the stream ordering of the state deltas we've processed. Otherwise | ||
| # we'll leave gaps in our processing. | ||
| room_max_stream_ordering = self._store.get_room_max_stream_ordering() | ||
|
|
||
| # Check that there are actually any delayed events to process. If not, bail early. | ||
| delayed_events_count = await self._store.get_count_of_delayed_events() | ||
| if delayed_events_count == 0: | ||
| # There are no delayed events to process. Update the | ||
| # `delayed_events_stream_pos` to the latest `events` stream pos and | ||
| # exit early. | ||
| self._event_pos = room_max_stream_ordering | ||
|
|
||
| logger.debug( | ||
| "No delayed events to process. Updating `delayed_events_stream_pos` to max stream ordering (%s)", | ||
| room_max_stream_ordering, | ||
| ) | ||
|
|
||
| await self._store.update_delayed_events_stream_pos(room_max_stream_ordering) | ||
|
|
||
| event_processing_positions.labels( | ||
| name="delayed_events", **{SERVER_NAME_LABEL: self.server_name} | ||
| ).set(room_max_stream_ordering) | ||
|
|
||
| return | ||
|
|
||
| # If self._event_pos is None then means we haven't fetched it from the DB yet | ||
| if self._event_pos is None: | ||
| self._event_pos = await self._store.get_delayed_events_stream_pos() | ||
anoadragon453 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| room_max_stream_ordering = self._store.get_room_max_stream_ordering() | ||
| if self._event_pos > room_max_stream_ordering: | ||
| # apparently, we've processed more events than exist in the database! | ||
| # this can happen if events are removed with history purge or similar. | ||
|
|
@@ -167,7 +194,7 @@ async def _unsafe_process_new_event(self) -> None: | |
| self._clock, name="delayed_events_delta", server_name=self.server_name | ||
| ): | ||
| room_max_stream_ordering = self._store.get_room_max_stream_ordering() | ||
| if self._event_pos == room_max_stream_ordering: | ||
| if self._event_pos >= room_max_stream_ordering: | ||
| return | ||
|
|
||
| logger.debug( | ||
|
|
@@ -202,6 +229,16 @@ async def _handle_state_deltas(self, deltas: List[StateDelta]) -> None: | |
| Process current state deltas to cancel other users' pending delayed events | ||
| that target the same state. | ||
| """ | ||
| # TODO: How to handle state deltas that are the result of a state reset? | ||
|
|
||
| # Get the senders of each delta's state event (as sender information is | ||
| # not currently stored in the `current_state_deltas` table). | ||
| event_id_and_sender_dict = await self._store.get_senders_for_event_ids( | ||
| [delta.event_id for delta in deltas if delta.event_id is not None] | ||
| ) | ||
|
|
||
| # Note: No need to batch as `get_current_state_deltas` will only ever | ||
| # return 100 rows at a time. | ||
anoadragon453 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| for delta in deltas: | ||
| if delta.event_id is None: | ||
| logger.debug( | ||
|
|
@@ -215,10 +252,34 @@ async def _handle_state_deltas(self, deltas: List[StateDelta]) -> None: | |
| "Handling: %r %r, %s", delta.event_type, delta.state_key, delta.event_id | ||
| ) | ||
|
|
||
| event = await self._store.get_event(delta.event_id, allow_none=True) | ||
| if not event: | ||
| sentinel = object() | ||
| sender_str = event_id_and_sender_dict.get(delta.event_id, sentinel) | ||
| if sender_str is None: | ||
| logger.error( | ||
| "Skipping state delta with event ID '%s' as 'sender' was unknown. This is unexpected - please report it as a bug!", | ||
| delta.event_id, | ||
| ) | ||
| continue | ||
| if sender_str is sentinel: | ||
| # This can happen if a room is purged. State deltas related to | ||
| # the room are left behind, but the event/room no longer exist. | ||
| logger.warning( | ||
| "Skipping state delta with event ID '%s' as it is an unknown event - the room may have been purged", | ||
| delta.event_id, | ||
| ) | ||
| continue | ||
|
||
|
|
||
| try: | ||
| # Ignore the type error: if `sender_str` is an object, then it | ||
| # will have been caught by the `if` conditional just above. | ||
| sender = UserID.from_string(sender_str) # type: ignore[arg-type] | ||
anoadragon453 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| except SynapseError as e: | ||
| logger.error( | ||
| "Skipping state delta with Matrix User ID '%s' that failed to parse: %s", | ||
| sender_str, | ||
| e, | ||
| ) | ||
| continue | ||
| sender = UserID.from_string(event.sender) | ||
|
|
||
| next_send_ts = await self._store.cancel_delayed_state_events( | ||
| room_id=delta.room_id, | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.