fix: tighten sliding sync memory management#348
Merged
Conversation
Two memory management issues fixed: 1. SlidingSyncManager.dispose() now calls this.slidingSync.stop() before removing listeners. Without this the SDK's internal Promise loop keeps polling the sliding sync proxy even after the client is disposed, leaking network connections and preventing garbage collection. 2. pruneRoomTimeline() is called from unsubscribeFromRoom(). When a room leaves the active subscription its EventTimeline chain can hold hundreds of events in memory. Once the event count exceeds PRUNE_TIMELINE_THRESHOLD (= ACTIVE_ROOM_TIMELINE_LIMIT_HIGH = 150) the live timeline is reset via EventTimelineSet.resetLiveTimeline(). The full history remains on disk in IndexedDBStore and is re-fetched via the active-room subscription on next open, so there is no user-visible change. Adds 5 unit tests: 1 for dispose, 4 for the pruning threshold boundary.
…gation Replace the adaptive 50/100/150 timeline limit system with a flat ACTIVE_ROOM_TIMELINE_LIMIT=50, matching Element Web approach. Stop calling unsubscribeFromRoom() on room navigation so subscriptions accumulate across the session for instant room switching. Element Web SlidingSyncManager.setRoomVisible() never removes subscriptions at all -- no pruning whatsoever. Our PRUNE_TIMELINE_THRESHOLD=100 guard is more conservative than their approach. - Remove AdaptiveSignals type, readAdaptiveSignals(), resolveAdaptiveRoomTimelineLimit(), applyRoomTimelineLimit() - Remove adaptiveTimeline and configuredTimelineLimit class fields - Remove adaptiveTimeline from SlidingSyncDiagnostics type and UI - Stop unsubscribing on nav in useSlidingSyncActiveRoom - Lower PRUNE_TIMELINE_THRESHOLD from 150 to 100
Wire onMembershipLeave to RoomMemberEvent.Membership in attach()/dispose(). When the local user leaves or is banned from a subscribed room, calls unsubscribeFromRoom() which prunes timelines exceeding PRUNE_TIMELINE_THRESHOLD. Previously, unsubscribeFromRoom() was dead code — nothing in the app called it. Also add 5 unit tests covering: leave, ban, other-user leave, join (no-op), and non-subscribed room (no-op).
Contributor
Author
|
Did limited testing on the PR Preview, however, I'm not in that many large, active rooms, so my ability to test is limited. I will try leaving it open overnight to see what happens. Overall, it seems more stable when switching between rooms, which is a major win imo. |
Contributor
Deploying with
|
| Status | Preview URL | Commit | Alias | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! | https://pr-348-sable.raspy-dream-bb1d.workers.dev | fa4da19 | pr-348 |
Thu, 19 Mar 2026 05:18:37 GMT |
dozro
approved these changes
Mar 19, 2026
Contributor
Author
7w1
approved these changes
Mar 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
A series of improvements to
SlidingSyncManagermemory handling, worked through in four steps.1. Stop polling loop on
dispose()SlidingSyncManager.dispose()removed event listeners but never calledthis.slidingSync.stop(). The SDK's internal HTTP polling loop kept running indefinitely after the client was "disposed", leaking network connections and blocking garbage collection.Fix:
dispose()now callsthis.slidingSync.stop()before removing listeners.2. Remove adaptive timeline-limit logic
SlidingSyncManagerpreviously adjusted per-room timeline limits dynamically based on connection quality (ACTIVE_ROOM_TIMELINE_LIMIT_LOW/HIGH). This complexity caused problems (spurious scroll resets on bulk-loads) and the adaptive behaviour wasn't observable in practice. All adaptive constants, helpers, and connection-quality listeners have been removed. The active room subscription now always requests 50 events.3. Stop unsubscribing on navigation
useSlidingSyncActiveRoompreviously calledunsubscribeFromRoom()in its cleanup function, so every room visited would have its subscription removed even if the user briefly navigated away and came back. The hook now only switches the active-room subscription; subscriptions accumulate across the session so returning to a room is instant.4. Auto-unsubscribe on leave / ban
onMembershipLeavehandler registered onRoomMemberEvent.Membershipinattach()/dispose(). When the local user leaves or is banned from a currently-subscribed room,unsubscribeFromRoom()fires automatically to clean up the sliding sync subscription.Additional Information
These changes are all loosely related to each other, and all work towards the goal of making slding sync more in-line with how it is handled in Element Web.
There are some things to note:
Tests
6 unit tests in
slidingSync.test.ts:dispose()callsstop()on the sliding sync instanceleavebanjoin