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

Commit 9391de3

Browse files
authored
Fix /initialSync error due to unhashable RoomStreamToken (#10827)
The deprecated /initialSync endpoint maintains a cache of responses, using parameter values as part of the cache key. When a `from` or `to` parameter is specified, it gets converted into a `StreamToken`, which contains a `RoomStreamToken` and forms part of the cache key. `RoomStreamToken`s need to be made hashable for this to work.
1 parent 52913d5 commit 9391de3

File tree

3 files changed

+19
-6
lines changed

3 files changed

+19
-6
lines changed

changelog.d/10827.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix error in deprecated `/initialSync` endpoint when using the undocumented `from` and `to` parameters.

synapse/storage/databases/main/stream.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
from collections import namedtuple
4040
from typing import TYPE_CHECKING, Collection, Dict, List, Optional, Set, Tuple
4141

42+
from frozendict import frozendict
43+
4244
from twisted.internet import defer
4345

4446
from synapse.api.filtering import Filter
@@ -379,7 +381,7 @@ def get_room_max_token(self) -> RoomStreamToken:
379381
if p > min_pos
380382
}
381383

382-
return RoomStreamToken(None, min_pos, positions)
384+
return RoomStreamToken(None, min_pos, frozendict(positions))
383385

384386
async def get_room_events_stream_for_rooms(
385387
self,

synapse/types.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
)
3131

3232
import attr
33+
from frozendict import frozendict
3334
from signedjson.key import decode_verify_key_bytes
3435
from unpaddedbase64 import decode_base64
3536
from zope.interface import Interface
@@ -457,6 +458,9 @@ class RoomStreamToken:
457458
458459
Note: The `RoomStreamToken` cannot have both a topological part and an
459460
instance map.
461+
462+
For caching purposes, `RoomStreamToken`s and by extension, all their
463+
attributes, must be hashable.
460464
"""
461465

462466
topological = attr.ib(
@@ -466,12 +470,12 @@ class RoomStreamToken:
466470
stream = attr.ib(type=int, validator=attr.validators.instance_of(int))
467471

468472
instance_map = attr.ib(
469-
type=Dict[str, int],
470-
factory=dict,
473+
type="frozendict[str, int]",
474+
factory=frozendict,
471475
validator=attr.validators.deep_mapping(
472476
key_validator=attr.validators.instance_of(str),
473477
value_validator=attr.validators.instance_of(int),
474-
mapping_validator=attr.validators.instance_of(dict),
478+
mapping_validator=attr.validators.instance_of(frozendict),
475479
),
476480
)
477481

@@ -507,7 +511,7 @@ async def parse(cls, store: "DataStore", string: str) -> "RoomStreamToken":
507511
return cls(
508512
topological=None,
509513
stream=stream,
510-
instance_map=instance_map,
514+
instance_map=frozendict(instance_map),
511515
)
512516
except Exception:
513517
pass
@@ -540,7 +544,7 @@ def copy_and_advance(self, other: "RoomStreamToken") -> "RoomStreamToken":
540544
for instance in set(self.instance_map).union(other.instance_map)
541545
}
542546

543-
return RoomStreamToken(None, max_stream, instance_map)
547+
return RoomStreamToken(None, max_stream, frozendict(instance_map))
544548

545549
def as_historical_tuple(self) -> Tuple[int, int]:
546550
"""Returns a tuple of `(topological, stream)` for historical tokens.
@@ -593,6 +597,12 @@ async def to_string(self, store: "DataStore") -> str:
593597

594598
@attr.s(slots=True, frozen=True)
595599
class StreamToken:
600+
"""A collection of positions within multiple streams.
601+
602+
For caching purposes, `StreamToken`s and by extension, all their attributes,
603+
must be hashable.
604+
"""
605+
596606
room_key = attr.ib(
597607
type=RoomStreamToken, validator=attr.validators.instance_of(RoomStreamToken)
598608
)

0 commit comments

Comments
 (0)