|
34 | 34 | EventTypes, |
35 | 35 | Membership, |
36 | 36 | ) |
| 37 | +from synapse.api.errors import SlidingSyncUnknownPosition |
37 | 38 | from synapse.api.room_versions import KNOWN_ROOM_VERSIONS |
38 | 39 | from synapse.events import StrippedStateEvent |
39 | 40 | from synapse.events.utils import parse_stripped_state_event |
40 | 41 | from synapse.logging.opentracing import start_active_span, trace |
| 42 | +from synapse.storage.databases.main.sliding_sync import UPDATE_INTERVAL_LAST_USED_TS_MS |
41 | 43 | from synapse.storage.databases.main.state import ( |
42 | 44 | ROOM_UNKNOWN_SENTINEL, |
43 | 45 | Sentinel as StateSentinel, |
|
68 | 70 | ) |
69 | 71 | from synapse.types.state import StateFilter |
70 | 72 | from synapse.util import MutableOverlayMapping |
| 73 | +from synapse.util.constants import MILLISECONDS_PER_SECOND, ONE_HOUR_SECONDS |
71 | 74 | from synapse.util.sentinel import Sentinel |
72 | 75 |
|
73 | 76 | if TYPE_CHECKING: |
|
77 | 80 | logger = logging.getLogger(__name__) |
78 | 81 |
|
79 | 82 |
|
| 83 | +# Minimum time in milliseconds since the last sync before we consider expiring |
| 84 | +# the connection due to too many rooms to send. This stops from getting into |
| 85 | +# tight loops with clients that request lots of data at once. |
| 86 | +# |
| 87 | +# c.f. `NUM_ROOMS_THRESHOLD`. These values are somewhat arbitrary picked. |
| 88 | +MINIMUM_NOT_USED_AGE_EXPIRY_MS = ONE_HOUR_SECONDS * MILLISECONDS_PER_SECOND |
| 89 | + |
| 90 | +# How many rooms with updates we allow before we consider the connection expired |
| 91 | +# due to too many rooms to send. |
| 92 | +# |
| 93 | +# c.f. `MINIMUM_NOT_USED_AGE_EXPIRY_MS`. These values are somewhat arbitrary |
| 94 | +# picked. |
| 95 | +NUM_ROOMS_THRESHOLD = 100 |
| 96 | + |
| 97 | +# Sanity check that our minimum age is sensible compared to the update interval, |
| 98 | +# i.e. if `MINIMUM_NOT_USED_AGE_EXPIRY_MS` is too small then we might expire the |
| 99 | +# connection even if it is actively being used (and we're just not updating the |
| 100 | +# DB frequently enough). We arbitrarily double the update interval to give some |
| 101 | +# wiggle room. |
| 102 | +assert 2 * UPDATE_INTERVAL_LAST_USED_TS_MS < MINIMUM_NOT_USED_AGE_EXPIRY_MS |
| 103 | + |
80 | 104 | # Helper definition for the types that we might return. We do this to avoid |
81 | 105 | # copying data between types (which can be expensive for many rooms). |
82 | 106 | RoomsForUserType = RoomsForUserStateReset | RoomsForUser | RoomsForUserSlidingSync |
@@ -176,6 +200,7 @@ def __init__(self, hs: "HomeServer"): |
176 | 200 | self.storage_controllers = hs.get_storage_controllers() |
177 | 201 | self.rooms_to_exclude_globally = hs.config.server.rooms_to_exclude_from_sync |
178 | 202 | self.is_mine_id = hs.is_mine_id |
| 203 | + self._clock = hs.get_clock() |
179 | 204 |
|
180 | 205 | async def compute_interested_rooms( |
181 | 206 | self, |
@@ -857,11 +882,41 @@ async def _filter_relevant_rooms_to_send( |
857 | 882 |
|
858 | 883 | # We only need to check for new events since any state changes |
859 | 884 | # will also come down as new events. |
860 | | - rooms_that_have_updates = ( |
861 | | - self.store.get_rooms_that_might_have_updates( |
| 885 | + |
| 886 | + rooms_that_have_updates = await ( |
| 887 | + self.store.get_rooms_that_have_updates_since_sliding_sync_table( |
862 | 888 | relevant_room_map.keys(), from_token.room_key |
863 | 889 | ) |
864 | 890 | ) |
| 891 | + |
| 892 | + # Check if we have lots of updates to send, if so then its |
| 893 | + # better for us to tell the client to do a full resync |
| 894 | + # instead (to try and avoid long SSS response times when |
| 895 | + # there is new data). |
| 896 | + # |
| 897 | + # Due to the construction of the SSS API, the client is in |
| 898 | + # charge of setting the range of rooms to request updates |
| 899 | + # for. Generally, it will start with a small range and then |
| 900 | + # expand (and occasionally it may contract the range again |
| 901 | + # if its been offline for a while). If we know there are a |
| 902 | + # lot of updates, it's better to reset the connection and |
| 903 | + # wait for the client to start again (with a much smaller |
| 904 | + # range) than to try and send down a large number of updates |
| 905 | + # (which can take a long time). |
| 906 | + # |
| 907 | + # We only do this if the last sync was over |
| 908 | + # `MINIMUM_NOT_USED_AGE_EXPIRY_MS` to ensure we don't get |
| 909 | + # into tight loops with clients that keep requesting large |
| 910 | + # sliding sync windows. |
| 911 | + if len(rooms_that_have_updates) > NUM_ROOMS_THRESHOLD: |
| 912 | + last_sync_ts = previous_connection_state.last_used_ts |
| 913 | + if ( |
| 914 | + last_sync_ts is not None |
| 915 | + and (self._clock.time_msec() - last_sync_ts) |
| 916 | + > MINIMUM_NOT_USED_AGE_EXPIRY_MS |
| 917 | + ): |
| 918 | + raise SlidingSyncUnknownPosition() |
| 919 | + |
865 | 920 | rooms_should_send.update(rooms_that_have_updates) |
866 | 921 | relevant_rooms_to_send_map = { |
867 | 922 | room_id: room_sync_config |
|
0 commit comments