Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit fb60cb1

Browse files
authored
Faster remote room joins: stream the un-partial-stating of events over replication. [rei:frrj/streams/unpsr] (#14545)
1 parent 24a97b3 commit fb60cb1

File tree

8 files changed

+204
-10
lines changed

8 files changed

+204
-10
lines changed

changelog.d/14545.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Faster remote room joins: stream the un-partial-stating of events over replication.

synapse/handlers/federation_event.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,8 @@ async def update_state_for_partial_state_event(
610610
self._state_storage_controller.notify_event_un_partial_stated(
611611
event.event_id
612612
)
613+
# Notify that there's a new row in the un_partial_stated_events stream.
614+
self._notifier.notify_replication()
613615

614616
@trace
615617
async def backfill(

synapse/replication/tcp/streams/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@
4242
)
4343
from synapse.replication.tcp.streams.events import EventsStream
4444
from synapse.replication.tcp.streams.federation import FederationStream
45-
from synapse.replication.tcp.streams.partial_state import UnPartialStatedRoomStream
45+
from synapse.replication.tcp.streams.partial_state import (
46+
UnPartialStatedEventStream,
47+
UnPartialStatedRoomStream,
48+
)
4649

4750
STREAMS_MAP = {
4851
stream.NAME: stream
@@ -63,6 +66,7 @@
6366
AccountDataStream,
6467
UserSignatureStream,
6568
UnPartialStatedRoomStream,
69+
UnPartialStatedEventStream,
6670
)
6771
}
6872

@@ -83,4 +87,5 @@
8387
"AccountDataStream",
8488
"UserSignatureStream",
8589
"UnPartialStatedRoomStream",
90+
"UnPartialStatedEventStream",
8691
]

synapse/replication/tcp/streams/partial_state.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,31 @@ def __init__(self, hs: "HomeServer"):
4646
current_token_without_instance(store.get_un_partial_stated_rooms_token),
4747
store.get_un_partial_stated_rooms_from_stream,
4848
)
49+
50+
51+
@attr.s(slots=True, frozen=True, auto_attribs=True)
52+
class UnPartialStatedEventStreamRow:
53+
# ID of the event that has been un-partial-stated.
54+
event_id: str
55+
56+
# True iff the rejection status of the event changed as a result of being
57+
# un-partial-stated.
58+
rejection_status_changed: bool
59+
60+
61+
class UnPartialStatedEventStream(Stream):
62+
"""
63+
Stream to notify about events becoming un-partial-stated.
64+
"""
65+
66+
NAME = "un_partial_stated_event"
67+
ROW_TYPE = UnPartialStatedEventStreamRow
68+
69+
def __init__(self, hs: "HomeServer"):
70+
store = hs.get_datastores().main
71+
super().__init__(
72+
hs.get_instance_name(),
73+
# TODO(faster_joins, multiple writers): we need to account for instance names
74+
current_token_without_instance(store.get_un_partial_stated_events_token),
75+
store.get_un_partial_stated_events_from_stream,
76+
)

synapse/storage/databases/main/events_worker.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
from synapse.storage.engines import PostgresEngine
7171
from synapse.storage.types import Cursor
7272
from synapse.storage.util.id_generators import (
73+
AbstractStreamIdGenerator,
7374
AbstractStreamIdTracker,
7475
MultiWriterIdGenerator,
7576
StreamIdGenerator,
@@ -292,6 +293,93 @@ def get_chain_id_txn(txn: Cursor) -> int:
292293
id_column="chain_id",
293294
)
294295

296+
self._un_partial_stated_events_stream_id_gen: AbstractStreamIdGenerator
297+
298+
if isinstance(database.engine, PostgresEngine):
299+
self._un_partial_stated_events_stream_id_gen = MultiWriterIdGenerator(
300+
db_conn=db_conn,
301+
db=database,
302+
stream_name="un_partial_stated_event_stream",
303+
instance_name=hs.get_instance_name(),
304+
tables=[
305+
("un_partial_stated_event_stream", "instance_name", "stream_id")
306+
],
307+
sequence_name="un_partial_stated_event_stream_sequence",
308+
# TODO(faster_joins, multiple writers) Support multiple writers.
309+
writers=["master"],
310+
)
311+
else:
312+
self._un_partial_stated_events_stream_id_gen = StreamIdGenerator(
313+
db_conn, "un_partial_stated_event_stream", "stream_id"
314+
)
315+
316+
def get_un_partial_stated_events_token(self) -> int:
317+
# TODO(faster_joins, multiple writers): This is inappropriate if there are multiple
318+
# writers because workers that don't write often will hold all
319+
# readers up.
320+
return self._un_partial_stated_events_stream_id_gen.get_current_token()
321+
322+
async def get_un_partial_stated_events_from_stream(
323+
self, instance_name: str, last_id: int, current_id: int, limit: int
324+
) -> Tuple[List[Tuple[int, Tuple[str, bool]]], int, bool]:
325+
"""Get updates for the un-partial-stated events replication stream.
326+
327+
Args:
328+
instance_name: The writer we want to fetch updates from. Unused
329+
here since there is only ever one writer.
330+
last_id: The token to fetch updates from. Exclusive.
331+
current_id: The token to fetch updates up to. Inclusive.
332+
limit: The requested limit for the number of rows to return. The
333+
function may return more or fewer rows.
334+
335+
Returns:
336+
A tuple consisting of: the updates, a token to use to fetch
337+
subsequent updates, and whether we returned fewer rows than exists
338+
between the requested tokens due to the limit.
339+
340+
The token returned can be used in a subsequent call to this
341+
function to get further updatees.
342+
343+
The updates are a list of 2-tuples of stream ID and the row data
344+
"""
345+
346+
if last_id == current_id:
347+
return [], current_id, False
348+
349+
def get_un_partial_stated_events_from_stream_txn(
350+
txn: LoggingTransaction,
351+
) -> Tuple[List[Tuple[int, Tuple[str, bool]]], int, bool]:
352+
sql = """
353+
SELECT stream_id, event_id, rejection_status_changed
354+
FROM un_partial_stated_event_stream
355+
WHERE ? < stream_id AND stream_id <= ? AND instance_name = ?
356+
ORDER BY stream_id ASC
357+
LIMIT ?
358+
"""
359+
txn.execute(sql, (last_id, current_id, instance_name, limit))
360+
updates = [
361+
(
362+
row[0],
363+
(
364+
row[1],
365+
bool(row[2]),
366+
),
367+
)
368+
for row in txn
369+
]
370+
limited = False
371+
upto_token = current_id
372+
if len(updates) >= limit:
373+
upto_token = updates[-1][0]
374+
limited = True
375+
376+
return updates, upto_token, limited
377+
378+
return await self.db_pool.runInteraction(
379+
"get_un_partial_stated_events_from_stream",
380+
get_un_partial_stated_events_from_stream_txn,
381+
)
382+
295383
def process_replication_rows(
296384
self,
297385
stream_name: str,

synapse/storage/databases/main/state.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def __init__(
8080
hs: "HomeServer",
8181
):
8282
super().__init__(database, db_conn, hs)
83+
self._instance_name: str = hs.get_instance_name()
8384

8485
async def get_room_version(self, room_id: str) -> RoomVersion:
8586
"""Get the room_version of a given room
@@ -404,18 +405,21 @@ async def update_state_for_partial_state_event(
404405
context: EventContext,
405406
) -> None:
406407
"""Update the state group for a partial state event"""
407-
await self.db_pool.runInteraction(
408-
"update_state_for_partial_state_event",
409-
self._update_state_for_partial_state_event_txn,
410-
event,
411-
context,
412-
)
408+
async with self._un_partial_stated_events_stream_id_gen.get_next() as un_partial_state_event_stream_id:
409+
await self.db_pool.runInteraction(
410+
"update_state_for_partial_state_event",
411+
self._update_state_for_partial_state_event_txn,
412+
event,
413+
context,
414+
un_partial_state_event_stream_id,
415+
)
413416

414417
def _update_state_for_partial_state_event_txn(
415418
self,
416419
txn: LoggingTransaction,
417420
event: EventBase,
418421
context: EventContext,
422+
un_partial_state_event_stream_id: int,
419423
) -> None:
420424
# we shouldn't have any outliers here
421425
assert not event.internal_metadata.is_outlier()
@@ -436,7 +440,10 @@ def _update_state_for_partial_state_event_txn(
436440

437441
# the event may now be rejected where it was not before, or vice versa,
438442
# in which case we need to update the rejected flags.
439-
if bool(context.rejected) != (event.rejected_reason is not None):
443+
rejection_status_changed = bool(context.rejected) != (
444+
event.rejected_reason is not None
445+
)
446+
if rejection_status_changed:
440447
self.mark_event_rejected_txn(txn, event.event_id, context.rejected)
441448

442449
self.db_pool.simple_delete_one_txn(
@@ -445,15 +452,24 @@ def _update_state_for_partial_state_event_txn(
445452
keyvalues={"event_id": event.event_id},
446453
)
447454

448-
# TODO(faster_joins): need to do something about workers here
449-
# https://github.com/matrix-org/synapse/issues/12994
450455
txn.call_after(self.is_partial_state_event.invalidate, (event.event_id,))
451456
txn.call_after(
452457
self._get_state_group_for_event.prefill,
453458
(event.event_id,),
454459
state_group,
455460
)
456461

462+
self.db_pool.simple_insert_txn(
463+
txn,
464+
"un_partial_stated_event_stream",
465+
{
466+
"stream_id": un_partial_state_event_stream_id,
467+
"instance_name": self._instance_name,
468+
"event_id": event.event_id,
469+
"rejection_status_changed": rejection_status_changed,
470+
},
471+
)
472+
457473

458474
class MainStateBackgroundUpdateStore(RoomMemberWorkerStore):
459475

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* Copyright 2022 The Matrix.org Foundation C.I.C
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
-- Stream for notifying that an event has become un-partial-stated.
17+
CREATE TABLE un_partial_stated_event_stream(
18+
-- Position in the stream
19+
stream_id BIGINT PRIMARY KEY NOT NULL,
20+
21+
-- Which instance wrote this entry.
22+
instance_name TEXT NOT NULL,
23+
24+
-- Which event has been un-partial-stated.
25+
event_id TEXT NOT NULL REFERENCES events(event_id) ON DELETE CASCADE,
26+
27+
-- true iff the `rejected` status of the event changed when it became
28+
-- un-partial-stated.
29+
rejection_status_changed BOOLEAN NOT NULL
30+
);
31+
32+
-- We want an index here because of the foreign key constraint:
33+
-- upon deleting an event, the database needs to be able to check here.
34+
CREATE UNIQUE INDEX un_partial_stated_event_stream_room_id ON un_partial_stated_event_stream (event_id);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* Copyright 2022 The Matrix.org Foundation C.I.C
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
CREATE SEQUENCE IF NOT EXISTS un_partial_stated_event_stream_sequence;
17+
18+
SELECT setval('un_partial_stated_event_stream_sequence', (
19+
SELECT COALESCE(MAX(stream_id), 1) FROM un_partial_stated_event_stream
20+
));

0 commit comments

Comments
 (0)