From f879f4d1e64021657783a4ab4c46d8b7fcbb26e5 Mon Sep 17 00:00:00 2001 From: Alex Prate Date: Sat, 16 May 2026 03:06:12 +0200 Subject: [PATCH] fix(matrix): throttle room history backfill requests Reduce room-history request bursts with smaller batches, adaptive stop conditions, and 429 retry backoff to prevent M_LIMIT_EXCEEDED during chat initialization. --- .../client/providers/matrix-provider.tsx | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/packages/core/src/matrix/client/providers/matrix-provider.tsx b/packages/core/src/matrix/client/providers/matrix-provider.tsx index 6c6499813e..d2415a5661 100644 --- a/packages/core/src/matrix/client/providers/matrix-provider.tsx +++ b/packages/core/src/matrix/client/providers/matrix-provider.tsx @@ -142,6 +142,10 @@ export const MATRIX_UPLOAD_TIMEOUT_MS = 120_000; const MATRIX_UPLOAD_STAGGER_MS = 400; const MATRIX_UPLOAD_RATE_LIMIT_MAX_ATTEMPTS = 4; +const MATRIX_HISTORY_BATCH_DELAY_MS = 220; +const MATRIX_HISTORY_RATE_LIMIT_MAX_RETRIES = 3; +const MATRIX_HISTORY_RECENT_LOAD_COOLDOWN_MS = 45_000; +const MATRIX_HISTORY_TARGET_EVENTS = 220; const MATRIX_GROUP_CALL_EVENT_TYPE = 'org.matrix.msc3401.call'; const MATRIX_GROUP_CALL_MEMBER_EVENT_TYPE = 'org.matrix.msc3401.call.member'; const MATRIX_LEGACY_CALL_MEMBER_EVENT_TYPE = 'm.call.member'; @@ -488,6 +492,9 @@ export const MatrixProvider: React.FC = ({ children }) => { const roomHistoryLoadRef = React.useRef>>( new Map(), ); + const roomHistoryLastLoadedAtRef = React.useRef>( + new Map(), + ); const [registeredRoomListeners, setRegisteredRoomListeners] = React.useState< RoomMessageListenerRecord[] >([]); @@ -1283,25 +1290,59 @@ export const MatrixProvider: React.FC = ({ children }) => { return; } - const pageSize = Math.max(10, options?.pageSize ?? 50); - const maxBatches = Math.max(1, options?.maxBatches ?? 40); + const now = Date.now(); + const lastLoadedAt = roomHistoryLastLoadedAtRef.current.get(roomId) ?? 0; + const hasRecentLoad = + now - lastLoadedAt < MATRIX_HISTORY_RECENT_LOAD_COOLDOWN_MS; + const existingEventsCount = room.getLiveTimeline().getEvents().length; + if ( + hasRecentLoad || + existingEventsCount >= MATRIX_HISTORY_TARGET_EVENTS + ) { + return; + } + + const pageSize = Math.max(10, options?.pageSize ?? 25); + const maxBatches = Math.max(1, options?.maxBatches ?? 8); const loadPromise = (async () => { + let rateLimitRetries = 0; for (let i = 0; i < maxBatches; i++) { const beforeCount = room.getLiveTimeline().getEvents().length; try { + if (i > 0) { + await delay(MATRIX_HISTORY_BATCH_DELAY_MS); + } await client.scrollback(room, pageSize); } catch (error) { + if ( + isMatrixRateLimitedError(error) && + rateLimitRetries < MATRIX_HISTORY_RATE_LIMIT_MAX_RETRIES + ) { + const backoffMs = matrixRateLimitBackoffMs( + error, + rateLimitRetries, + ); + rateLimitRetries += 1; + await delay(backoffMs); + i -= 1; + continue; + } console.warn( '[MatrixProvider] Failed while loading room history:', error, ); break; } + rateLimitRetries = 0; const afterCount = room.getLiveTimeline().getEvents().length; if (afterCount <= beforeCount) { break; } + if (afterCount >= MATRIX_HISTORY_TARGET_EVENTS) { + break; + } } + roomHistoryLastLoadedAtRef.current.set(roomId, Date.now()); })(); roomHistoryLoadRef.current.set(roomId, loadPromise);