Skip to content

Address two bugs that prevent containers from sleeping - #251

Open
connyay wants to merge 4 commits into
cloudflare:mainfrom
connyay:cjh-never-sleep
Open

Address two bugs that prevent containers from sleeping#251
connyay wants to merge 4 commits into
cloudflare:mainfrom
connyay:cjh-never-sleep

Conversation

@connyay

@connyay connyay commented Sep 4, 2026

Copy link
Copy Markdown

Fixes #241 and #242

I personally hit #242 while testing a containers deployment. This is a nasty bug that can cause unexpected bills.

containerFetch held the count until the response body finished piping.
If nobody read the body it stalled on backpressure, the counter never
got back to zero, and every alarm renewed sleepAfter. Decrement as soon
as the container responds instead, and let bytes moving through the body
renew the activity timeout. A stream someone is actually reading still
keeps the container awake.

Fixes cloudflare#242
containerFetch counted a request as in flight until the proxied fetch
settled. If the client hung up while the container was still working,
that fetch could sit there forever. The count stayed above zero, every
alarm renewed sleepAfter, and the container never slept.

Listen on the request's abort signal too and release the count from
there. An abort can race the container's own answer, so the settle-once
guard WebSockets already used now fronts every release path: whichever
fires first wins, the other is a no-op. The runtime only fires abort on
an incoming request's signal when the enable_request_signal
compatibility flag is on.

Fixes cloudflare#241
@connyay
connyay requested a review from a team as a code owner September 4, 2026 15:57
@connyay
connyay marked this pull request as draft September 4, 2026 16:08
Comment thread src/lib/container.ts Outdated
if (res.body !== null) {
return new Response(
res.body.pipeThrough(
new TransformStream({

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a performance regression of #214

looking for alternative fixes

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yuck. not my favorite thing: e7fdf68

…rmStream

Renewing the activity timeout as a body flows means the body has to
pass through JavaScript. A TransformStream with a transform callback is
the most expensive way to do that, because the runtime hands it 4KB at
a time. A 32MB response costs about a second of CPU that way. The
native IdentityTransformStream pipe moves the same body in 18ms.

Read the container body with a BYOB reader into one reused buffer,
sized to Content-Length when it is known, and write into an
IdentityTransformStream that the runtime pumps natively. Each trip
through JavaScript is then a memcpy and a promise, and the buffer size
decides how many trips a body takes. Measured against a service binding
under workerd, the same 32MB body takes 51ms with a known length and
80ms chunked, against 980ms with a TransformStream.
@connyay
connyay marked this pull request as ready for review September 4, 2026 16:58
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.

inflightRequests leaks on client-aborted proxied requests — container never stops and bills indefinitely

1 participant