@@ -537,10 +537,7 @@ function handleSlowRequestLatency(data) {
537537 const durationSec = ( latencyMs / 1000 ) . toFixed ( 1 ) ;
538538 const queueSec = ( queueTimeMs / 1000 ) . toFixed ( 1 ) ;
539539
540- let msg = `🐌 Slow request #${ data . requestNumber } completed: ${ durationSec } s (${ scenario } )` ;
541- if ( queueTimeMs > 100 ) {
542- msg += ` [Queue Time: ${ queueSec } s]` ;
543- }
540+ let msg = `🐌 Slow request #${ data . requestNumber } completed: ${ durationSec } s (${ scenario } ) [Queue Time: ${ queueSec } s]` ;
544541
545542 logEvent ( 'warning' , msg ) ;
546543}
@@ -593,19 +590,29 @@ function updateLatencyDisplay(currentLatency, isTimeout, isError) {
593590 if ( avgEl && history . values . length > 0 ) {
594591 const avg = history . values . reduce ( ( a , b ) => a + b , 0 ) / history . values . length ;
595592 avgEl . textContent = formatLatency ( avg ) ;
593+ avgEl . className = `latency-value ${ getLatencyClass ( avg , false ) } ` ;
596594 }
597595
598596 // Calculate max
599597 const maxEl = document . getElementById ( 'latencyMax' ) ;
600598 if ( maxEl && history . values . length > 0 ) {
601599 const max = Math . max ( ...history . values ) ;
602600 maxEl . textContent = formatLatency ( max ) ;
601+
602+ // precise timeout check for max value could be complex,
603+ // but high latency > 1s will be red anyway which is sufficient
604+ maxEl . className = `latency-value ${ getLatencyClass ( max , false ) } ` ;
603605 }
604606
605607 // Update timeout count
606608 const timeoutsEl = document . getElementById ( 'latencyTimeouts' ) ;
607609 if ( timeoutsEl ) {
608610 timeoutsEl . textContent = state . latencyStats . timeoutCount ;
611+ if ( state . latencyStats . timeoutCount > 0 ) {
612+ timeoutsEl . className = 'latency-value timeout' ;
613+ } else {
614+ timeoutsEl . className = 'latency-value' ;
615+ }
609616 }
610617}
611618
@@ -682,10 +689,13 @@ function updateLatencyChart() {
682689 * This runs independently in the browser and won't be affected by server thread pool issues.
683690 */
684691function startClientProbe ( ) {
685- // Client probe at same interval as server for comparison
686- state . clientProbeInterval = setInterval ( async ( ) => {
692+ // Use a flag to track running state
693+ state . isClientProbeRunning = true ;
694+
695+ const runProbe = async ( ) => {
696+ if ( ! state . isClientProbeRunning ) return ;
697+
687698 try {
688- const start = performance . now ( ) ;
689699 const controller = new AbortController ( ) ;
690700 const timeoutId = setTimeout ( ( ) => controller . abort ( ) , CONFIG . latencyTimeoutMs ) ;
691701
@@ -694,29 +704,32 @@ function startClientProbe() {
694704 signal : controller . signal
695705 } ) ;
696706 clearTimeout ( timeoutId ) ;
697-
698- const latency = performance . now ( ) - start ;
699- // Client probe data could be shown separately or compared
700- // For now, we rely on server-side probe which is more accurate for thread pool measurement
701707 } catch ( e ) {
702708 clearTimeout ( timeoutId ) ;
703- if ( e . name === 'AbortError' ) {
704- // Timeout - handled by server probe
705- }
709+ // Ignore errors/aborts
706710 }
707711 } catch ( err ) {
708- // Network error - server probe will detect this
712+ // Ignore network errors
713+ }
714+
715+ // Schedule next probe ONLY after this one completes (Closed Loop)
716+ if ( state . isClientProbeRunning ) {
717+ state . clientProbeTimeout = setTimeout ( runProbe , CONFIG . latencyProbeIntervalMs ) ;
709718 }
710- } , CONFIG . latencyProbeIntervalMs ) ;
719+ } ;
720+
721+ // Start the loop
722+ runProbe ( ) ;
711723}
712724
713725/**
714726 * Stop client-side probe.
715727 */
716728function stopClientProbe ( ) {
717- if ( state . clientProbeInterval ) {
718- clearInterval ( state . clientProbeInterval ) ;
719- state . clientProbeInterval = null ;
729+ state . isClientProbeRunning = false ;
730+ if ( state . clientProbeTimeout ) {
731+ clearTimeout ( state . clientProbeTimeout ) ;
732+ state . clientProbeTimeout = null ;
720733 }
721734}
722735
0 commit comments