From ad1c0a7ac900e4dd4675dd9a24816bdb00abe87e Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Thu, 12 Feb 2026 23:07:10 +0100 Subject: [PATCH] test: add retry logic for eventloopdelay histogram sampling On some build configurations (e.g., sharedlibs), the histogram may not record valid samples (min > 0) after the initial spinning period. This adds a retry mechanism that will spin the event loop additional times if valid samples haven't been recorded yet. This helps ensure the test passes on slower or differently-configured systems where event loop delay sampling may take longer to produce measurable results. Refs: https://github.com/nodejs/reliability/issues/1461 --- .../test-performance-eventloopdelay.js | 107 ++++++++++-------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/test/sequential/test-performance-eventloopdelay.js b/test/sequential/test-performance-eventloopdelay.js index 72e6f7abfef3c2..62ac8a6f34c962 100644 --- a/test/sequential/test-performance-eventloopdelay.js +++ b/test/sequential/test-performance-eventloopdelay.js @@ -3,7 +3,6 @@ const common = require('../common'); const assert = require('assert'); -const os = require('os'); const { monitorEventLoopDelay, } = require('perf_hooks'); @@ -52,59 +51,77 @@ const { sleep } = require('internal/util'); } { - const s390x = os.arch() === 's390x'; const histogram = monitorEventLoopDelay({ resolution: 1 }); histogram.enable(); - let m = 5; - if (s390x) { - m = m * 2; + + // Check if histogram has recorded valid samples (min > 0 indicates real delays measured) + function hasValidSamples() { + return histogram.count > 0 && histogram.min > 0 && histogram.max > 0; } + + // Spin the event loop with blocking work to generate measurable delays. + // Some configurations (s390x, sharedlibs) need more iterations. + let spinsRemaining = 5; + const maxRetries = 3; + let retries = 0; + function spinAWhile() { sleep(1000); - if (--m > 0) { + if (--spinsRemaining > 0) { setTimeout(spinAWhile, common.platformTimeout(500)); } else { - // Give the histogram a chance to record final samples before disabling. - // This helps on slower systems where sampling may be delayed. - setImmediate(common.mustCall(() => { - histogram.disable(); - // The values are non-deterministic, so we just check that a value is - // present, as opposed to a specific value. - assert(histogram.count > 0, `Expected samples to be recorded, got count=${histogram.count}`); - assert(histogram.min > 0); - assert(histogram.max > 0); - assert(histogram.stddev > 0); - assert(histogram.mean > 0); - assert(histogram.percentiles.size > 0); - for (let n = 1; n < 100; n = n + 0.1) { - assert(histogram.percentile(n) >= 0); + // Give the histogram a chance to record final samples before checking. + setImmediate(() => { + // If we don't have valid samples yet, retry with more spinning. + // This handles slower configurations like sharedlibs builds. + if (!hasValidSamples() && retries < maxRetries) { + retries++; + spinsRemaining = 5; + setTimeout(spinAWhile, common.platformTimeout(500)); + return; } - histogram.reset(); - assert.strictEqual(histogram.min, 9223372036854776000); - assert.strictEqual(histogram.max, 0); - assert(Number.isNaN(histogram.stddev)); - assert(Number.isNaN(histogram.mean)); - assert.strictEqual(histogram.percentiles.size, 1); - ['a', false, {}, []].forEach((i) => { - assert.throws( - () => histogram.percentile(i), - { - name: 'TypeError', - code: 'ERR_INVALID_ARG_TYPE', - } - ); - }); - [-1, 0, 101, NaN].forEach((i) => { - assert.throws( - () => histogram.percentile(i), - { - name: 'RangeError', - code: 'ERR_OUT_OF_RANGE', - } - ); - }); - })); + // Wrap final assertions in mustCall to ensure they run + common.mustCall(() => { + histogram.disable(); + // The values are non-deterministic, so we just check that a value is + // present, as opposed to a specific value. + assert(histogram.count > 0, `Expected samples to be recorded, got count=${histogram.count}`); + assert(histogram.min > 0, `Expected min > 0, got ${histogram.min}`); + assert(histogram.max > 0); + assert(histogram.stddev > 0); + assert(histogram.mean > 0); + assert(histogram.percentiles.size > 0); + for (let n = 1; n < 100; n = n + 0.1) { + assert(histogram.percentile(n) >= 0); + } + histogram.reset(); + assert.strictEqual(histogram.min, 9223372036854776000); + assert.strictEqual(histogram.max, 0); + assert(Number.isNaN(histogram.stddev)); + assert(Number.isNaN(histogram.mean)); + assert.strictEqual(histogram.percentiles.size, 1); + + ['a', false, {}, []].forEach((i) => { + assert.throws( + () => histogram.percentile(i), + { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', + } + ); + }); + [-1, 0, 101, NaN].forEach((i) => { + assert.throws( + () => histogram.percentile(i), + { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + } + ); + }); + })(); + }); } } spinAWhile();