@@ -8,6 +8,8 @@ import OptimizationsTree from './Tree/OptimizationsTree';
88
99/** Poll interval while at least one optimization is running and the tab is visible (2–5s range). */
1010const POLL_ACTIVE_MS = 4000 ;
11+ /** Max delay between retries after a failed poll (exponential backoff cap). */
12+ const POLL_ERROR_RETRY_MAX_MS = 32000 ;
1113
1214function isRequestCanceled ( error ) {
1315 return error ?. code === 'ERR_CANCELED' || error ?. name === 'CanceledError' ;
@@ -23,6 +25,7 @@ const Optimizations = ({ collectionName }) => {
2325 const pollTimeoutRef = useRef ( null ) ;
2426 const lastRunningRef = useRef ( false ) ;
2527 const mountedRef = useRef ( true ) ;
28+ const pollErrorBackoffMsRef = useRef ( POLL_ACTIVE_MS ) ;
2629
2730 const runFetch = useCallback (
2831 async ( { preserveSelection = false } = { } ) => {
@@ -34,6 +37,7 @@ const Optimizations = ({ collectionName }) => {
3437 abortRef . current = ac ;
3538
3639 if ( ! preserveSelection ) {
40+ pollErrorBackoffMsRef . current = POLL_ACTIVE_MS ;
3741 setIsRefreshing ( true ) ;
3842 setSelectedOptimization ( null ) ;
3943 }
@@ -52,6 +56,7 @@ const Optimizations = ({ collectionName }) => {
5256 const result = next ?. result ;
5357 const hasRunning = Array . isArray ( result ?. running ) && result . running . length > 0 ;
5458 lastRunningRef . current = hasRunning ;
59+ pollErrorBackoffMsRef . current = POLL_ACTIVE_MS ;
5560
5661 clearTimeout ( pollTimeoutRef . current ) ;
5762 if ( hasRunning && ! document . hidden ) {
@@ -63,6 +68,14 @@ const Optimizations = ({ collectionName }) => {
6368 } catch ( error ) {
6469 if ( isRequestCanceled ( error ) ) return ;
6570 console . error ( 'Error fetching optimizations:' , error ) ;
71+ if ( mountedRef . current && lastRunningRef . current && ! document . hidden ) {
72+ const delay = pollErrorBackoffMsRef . current ;
73+ pollErrorBackoffMsRef . current = Math . min ( pollErrorBackoffMsRef . current * 2 , POLL_ERROR_RETRY_MAX_MS ) ;
74+ pollTimeoutRef . current = window . setTimeout ( ( ) => {
75+ pollTimeoutRef . current = null ;
76+ void runFetch ( { preserveSelection : true } ) ;
77+ } , delay ) ;
78+ }
6679 } finally {
6780 if ( ! preserveSelection && mountedRef . current ) {
6881 setIsRefreshing ( false ) ;
0 commit comments