-
Notifications
You must be signed in to change notification settings - Fork 597
Replace wait_for_quarantined_media_stream_id(...) with standard wait_for_stream_token(...)
#19764
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
Changes from 3 commits
64a1d42
7a4a656
cf053ca
904573d
d2fa8ed
e88ab52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Replace unique `quarantined_media` waiting patterns for with standard `wait_for_stream_token(...)`. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,7 +43,13 @@ | |
| from synapse.storage.databases.main.media_repository import ( | ||
| MediaSortOrder, | ||
| ) | ||
| from synapse.types import JsonDict, UserID | ||
| from synapse.types import ( | ||
| JsonDict, | ||
| MultiWriterStreamToken, | ||
| StreamKeyType, | ||
| StreamToken, | ||
| UserID, | ||
| ) | ||
|
|
||
| if TYPE_CHECKING: | ||
| from synapse.server import HomeServer | ||
|
|
@@ -243,6 +249,7 @@ def __init__(self, hs: "HomeServer"): | |
| self.auth = hs.get_auth() | ||
| self.server_name = hs.hostname | ||
| self.replication = hs.get_replication_data_handler() | ||
| self.notifier = hs.get_notifier() | ||
|
|
||
| async def on_GET(self, request: SynapseRequest) -> tuple[int, JsonDict]: | ||
| await assert_requester_is_admin(self.auth, request) | ||
|
|
@@ -256,8 +263,7 @@ async def on_GET(self, request: SynapseRequest) -> tuple[int, JsonDict]: | |
| # The caller is trying to get future data, which we don't allow because | ||
| # we know it's an invalid state that should never happen. We could | ||
| # wait until we reach the token but we might as well not waste our | ||
| # resources on that which is why `wait_for_quarantined_media_stream_id(...)` | ||
| # has assertions around this. | ||
|
Comment on lines
-259
to
-260
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed the last part of this comment as |
||
| # resources on that. | ||
| raise SynapseError( | ||
| HTTPStatus.BAD_REQUEST, | ||
| "The `from` token is considered invalid because it includes stream positions " | ||
|
|
@@ -268,9 +274,16 @@ async def on_GET(self, request: SynapseRequest) -> tuple[int, JsonDict]: | |
| errcode=Codes.INVALID_PARAM, | ||
| ) | ||
|
|
||
| # Create a `StreamToken` that's compatible with `wait_for_stream_token`. | ||
| # | ||
| # FIXME: Ideally, this endpoint would use a `StreamToken` to begin with | ||
| from_token = StreamToken.START.copy_and_replace( | ||
| StreamKeyType.QUARANTINED_MEDIA, MultiWriterStreamToken(stream=from_id) | ||
| ) | ||
|
|
||
| # We need to wait to ensure that our current worker is actually caught up with | ||
| # the stream position, otherwise we might not return what we think we're returning. | ||
| if not await self.store.wait_for_quarantined_media_stream_id(from_id): | ||
| if not await self.notifier.wait_for_stream_token(from_token): | ||
| raise SynapseError( | ||
| HTTPStatus.INTERNAL_SERVER_ERROR, | ||
| "Timed out while waiting for the worker serving this request to catch up to the given " | ||
|
|
@@ -280,7 +293,7 @@ async def on_GET(self, request: SynapseRequest) -> tuple[int, JsonDict]: | |
| errcode=Codes.UNKNOWN, | ||
| ) | ||
|
|
||
| to_id = await self.store.get_current_quarantined_media_stream_id() | ||
| to_id = self.store.get_current_quarantined_media_stream_id() | ||
| changes = await self.store.get_quarantined_media_changes( | ||
| from_id=from_id, | ||
| to_id=to_id, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,12 +62,12 @@ | |
| from synapse.storage.util.id_generators import IdGenerator, MultiWriterIdGenerator | ||
| from synapse.types import ( | ||
| JsonDict, | ||
| MultiWriterStreamToken, | ||
| RetentionPolicy, | ||
| StrCollection, | ||
| ThirdPartyInstanceID, | ||
| ) | ||
| from synapse.util.caches.descriptors import cached, cachedList | ||
| from synapse.util.duration import Duration | ||
| from synapse.util.json import json_encoder | ||
| from synapse.util.stringutils import MXC_REGEX | ||
|
|
||
|
|
@@ -1302,7 +1302,15 @@ def _get_media_ids_by_user_txn( | |
|
|
||
| return local_media_ids | ||
|
|
||
| async def get_current_quarantined_media_stream_id(self) -> int: | ||
| def get_quarantined_media_stream_token(self) -> MultiWriterStreamToken: | ||
| return MultiWriterStreamToken.from_generator( | ||
| self._quarantined_media_changes_id_gen | ||
| ) | ||
|
|
||
| def get_quarantined_media_stream_id_generator(self) -> MultiWriterIdGenerator: | ||
| return self._quarantined_media_changes_id_gen | ||
|
|
||
| def get_current_quarantined_media_stream_id(self) -> int: | ||
| """Gets the position of the quarantined media changes stream. | ||
|
|
||
| Returns: | ||
|
|
@@ -1318,74 +1326,6 @@ async def get_max_allocated_quarantined_media_stream_id(self) -> int: | |
| """ | ||
| return await self._quarantined_media_changes_id_gen.get_max_allocated_token() | ||
|
|
||
| async def wait_for_quarantined_media_stream_id(self, target_id: int) -> bool: | ||
| """Waits until the quarantined media changes stream reaches the given stream ID. | ||
|
|
||
| See https://github.com/element-hq/synapse/pull/19644 for more details. | ||
|
|
||
| TODO: Replace function and call sites with https://github.com/element-hq/synapse/pull/19644 | ||
|
Comment on lines
-1324
to
-1326
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing |
||
|
|
||
| Args: | ||
| target_id: The stream ID to wait for. | ||
|
|
||
| Returns: | ||
| True when caught up to the target stream ID. | ||
| False when timing out while waiting. | ||
| """ | ||
| # We ideally would use something like `wait_for_stream_position` in the meantime, | ||
| # but that short circuits if the instance name matches the current instance name. | ||
| # Doing so means that if *another* writer is actually leading the to_id, then we'll | ||
| # assume that we're caught up when we aren't. | ||
| # | ||
| # NOTE: Because this is implemented to wait for stream positions by integer ID, | ||
| # we're technically waiting for *all* workers to catch up rather than just waiting | ||
| # for *our* worker to catch up. This is okay for now because the quarantined media | ||
| # stream should be pretty fast to update, and if it's not then the only thing we're | ||
| # affecting is an admin API that probably has a tool automatically retrying requests | ||
| # anyway. https://github.com/element-hq/synapse/pull/19644 does the waiting properly | ||
| # so this should be replaced by that (or similar). | ||
|
|
||
| # Get the minimum shared position/ID across all workers | ||
| current_id = self._quarantined_media_changes_id_gen.get_current_token() | ||
| if current_id >= target_id: | ||
| return True # nothing to wait for: we're already caught up. | ||
|
|
||
| # "This should never happen". Tokens we hand out via the API should exist. If they | ||
| # don't, then we're in a bad state and need to explode. | ||
| max_persisted_position = ( | ||
| await self._quarantined_media_changes_id_gen.get_max_allocated_token() | ||
| ) | ||
| assert max_persisted_position >= target_id, ( | ||
| f"Unable to wait for invalid future token (token={target_id} has positions " | ||
| f"ahead of our max persisted position={max_persisted_position})" | ||
| ) | ||
|
|
||
| # Start waiting until we've caught up to the `stream_token` | ||
| start = self.clock.time_msec() | ||
| logged = False | ||
| while True: | ||
| # Like above, get the minimum shared ID across all workers | ||
| current_id = self._quarantined_media_changes_id_gen.get_current_token() | ||
| if current_id >= target_id: | ||
| return True | ||
|
|
||
| now = self.clock.time_msec() | ||
|
|
||
| # Timed out | ||
| if now - start > 10_000: | ||
| return False | ||
|
|
||
| if not logged: | ||
| logger.info( | ||
| "Waiting for current token to reach %s; currently at %s", | ||
| target_id, | ||
| current_id, | ||
| ) | ||
| logged = True | ||
|
|
||
| # TODO: be better | ||
| await self.clock.sleep(Duration(milliseconds=500)) | ||
|
|
||
| async def get_quarantined_media_changes( | ||
| self, *, from_id: int, to_id: int, limit: int | ||
| ) -> list[QuarantinedMediaUpdate]: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -85,6 +85,7 @@ def get_current_token(self) -> StreamToken: | |
| ) | ||
| thread_subscriptions_key = self.store.get_max_thread_subscriptions_stream_id() | ||
| sticky_events_key = self.store.get_max_sticky_events_stream_id() | ||
| quarantined_media_key = self.store.get_quarantined_media_stream_token() | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The rest of this is just boilerplate to add |
||
|
|
||
| token = StreamToken( | ||
| room_key=self.sources.room.get_current_key(), | ||
|
|
@@ -100,6 +101,7 @@ def get_current_token(self) -> StreamToken: | |
| un_partial_stated_rooms_key=un_partial_stated_rooms_key, | ||
| thread_subscriptions_key=thread_subscriptions_key, | ||
| sticky_events_key=sticky_events_key, | ||
| quarantined_media_key=quarantined_media_key, | ||
| ) | ||
| return token | ||
|
|
||
|
|
@@ -128,6 +130,7 @@ async def bound_future_token(self, token: StreamToken) -> StreamToken: | |
| StreamKeyType.UN_PARTIAL_STATED_ROOMS: self.store.get_un_partial_stated_rooms_id_generator(), | ||
| StreamKeyType.THREAD_SUBSCRIPTIONS: self.store.get_thread_subscriptions_stream_id_generator(), | ||
| StreamKeyType.STICKY_EVENTS: self.store.get_sticky_events_stream_id_generator(), | ||
| StreamKeyType.QUARANTINED_MEDIA: self.store.get_quarantined_media_stream_id_generator(), | ||
| } | ||
|
|
||
| for _, key in StreamKeyType.__members__.items(): | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.