Skip to content

test(cloudflare): pin OTLP event-telemetry contracts; fix teardown-hook cleanup leaks - #1339

Merged
sam-goodwin merged 2 commits into
mainfrom
fix/otlp-event-flush-background
Aug 25, 2026
Merged

test(cloudflare): pin OTLP event-telemetry contracts; fix teardown-hook cleanup leaks#1339
sam-goodwin merged 2 commits into
mainfrom
fix/otlp-event-flush-background

Conversation

@sam-goodwin

@sam-goodwin sam-goodwin commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Resolves the investigation behind #1331 / #1332: the runtime bridges need no change — the existing waitUntil-deferred background export delivers every batch, verified empirically on local and deployed workerd for Worker fetch, DO fetch, and DO RPC events. The loss in #1331's repro was the test harness tearing workerd down before background work ran (#1341 fixes the Vitest adapter). This PR pins the telemetry contracts as tests, and fixes the same teardown-leak class in the two other test adapters.

Telemetry contract tests (no bridge changes)

environment Worker fetch DO fetch DO RPC
local workerd (dev sidecar, alive)
deployed workerd
  • Delivery — every event's OTLP batch reaches the collector while the runtime is alive. Locally via a 500ms-delayed 127.0.0.1 collector counting only fully-written responses; live via the DO-backed collector on the test zone (Telemetry.test.ts now also deploys the Worker→DO fixture and asserts the DO fetch and DO RPC event spans arrive from deployed workerd — state.waitUntil is a no-op in DOs, so this is the path Cloudflare event-scoped OTLP batches can be dropped during background scope close #1331 believed was broken).
  • Latency — an event's response never waits on its own telemetry export:
const completedAtResponse = collector.completedRequests.value;
expect(completedAtResponse).toBeLessThanOrEqual(1); // own batch still in flight

A foreground-flush design fails the latency bound; a lossy design fails the delivery poll.

Teardown-hook leak fixes (companion to #1341)

A failing teardown hook silently dropped later cleanup — leaking the shared scope and local provider sidecar for the rest of the test process. Each adapter had it with a different trigger; #1341 covers Vitest, this covers the other two:

  • alchemy-test runner: runAfterAll short-circuited on the first failing hook. Every afterAll now runs; failures aggregate. (beforeAll keeps short-circuit semantics — a failed setup invalidates what follows.)
  • Test/Bun: bun:test stops the afterAll chain once a hook throws (probed empirically; registration order and afterAll-after-failed-beforeAll are fine), skipping the microtask-registered fallback. User teardowns are now guarded:
const guardTeardown = (eff) => () =>
  runEff(eff).catch(async (error) => {
    await Effect.runPromise(closeAll); // idempotent
    throw error;                       // still fails the suite
  });

Both regression tests are two-sided and red against the previous code: the runner test asserts a second teardown's sentinel after a failing first; the Bun test spawns real bun test on a fixture using the public adapter and asserts a shared-scope finalizer sentinel after the throwing user teardown.

Builds on @dmmulroy's investigation and repro topology from #1331/#1332.

@sam-goodwin

Copy link
Copy Markdown
Contributor Author

cc @dmmulroy

@alchemy-version-bot

alchemy-version-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Install the packages built from this commit:

Alchemy

alchemy

bun add https://pkg.ing/alchemy/987fd5c

@alchemy.run/better-auth

bun add https://pkg.ing/@alchemy.run/better-auth/987fd5c

@alchemy.run/cloudflare-runtime

bun add https://pkg.ing/@alchemy.run/cloudflare-runtime/987fd5c

@alchemy.run/frontend-frameworks

bun add https://pkg.ing/@alchemy.run/frontend-frameworks/987fd5c

@alchemy.run/node-utils

bun add https://pkg.ing/@alchemy.run/node-utils/987fd5c

@alchemy.run/pr-package

bun add https://pkg.ing/@alchemy.run/pr-package/987fd5c

@alchemy.run/floci

bun add https://pkg.ing/@alchemy.run/floci/987fd5c

Distilled

@distilled.cloud/core

bun add https://pkg.ing/@distilled.cloud/core/dd2a322

@distilled.cloud/aws

bun add https://pkg.ing/@distilled.cloud/aws/dd2a322

@distilled.cloud/axiom

bun add https://pkg.ing/@distilled.cloud/axiom/dd2a322

@distilled.cloud/cloudflare

bun add https://pkg.ing/@distilled.cloud/cloudflare/dd2a322

@distilled.cloud/hetzner

bun add https://pkg.ing/@distilled.cloud/hetzner/dd2a322

@distilled.cloud/neon

bun add https://pkg.ing/@distilled.cloud/neon/dd2a322

@distilled.cloud/planetscale

bun add https://pkg.ing/@distilled.cloud/planetscale/dd2a322

@sam-goodwin
sam-goodwin force-pushed the fix/otlp-event-flush-background branch 2 times, most recently from 8533cd3 to 2772733 Compare August 24, 2026 22:50
@sam-goodwin sam-goodwin changed the title fix(cloudflare): flush event telemetry without delaying Worker responses fix(cloudflare): export Worker event telemetry in background, DO flush foreground Aug 24, 2026
…s Worker and DO event paths

Investigation of #1331 (with the corrected vitest-harness teardown
ordering) shows the existing waitUntil-deferred background export
delivers every batch, locally and on deployed workerd, for Worker fetch,
DO fetch, and DO RPC events — so no bridge change is needed. These tests
pin the two contracts that investigation relied on:

- delivery: every event's OTLP batch reaches the collector while the
  runtime is alive (local workerd via a delayed 127.0.0.1 collector;
  deployed workerd via the DO-backed collector on the test zone)
- latency: an event's response never waits on its own telemetry export

The local test discriminates: a foreground-flush design fails the
latency bound; a lossy design fails the delivery poll.

Co-Authored-By: Dillon Mulroy <dillon.mulroy@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sam-goodwin sam-goodwin changed the title fix(cloudflare): export Worker event telemetry in background, DO flush foreground test(cloudflare): pin OTLP event-telemetry delivery and latency across Worker and DO paths Aug 25, 2026
@sam-goodwin
sam-goodwin force-pushed the fix/otlp-event-flush-background branch from 426452d to b060679 Compare August 25, 2026 00:51
…r + Bun adapter)

Companion to #1341's Vitest aroundAll fix — the other two adapters had
the same class of leak, each with a different trigger:

- alchemy-test runner: runAfterAll short-circuited on the first failing
  hook, so a failing teardown assertion dropped every later afterAll —
  including Test.make's fallback that closes the shared scope and local
  provider sidecar. afterAll hooks now all run; failures aggregate.
- Test/Bun: bun:test stops the afterAll chain once a hook throws
  (verified empirically; registration order and afterAll-after-failed-
  beforeAll are fine), skipping the microtask-registered fallback. The
  adapter now guards user teardowns: on failure it runs the idempotent
  cleanup before rethrowing.

Both regression tests are red against the previous code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sam-goodwin sam-goodwin changed the title test(cloudflare): pin OTLP event-telemetry delivery and latency across Worker and DO paths test(cloudflare): pin OTLP event-telemetry contracts; fix teardown-hook cleanup leaks Aug 25, 2026
@sam-goodwin
sam-goodwin merged commit b5bbe33 into main Aug 25, 2026
7 checks passed
@sam-goodwin
sam-goodwin deleted the fix/otlp-event-flush-background branch August 25, 2026 02:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant