@@ -126,8 +126,21 @@ const cfg = {
126126 onlySessionId : process . env . SCHED_SESSION_ID || null ,
127127} ;
128128
129- const log = ( msg ) => console . log ( `[ado-scheduler] ${ msg } ` ) ;
130- const errlog = ( msg ) => console . error ( `[ado-scheduler] ${ msg } ` ) ;
129+ const iso = ( ) => new Date ( ) . toISOString ( ) ;
130+ const log = ( msg ) => console . log ( `${ iso ( ) } [ado-scheduler] ${ msg } ` ) ;
131+ const errlog = ( msg ) => console . error ( `${ iso ( ) } [ado-scheduler] ${ msg } ` ) ;
132+
133+ // ─── steady-state log suppression ────────────────────────────────────────────
134+ // The poll loop re-evaluates every session each pass (~every pollMs). Logging
135+ // every session's placement decision each pass floods the log with identical
136+ // "skip … colocate (lease ok)" lines. Instead we log a session's decision only
137+ // when it CHANGES, and emit a periodic heartbeat so liveness is still visible.
138+ // Set SCHED_VERBOSE=1 to restore per-pass logging of every decision.
139+ const VERBOSE = truthy ( process . env . SCHED_VERBOSE ) ;
140+ const HEARTBEAT_MS = intEnv ( "SCHED_HEARTBEAT_MS" , 60000 ) ;
141+ const lastDecisionSig = new Map ( ) ; // sessionId -> "action:reason"
142+ let lastObservedSig = null ;
143+ let lastHeartbeatMs = 0 ;
131144
132145// ─── credentials ─────────────────────────────────────────────────────────────
133146const credential = new DefaultAzureCredential ( ) ;
@@ -288,15 +301,35 @@ async function pass(mgmt, pool) {
288301 ! s . parentSessionId && ! s . isSystem && s . status === cfg . targetStatus ) ;
289302 if ( cfg . onlySessionId ) candidates = candidates . filter ( ( s ) => s . sessionId === cfg . onlySessionId ) ;
290303
291- log ( `observed ${ sessions . length } sessions; ${ candidates . length } runnable root candidate(s)` +
292- ( cfg . onlySessionId ? ` (filtered to ${ cfg . onlySessionId } )` : "" ) ) ;
304+ // Heartbeat: force a full re-log of the observed line + every decision on a
305+ // fixed cadence so the log periodically shows the current steady state.
306+ const heartbeat = VERBOSE || ( nowMs - lastHeartbeatMs >= HEARTBEAT_MS ) ;
307+ if ( heartbeat ) lastHeartbeatMs = nowMs ;
308+
309+ // Prune decision signatures for sessions no longer observed as candidates so
310+ // a re-appearing session logs its decision afresh (and the map stays bounded).
311+ const candidateIds = new Set ( candidates . map ( ( s ) => s . sessionId ) ) ;
312+ for ( const id of lastDecisionSig . keys ( ) ) {
313+ if ( ! candidateIds . has ( id ) ) lastDecisionSig . delete ( id ) ;
314+ }
315+
316+ const observedSig = `${ sessions . length } /${ candidates . length } ` ;
317+ if ( heartbeat || observedSig !== lastObservedSig ) {
318+ log ( `observed ${ sessions . length } sessions; ${ candidates . length } runnable root candidate(s)` +
319+ ( cfg . onlySessionId ? ` (filtered to ${ cfg . onlySessionId } )` : "" ) ) ;
320+ lastObservedSig = observedSig ;
321+ }
293322
294323 let queued = 0 ;
295324 for ( const s of candidates ) {
296325 const affinity = await getAffinity ( pool , s . sessionId ) ;
297326 const decision = decidePlacement ( s , affinity , nowMs ) ;
298327 if ( decision . action !== "queue-ado-run" ) {
299- log ( ` skip ${ s . sessionId } — ${ decision . action } : ${ decision . reason } ` ) ;
328+ const sig = `${ decision . action } :${ decision . reason } ` ;
329+ if ( VERBOSE || heartbeat || lastDecisionSig . get ( s . sessionId ) !== sig ) {
330+ log ( ` skip ${ s . sessionId } — ${ decision . action } : ${ decision . reason } ` ) ;
331+ }
332+ lastDecisionSig . set ( s . sessionId , sig ) ;
300333 continue ;
301334 }
302335 if ( queued >= cfg . maxQueuePerPass ) {
@@ -334,6 +367,7 @@ try {
334367 log ( `ADO: org=${ cfg . adoOrg } project="${ cfg . adoProject } " pipeline=${ cfg . pipelineId } ref=${ cfg . branchRef } pilotswarmRef=${ cfg . pilotswarmRef } ` ) ;
335368 log ( `store: ${ cfg . host } /${ cfg . database } user=${ cfg . user } mi=${ cfg . useMi } cmsSchema=${ cfg . cmsSchema } affinitySchema=${ cfg . affinitySchema } ` ) ;
336369 log ( `loop: pollMs=${ cfg . pollMs } once=${ cfg . once } dryRun=${ cfg . dryRun } maxQueue/pass=${ cfg . maxQueuePerPass } targetStatus=${ cfg . targetStatus } retryOnLeaseExpiry=${ cfg . retryOnLeaseExpiry } ` ) ;
370+ log ( `logging: verbose=${ VERBOSE } heartbeatMs=${ HEARTBEAT_MS } (change-only per-session logging; set SCHED_VERBOSE=1 for per-pass)` ) ;
337371
338372 // Load the ADO PAT from Key Vault (workload identity) before any queue call.
339373 await loadAdoPatFromKeyVault ( ) ;
0 commit comments