feat: implement polling fallback for SSE connection failures and add event deduplication

This commit is contained in:
Jose Selesan
2026-04-17 17:34:04 -03:00
parent e0d755bef7
commit 585e7d2226

View File

@@ -172,7 +172,9 @@ export function HomePage() {
let eventSource: EventSource | null = null; let eventSource: EventSource | null = null;
let reconnectTimeout: ReturnType<typeof setTimeout> | null = null; let reconnectTimeout: ReturnType<typeof setTimeout> | null = null;
let reconnectAttempts = 0; let reconnectAttempts = 0;
const maxReconnectAttempts = 5; const maxReconnectAttempts = 3;
let lastEventId: string | null = null;
let pollInterval: ReturnType<typeof setInterval> | null = null;
const connect = () => { const connect = () => {
if (eventSource) { if (eventSource) {
@@ -180,7 +182,8 @@ export function HomePage() {
} }
if (reconnectAttempts >= maxReconnectAttempts) { if (reconnectAttempts >= maxReconnectAttempts) {
console.log('[SSE] Max reconnect attempts reached'); console.log('[SSE] Max reconnect attempts reached, switching to polling');
startPolling();
return; return;
} }
@@ -191,30 +194,46 @@ export function HomePage() {
eventSource.onerror = () => { eventSource.onerror = () => {
if (eventSource?.readyState === EventSource.CLOSED) { if (eventSource?.readyState === EventSource.CLOSED) {
reconnectAttempts++; reconnectAttempts++;
const delay = Math.min(1000 * 2 ** reconnectAttempts, 30000);
console.log( console.log(
`[SSE] Connection closed, retry ${reconnectAttempts}/${maxReconnectAttempts} in ${delay}ms` `[SSE] Connection closed, retry ${reconnectAttempts}/${maxReconnectAttempts}`
); );
reconnectTimeout = setTimeout(() => { reconnectTimeout = setTimeout(() => {
connect(); connect();
}, delay); }, 2000);
} }
}; };
eventSource.addEventListener('message', (event) => { eventSource.addEventListener('message', (event) => {
if (event.data && event.data !== 'connected' && event.data !== 'ping') { if (event.data && event.data !== 'connected' && event.data !== 'ping') {
if (event.data !== lastEventId) {
lastEventId = event.data;
reconnectAttempts = 0; reconnectAttempts = 0;
void queryClient.invalidateQueries({ queryKey: ['admin-bookings', selectedComplex.id] }); void queryClient.invalidateQueries({
queryKey: ['admin-bookings', selectedComplex.id],
});
}
} }
}); });
}; };
const startPolling = () => {
if (pollInterval) return;
console.log('[SSE] Starting polling fallback');
pollInterval = setInterval(() => {
void queryClient.invalidateQueries({ queryKey: ['admin-bookings', selectedComplex.id] });
}, 10000);
};
connect(); connect();
return () => { return () => {
if (reconnectTimeout) { if (reconnectTimeout) {
clearTimeout(reconnectTimeout); clearTimeout(reconnectTimeout);
} }
if (pollInterval) {
clearInterval(pollInterval);
}
eventSource?.close(); eventSource?.close();
}; };
}, [selectedComplex?.id, queryClient]); }, [selectedComplex?.id, queryClient]);