Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions packages/client-runtime/src/connection/supervisor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,43 @@ describe("EnvironmentSupervisor", () => {
}).pipe(Effect.provide(TestClock.layer())),
);

it.effect("resets the tolerant budget across a network drop", () =>
Effect.gen(function* () {
// A slow timeout arms the tolerant 45s budget. A network drop is not
// backend slowness, so the reconnect after the network returns must start
// fresh on the normal 15s budget rather than carry the enlarged one.
const harness = yield* makeHarness({ ready: () => Effect.never });
const supervisor = yield* EnvironmentSupervisor.make(TARGET_ENTRY, {
initiallyDesired: true,
}).pipe(Effect.provide(harness.dependencies));

yield* awaitState(
supervisor.state,
(state) =>
state.phase === "connecting" && state.stage === "synchronizing" && state.attempt === 1,
);
yield* TestClock.adjust("15 seconds");
yield* eventuallyState(
supervisor.state,
(state) => state.phase === "backoff" && state.attempt === 1,
);

// Network blips offline (which resets the tolerant classification) then back.
yield* harness.setNetworkStatus("offline");
yield* awaitState(supervisor.state, (state) => state.phase === "offline");
yield* harness.setNetworkStatus("online");

// The reconnect must time out at the normal 15s budget — a leaked tolerant
// flag would keep it "connecting" until 45s.
yield* awaitState(
supervisor.state,
(state) => state.phase === "connecting" && state.stage === "synchronizing",
);
yield* TestClock.adjust("15 seconds");
yield* eventuallyState(supervisor.state, (state) => state.phase === "backoff");
}).pipe(Effect.provide(TestClock.layer())),
);

it.effect("reconnects a relay session on a credentials change during a background probe", () =>
Effect.gen(function* () {
// The first session's heartbeat probe stalls; a credentials change that
Expand Down
6 changes: 6 additions & 0 deletions packages/client-runtime/src/connection/supervisor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,12 @@ export const make = Effect.fn("EnvironmentSupervisor.make")(function* (
continue;
}
if (currentIntent.network === "offline") {
// A network drop is not backend slowness — don't carry a "slow" budget
// across the outage, so the first attempt after the network returns
// starts fresh on the normal establishment budget. (failureCount is kept
// so backoff continuity survives a transient blip.)
tolerant = false;
consecutiveSlowTimeouts = 0;
yield* clearLease;
yield* setState(offlineState(currentIntent, generation, failureCount + 1, latestFailure));
yield* waitForSignal;
Expand Down
Loading