feat: implement sync retry mechanism in ClientRoot with timeout handling
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
} from 'folds';
|
||||
import { HttpApiEvent, HttpApiEventHandlerMap, MatrixClient, SyncState } from 'matrix-js-sdk';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import React, { MouseEventHandler, ReactNode, useCallback, useEffect, useState } from 'react';
|
||||
import React, { MouseEventHandler, ReactNode, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
clearCacheAndReload,
|
||||
clearLoginData,
|
||||
@@ -155,9 +155,13 @@ const useBackgroundSync = (mx?: MatrixClient) => {
|
||||
type ClientRootProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
const MAX_SYNC_RETRIES = 3;
|
||||
|
||||
export function ClientRoot({ children }: ClientRootProps) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [syncError, setSyncError] = useState<Error | null>(null);
|
||||
const syncRetryCount = useRef(0);
|
||||
const mxRef = useRef<MatrixClient | undefined>(undefined);
|
||||
const { baseUrl } = getCurrentSession() ?? {};
|
||||
|
||||
// Enable view transitions for smooth navigation
|
||||
@@ -173,6 +177,11 @@ export function ClientRoot({ children }: ClientRootProps) {
|
||||
}, [])
|
||||
);
|
||||
const mx = loadState.status === AsyncStatus.Success ? loadState.data : undefined;
|
||||
|
||||
useEffect(() => {
|
||||
mxRef.current = mx;
|
||||
}, [mx]);
|
||||
|
||||
const [startState, startMatrix] = useAsyncCallback<void, Error, [MatrixClient]>(
|
||||
useCallback((m) => startClient(m), [])
|
||||
);
|
||||
@@ -193,14 +202,14 @@ export function ClientRoot({ children }: ClientRootProps) {
|
||||
}
|
||||
}, [mx, startMatrix]);
|
||||
|
||||
// Timeout if sync takes too long
|
||||
// Timeout if sync takes too long - nudge the client rather than immediately erroring
|
||||
useEffect(() => {
|
||||
if (!mx || !loading) return;
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
console.error('[ClientRoot] Sync timeout - client failed to reach PREPARED state');
|
||||
setSyncError(new Error('Sync timeout - failed to connect to homeserver'));
|
||||
}, 30000); // 30 second timeout
|
||||
console.warn('[ClientRoot] Sync taking too long, nudging client...');
|
||||
mx.retryImmediately();
|
||||
}, 30000);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [mx, loading]);
|
||||
@@ -208,14 +217,25 @@ export function ClientRoot({ children }: ClientRootProps) {
|
||||
useSyncState(
|
||||
mx,
|
||||
useCallback((state, prevState, data) => {
|
||||
const client = mxRef.current;
|
||||
if (state === 'PREPARED') {
|
||||
setLoading(false);
|
||||
setSyncError(null);
|
||||
syncRetryCount.current = 0;
|
||||
} else if (state === 'ERROR') {
|
||||
if (syncRetryCount.current < MAX_SYNC_RETRIES) {
|
||||
syncRetryCount.current += 1;
|
||||
const attempt = syncRetryCount.current;
|
||||
console.warn(`[ClientRoot] Sync error, auto-retrying (${attempt}/${MAX_SYNC_RETRIES})...`);
|
||||
setTimeout(() => {
|
||||
if (client) startClient(client).catch(() => {});
|
||||
}, 2000 * attempt);
|
||||
} else {
|
||||
const error = data instanceof Error ? data : new Error('Sync failed');
|
||||
console.error('[ClientRoot] Sync error:', error);
|
||||
console.error('[ClientRoot] Sync error after max retries:', error);
|
||||
setSyncError(error);
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user