cloudflare / cloudflare/workerd
Proposal: Allow waitUntil to accept a function returning a Promise for better instrumentation support
- Dominant language
- C++
- Stars
- 8.7k
- Forks
- 739
- Avg merge
- 2d 20h
- Merged PRs (30d)
- 174
Description
### Problem
> FWIW there is no instrumentation just yet natively from Cloudflare for `waitUntil`
The current `waitUntil(promise: Promise)` signature makes it difficult to properly instrument background work with OpenTelemetry.
When passing a promise to `waitUntil`, the async context is captured at promise creation time:
```typescript
// Async context is already captured when the IIFE is invoked
ctx.waitUntil(doBackgroundWork());
```
When instrumenting code (especially `waitUntil`) for observability it could create a problem. By the time `waitUntil` is being instrumented, the promise already exists with its context "baked in". Child spans created inside the async function end up with the wrong parent.
Following shows a `waitUntil` function that starts another span right inside. Instead of having the "Subroutine" as children directly of `waitUntil` it uses `http.server` instead:
Pseudo code
```js
waitUntil(
(async () => {
// do something
otel.startSpan((span) => {
span.name = 'Subroutine';
// this span's parent is not `waitUntil`, but the `fetch` method
});
})()
)
```
### Proposal
Add an overload that accepts a function returning a Promise:
```typescript
interface ExecutionContext {
waitUntil(promise: Promise): void; // existing
waitUntil(fn: () => Promise): void; // new
}
```
**Usage:**
```typescript
ctx.waitUntil(doBackgroundWork);
// or
ctx.waitUntil(async () => {
await doBackgroundWork();
// do more
});
```
This would allow instrumentation wrappers to (and also Cloudflare in their native observability):
1. Intercept the `waitUntil` call
2. Establish the proper tracing context (create/activate a span)
3. Invoke the function **within** that context
4. All child spans would correctly inherit the `waitUntil` span as parent
Existing code using `waitUntil(promise)` continues to work unchanged. However, it would still be nice to only have one way in general and maybe move towards the new model.
Contributor guide
Research direction
Start at the ExecutionContext.waitUntil API and trace how its current Promise argument is handled. Compare the proposed function-returning-Promise form with the existing form, then identify the relevant runtime and type coverage needed to verify both remain supported and that instrumentation can establish the intended parent context.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend-api-design
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100