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