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

Commit f9f3f77

Browse files
committed
Store the thread ID with the receipt.
1 parent 893378d commit f9f3f77

File tree

8 files changed

+119
-37
lines changed

8 files changed

+119
-37
lines changed

synapse/handlers/receipts.py

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ async def _handle_new_receipts(self, receipts: List[ReadReceipt]) -> bool:
124124
receipt.receipt_type,
125125
receipt.user_id,
126126
receipt.event_ids,
127+
receipt.thread_id,
127128
receipt.data,
128129
)
129130

synapse/storage/database.py

+20-7
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@
9595
"local_media_repository_thumbnails": "local_media_repository_thumbnails_method_idx",
9696
"remote_media_cache_thumbnails": "remote_media_repository_thumbnails_method_idx",
9797
"event_push_summary": "event_push_summary_unique_index2",
98+
"receipts_linearized": "receipts_linearized_unique_index",
99+
"receipts_graph": "receipts_graph_unique_index",
98100
}
99101

100102

@@ -1191,6 +1193,7 @@ def simple_upsert_txn(
11911193
keyvalues: Dict[str, Any],
11921194
values: Dict[str, Any],
11931195
insertion_values: Optional[Dict[str, Any]] = None,
1196+
where_clause: Optional[str] = None,
11941197
lock: bool = True,
11951198
) -> bool:
11961199
"""
@@ -1213,7 +1216,12 @@ def simple_upsert_txn(
12131216

12141217
if table not in self._unsafe_to_upsert_tables:
12151218
return self.simple_upsert_txn_native_upsert(
1216-
txn, table, keyvalues, values, insertion_values=insertion_values
1219+
txn,
1220+
table,
1221+
keyvalues,
1222+
values,
1223+
insertion_values=insertion_values,
1224+
where_clause=where_clause,
12171225
)
12181226
else:
12191227
return self.simple_upsert_txn_emulated(
@@ -1222,6 +1230,7 @@ def simple_upsert_txn(
12221230
keyvalues,
12231231
values,
12241232
insertion_values=insertion_values,
1233+
where_clause=where_clause,
12251234
lock=lock,
12261235
)
12271236

@@ -1232,6 +1241,7 @@ def simple_upsert_txn_emulated(
12321241
keyvalues: Dict[str, Any],
12331242
values: Dict[str, Any],
12341243
insertion_values: Optional[Dict[str, Any]] = None,
1244+
where_clause: Optional[str] = None,
12351245
lock: bool = True,
12361246
) -> bool:
12371247
"""
@@ -1259,14 +1269,15 @@ def _getwhere(key: str) -> str:
12591269
else:
12601270
return "%s = ?" % (key,)
12611271

1272+
where = [_getwhere(k) for k in keyvalues]
1273+
if where_clause:
1274+
where.append(where_clause)
1275+
12621276
if not values:
12631277
# If `values` is empty, then all of the values we care about are in
12641278
# the unique key, so there is nothing to UPDATE. We can just do a
12651279
# SELECT instead to see if it exists.
1266-
sql = "SELECT 1 FROM %s WHERE %s" % (
1267-
table,
1268-
" AND ".join(_getwhere(k) for k in keyvalues),
1269-
)
1280+
sql = "SELECT 1 FROM %s WHERE %s" % (table, " AND ".join(where))
12701281
sqlargs = list(keyvalues.values())
12711282
txn.execute(sql, sqlargs)
12721283
if txn.fetchall():
@@ -1277,7 +1288,7 @@ def _getwhere(key: str) -> str:
12771288
sql = "UPDATE %s SET %s WHERE %s" % (
12781289
table,
12791290
", ".join("%s = ?" % (k,) for k in values),
1280-
" AND ".join(_getwhere(k) for k in keyvalues),
1291+
" AND ".join(where),
12811292
)
12821293
sqlargs = list(values.values()) + list(keyvalues.values())
12831294

@@ -1307,6 +1318,7 @@ def simple_upsert_txn_native_upsert(
13071318
keyvalues: Dict[str, Any],
13081319
values: Dict[str, Any],
13091320
insertion_values: Optional[Dict[str, Any]] = None,
1321+
where_clause: Optional[str] = None,
13101322
) -> bool:
13111323
"""
13121324
Use the native UPSERT functionality in PostgreSQL.
@@ -1331,11 +1343,12 @@ def simple_upsert_txn_native_upsert(
13311343
allvalues.update(values)
13321344
latter = "UPDATE SET " + ", ".join(k + "=EXCLUDED." + k for k in values)
13331345

1334-
sql = ("INSERT INTO %s (%s) VALUES (%s) ON CONFLICT (%s) DO %s") % (
1346+
sql = ("INSERT INTO %s (%s) VALUES (%s) ON CONFLICT (%s) %s DO %s") % (
13351347
table,
13361348
", ".join(k for k in allvalues),
13371349
", ".join("?" for _ in allvalues),
13381350
", ".join(k for k in keyvalues),
1351+
f"WHERE {where_clause}" if where_clause else "",
13391352
latter,
13401353
)
13411354
txn.execute(sql, list(allvalues.values()))

synapse/storage/databases/main/receipts.py

+53-18
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ def _insert_linearized_receipt_txn(
637637
receipt_type: str,
638638
user_id: str,
639639
event_id: str,
640+
thread_id: Optional[str],
640641
data: JsonDict,
641642
stream_id: int,
642643
) -> Optional[int]:
@@ -663,12 +664,27 @@ def _insert_linearized_receipt_txn(
663664
# We don't want to clobber receipts for more recent events, so we
664665
# have to compare orderings of existing receipts
665666
if stream_ordering is not None:
666-
sql = (
667-
"SELECT stream_ordering, event_id FROM events"
668-
" INNER JOIN receipts_linearized AS r USING (event_id, room_id)"
669-
" WHERE r.room_id = ? AND r.receipt_type = ? AND r.user_id = ?"
667+
if thread_id is None:
668+
thread_clause = "r.thread_id IS NULL"
669+
thread_args: Tuple[str, ...] = ()
670+
else:
671+
thread_clause = "r.thread_id = ?"
672+
thread_args = (thread_id,)
673+
674+
sql = f"""
675+
SELECT stream_ordering, event_id FROM events
676+
INNER JOIN receipts_linearized AS r USING (event_id, room_id)
677+
WHERE r.room_id = ? AND r.receipt_type = ? AND r.user_id = ? AND {thread_clause}
678+
"""
679+
txn.execute(
680+
sql,
681+
(
682+
room_id,
683+
receipt_type,
684+
user_id,
685+
)
686+
+ thread_args,
670687
)
671-
txn.execute(sql, (room_id, receipt_type, user_id))
672688

673689
for so, eid in txn:
674690
if int(so) >= stream_ordering:
@@ -688,21 +704,28 @@ def _insert_linearized_receipt_txn(
688704
self._receipts_stream_cache.entity_has_changed, room_id, stream_id
689705
)
690706

707+
keyvalues = {
708+
"room_id": room_id,
709+
"receipt_type": receipt_type,
710+
"user_id": user_id,
711+
}
712+
where_clause = ""
713+
if thread_id is None:
714+
where_clause = "thread_id IS NULL"
715+
else:
716+
keyvalues["thread_id"] = thread_id
717+
691718
self.db_pool.simple_upsert_txn(
692719
txn,
693720
table="receipts_linearized",
694-
keyvalues={
695-
"room_id": room_id,
696-
"receipt_type": receipt_type,
697-
"user_id": user_id,
698-
},
721+
keyvalues=keyvalues,
699722
values={
700723
"stream_id": stream_id,
701724
"event_id": event_id,
702725
"event_stream_ordering": stream_ordering,
703726
"data": json_encoder.encode(data),
704-
"thread_id": None,
705727
},
728+
where_clause=where_clause,
706729
# receipts_linearized has a unique constraint on
707730
# (user_id, room_id, receipt_type), so no need to lock
708731
lock=False,
@@ -754,6 +777,7 @@ async def insert_receipt(
754777
receipt_type: str,
755778
user_id: str,
756779
event_ids: List[str],
780+
thread_id: Optional[str],
757781
data: dict,
758782
) -> Optional[Tuple[int, int]]:
759783
"""Insert a receipt, either from local client or remote server.
@@ -786,6 +810,7 @@ async def insert_receipt(
786810
receipt_type,
787811
user_id,
788812
linearized_event_id,
813+
thread_id,
789814
data,
790815
stream_id=stream_id,
791816
# Read committed is actually beneficial here because we check for a receipt with
@@ -800,7 +825,8 @@ async def insert_receipt(
800825

801826
now = self._clock.time_msec()
802827
logger.debug(
803-
"RR for event %s in %s (%i ms old)",
828+
"Receipt %s for event %s in %s (%i ms old)",
829+
receipt_type,
804830
linearized_event_id,
805831
room_id,
806832
now - event_ts,
@@ -813,6 +839,7 @@ async def insert_receipt(
813839
receipt_type,
814840
user_id,
815841
event_ids,
842+
thread_id,
816843
data,
817844
)
818845

@@ -827,6 +854,7 @@ def _insert_graph_receipt_txn(
827854
receipt_type: str,
828855
user_id: str,
829856
event_ids: List[str],
857+
thread_id: Optional[str],
830858
data: JsonDict,
831859
) -> None:
832860
assert self._can_write_to_receipts
@@ -838,19 +866,26 @@ def _insert_graph_receipt_txn(
838866
# FIXME: This shouldn't invalidate the whole cache
839867
txn.call_after(self._get_linearized_receipts_for_room.invalidate, (room_id,))
840868

869+
keyvalues = {
870+
"room_id": room_id,
871+
"receipt_type": receipt_type,
872+
"user_id": user_id,
873+
}
874+
where_clause = ""
875+
if thread_id is None:
876+
where_clause = "thread_id IS NULL"
877+
else:
878+
keyvalues["thread_id"] = thread_id
879+
841880
self.db_pool.simple_upsert_txn(
842881
txn,
843882
table="receipts_graph",
844-
keyvalues={
845-
"room_id": room_id,
846-
"receipt_type": receipt_type,
847-
"user_id": user_id,
848-
},
883+
keyvalues=keyvalues,
849884
values={
850885
"event_ids": json_encoder.encode(event_ids),
851886
"data": json_encoder.encode(data),
852-
"thread_id": None,
853887
},
888+
where_clause=where_clause,
854889
# receipts_graph has a unique constraint on
855890
# (user_id, room_id, receipt_type), so no need to lock
856891
lock=False,

tests/handlers/test_appservice.py

+1
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ def test_sending_read_receipt_batches_to_application_services(self):
447447
receipt_type="m.read",
448448
user_id=self.local_user,
449449
event_ids=[f"$eventid_{i}"],
450+
thread_id=None,
450451
data={},
451452
)
452453
)

tests/replication/slave/storage/test_events.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def test_push_actions_for_user(self, send_receipt: bool):
174174
if send_receipt:
175175
self.get_success(
176176
self.master_store.insert_receipt(
177-
ROOM_ID, ReceiptTypes.READ, USER_ID_2, [event1.event_id], {}
177+
ROOM_ID, ReceiptTypes.READ, USER_ID_2, [event1.event_id], None, {}
178178
)
179179
)
180180

tests/replication/tcp/streams/test_receipts.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ def test_receipt(self):
3333
# tell the master to send a new receipt
3434
self.get_success(
3535
self.hs.get_datastores().main.insert_receipt(
36-
"!room:blue", "m.read", USER_ID, ["$event:blue"], {"a": 1}
36+
"!room:blue",
37+
"m.read",
38+
USER_ID,
39+
["$event:blue"],
40+
thread_id=None,
41+
data={"a": 1},
3742
)
3843
)
3944
self.replicate()
@@ -58,7 +63,12 @@ def test_receipt(self):
5863

5964
self.get_success(
6065
self.hs.get_datastores().main.insert_receipt(
61-
"!room2:blue", "m.read", USER_ID, ["$event2:foo"], {"a": 2}
66+
"!room2:blue",
67+
"m.read",
68+
USER_ID,
69+
["$event2:foo"],
70+
thread_id=None,
71+
data={"a": 2},
6272
)
6373
)
6474
self.replicate()

tests/storage/test_event_push_actions.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def _mark_read(event_id: str) -> None:
110110
"m.read",
111111
user_id=user_id,
112112
event_ids=[event_id],
113+
thread_id=None,
113114
data={},
114115
)
115116
)
@@ -267,13 +268,14 @@ def _create_event(
267268
def _rotate() -> None:
268269
self.get_success(self.store._rotate_notifs())
269270

270-
def _mark_read(event_id: str) -> None:
271+
def _mark_read(event_id: str, thread_id: Optional[str] = None) -> None:
271272
self.get_success(
272273
self.store.insert_receipt(
273274
room_id,
274275
"m.read",
275276
user_id=user_id,
276277
event_ids=[event_id],
278+
thread_id=thread_id,
277279
data={},
278280
)
279281
)

0 commit comments

Comments
 (0)