History

Deflake concurrent health-check coalescing tests (#1844)

Rhys Sullivan committed 2 months ago

Commit 9fa1e9f1 parent 35dba111 file · +40 −15Browse files

Unified · Split

packages/core/sdk/src/connections.test.ts+40−15
@@ -2489,8 +2489,13 @@ describe("agent read revalidation (coreTools connections.list)", () => { · 2488 unmodified lines
24892489 it.effect("concurrent lists past the freshness gate collapse to one probe", () =>
24902490 Effect.gen(function* () {
24912491 const gate = yield* Deferred.make<void>();
2492+ // Completed on probe entry, AFTER `counters.probes` has been bumped:
2493+ // awaiting it is the explicit "a probe has started" ordering point (a
2494+ // wall-clock sleep is a timing assumption that loses under suite load).
2495+ const entered = yield* Deferred.make<void>();
24922496 const { executor, counters, stamp, persisted } = yield* makeHealthHarness({
2493− probe: Deferred.await(gate).pipe(
2497+ probe: Deferred.succeed(entered, void 0).pipe(
2498+ Effect.andThen(Deferred.await(gate)),
24942499 Effect.map(() => ({
24952500 status: "healthy" as const,
24962501 checkedAt: Date.now(),
@@ -2508,11 +2513,12 @@ describe("agent read revalidation (coreTools connections.list)", () => { · 11 unmodified lines
25082513 { concurrency: "unbounded" },
25092514 ),
25102515 );
2511− // The probe is held open by the gate, so NOTHING has been persisted yet:
2512− // every one of the five lists must pass the freshness check and reach
2513− // the probe path. Give them real time to get there — the counter then
2514− // says how many probes actually started.
2515− yield* Effect.promise(() => new Promise((resolve) => setTimeout(resolve, 25)));
2516+ // Wait until the first probe has provably started. The gate still holds
2517+ // it open, so NOTHING has been persisted: every list that runs must pass
2518+ // the freshness check into the probe path, and the in-flight gate must
2519+ // keep the count at one — any duplicate a later list starts is caught by
2520+ // the final count below.
2521+ yield* Deferred.await(entered);
25162522 expect(counters.probes).toBe(1);
25172523
25182524 yield* Deferred.succeed(gate, void 0);
@@ -2977,6 +2983,12 @@ describe("credential-only health path", () => { · 458 unmodified lines
29772983 it.effect("concurrent checks collapse to one credential resolution", () =>
29782984 Effect.gen(function* () {
29792985 const gate = yield* Deferred.make<void>();
2986+ // Completed by the provider on entry, AFTER the resolve counter has
2987+ // been bumped: awaiting it is the explicit "a resolution has started"
2988+ // ordering point. A wall-clock sleep here is a timing assumption — under
2989+ // parallel suite load the forked checks may not have finished their row
2990+ // loads yet, and the counter reads 0.
2991+ const entered = yield* Deferred.make<void>();
29802992 const { executor, counters, stamp, persisted, hooks } = yield* makeHealthHarness();
29812993 // No declared probe spec + an OAuth client on the row routes checkHealth
29822994 // down the credential-only path: the verdict is "the credential
@@ -2984,7 +2996,9 @@ describe("credential-only health path", () => { · 1 unmodified line
29842996 // behind the same in-flight gate as probing, so concurrent checks must
29852997 // collapse to ONE resolution.
29862998 yield* stamp({ oauth_client: "acme", expires_at: null });
2987− hooks.onResolve = Deferred.await(gate);
2999+ hooks.onResolve = Deferred.succeed(entered, void 0).pipe(
3000+ Effect.andThen(Deferred.await(gate)),
3001+ );
29883002 counters.resolves = 0;
29893003 const ref = { owner: "org", integration: INTEG, name: ConnectionName.make("main") } as const;
29903004
@@ -2993,10 +3007,12 @@ describe("credential-only health path", () => { · 2 unmodified lines
29933007 concurrency: "unbounded",
29943008 }),
29953009 );
2996− // The resolution is held open by the gate, so nothing has been
2997− // persisted: both checks must reach the credential path. Give them real
2998− // time to get there — the counter then says how many resolutions started.
2999− yield* Effect.promise(() => new Promise((resolve) => setTimeout(resolve, 25)));
3010+ // Wait until the first resolution has provably entered the provider.
3011+ // The gate still holds it open, so nothing has been persisted: while it
3012+ // holds, the in-flight gate must keep the count at exactly one — and any
3013+ // duplicate the second check ever starts is caught by the final count
3014+ // below.
3015+ yield* Deferred.await(entered);
30003016 expect(counters.resolves).toBe(1);
30013017
30023018 yield* Deferred.succeed(gate, void 0);
@@ -3056,6 +3072,11 @@ describe("health probe gate key integrity", () => { · 53 unmodified lines
30563072 Effect.gen(function* () {
30573073 const counters = { probes: 0 };
30583074 const gate = yield* Deferred.make<void>();
3075+ // Completed by the probe that brings the count to two: awaiting it is
3076+ // the explicit "both probes started" ordering point. A collided gate
3077+ // never completes it (the second check joins the first probe's Deferred
3078+ // instead of starting its own), which fails the test by timeout.
3079+ const bothEntered = yield* Deferred.make<void>();
30593080 // Every probe increments the shared counter and then parks on the gate,
30603081 // so both checks are provably in flight at once: nothing is persisted,
30613082 // and a collided gate would let the second check join the first probe's
@@ -3071,7 +3092,10 @@ describe("health probe gate key integrity", () => { · 9 unmodified lines
30713092 checkHealth: () =>
30723093 Effect.suspend(() => {
30733094 counters.probes += 1;
3074− return Deferred.await(gate).pipe(
3095+ const arrived =
3096+ counters.probes >= 2 ? Deferred.succeed(bothEntered, void 0) : Effect.void;
3097+ return arrived.pipe(
3098+ Effect.andThen(Deferred.await(gate)),
30753099 Effect.map(() => ({
30763100 status: "healthy" as const,
30773101 checkedAt: Date.now(),
@@ -3111,9 +3135,10 @@ describe("health probe gate key integrity", () => { · 33 unmodified lines
31113135
31123136 const checkA = yield* Effect.forkChild(executorA.connections.checkHealth(ref));
31133137 const checkB = yield* Effect.forkChild(executorB.connections.checkHealth(ref));
3114− // Give both fibers real time to reach the probe path while the gate
3115− // holds every probe open; the counter then says how many started.
3116− yield* Effect.promise(() => new Promise((resolve) => setTimeout(resolve, 25)));
3138+ // Wait until both probes have provably started; the gate holds every
3139+ // probe open, so both checks are in flight at once and nothing has been
3140+ // persisted.
3141+ yield* Deferred.await(bothEntered);
31173142 expect(counters.probes).toBe(2);
31183143
31193144 yield* Deferred.succeed(gate, void 0);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t