Skip to content

fix(deno): End the http.server span when Deno.serve has sent the response - #24933

Draft
JPeer264 wants to merge 1 commit into
jp/cloudflare-streaming-response-detectionfrom
jp/deno-serve-span-end-on-completed
Draft

JPeer264 wants to merge 1 commit into
jp/cloudflare-streaming-response-detectionfrom
jp/deno-serve-span-end-on-completed

Conversation

@JPeer264

@JPeer264 JPeer264 commented Oct 1, 2026

Copy link
Copy Markdown
Member

Streamed responses on Deno had the same problem as #22876. Deno.serve passes info.completed to the handler, a promise that settles when the response has been sent or could not be sent. Ending the http.server span on it keeps the span open for streamed responses of every content type, without a content-type classifier, and the SDK can return the handler's response unchanged.

Before, the SDK replaced every response that it classified as streaming with a new Response around a JS stream. For a string body, this skips Deno's static fast path and sends the body chunked instead of with a Content-Length. In a local benchmark with a 20 KB HTML string, the replaced response handled about 23% to 32% fewer requests per second. Reading completed had no measurable cost.

Details that are not obvious from the diff:

  • completed is read before the user handler runs. Deno creates the promise on first access and settles it on the next close of the request, and a WebSocket upgrade closes the request inside the handler.
  • completed rejects with Interrupted when the response could not be sent (docs: document that ServeHandlerInfo.completed can reject denoland/deno#35747), so the SDK handles the rejection and still ends the span. Deno 2.9.0 resolved it on a client disconnect in my tests.
  • For a buffered body, Deno settles completed when it takes the body, not when the client has received it, so these spans end at the same time as before.
  • Runtimes without completed keep the old stream wrapper. It is in the Deno runtime since 1.46 or earlier, and in the types since 2.0.
  • With --unstable-no-legacy-abort, reading completed makes Deno also abort request.signal when a response fails.

🤖 Generated with Claude Code

…onse

Deno.serve exposes `info.completed`, which settles when the response has
been sent or could not be sent. The span now ends on it, so it stays
open for streamed responses of every content type, and the SDK returns
the handler's response unchanged. Before, the SDK replaced responses it
classified as streaming, which moved string bodies off Deno's static
fast path and replaced Content-Length with chunked encoding.

`completed` is read before the handler runs, because Deno creates it on
first access and settles it on the next close of the request, which a
WebSocket upgrade does inside the handler. Runtimes without `completed`
keep the stream wrapper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JPeer264
JPeer264 added this pull request to stack #24934 October 1, 2026 11:32
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 29.24 kB - -
@sentry/browser - with treeshaking flags 27.5 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.4 kB - -
@sentry/browser (incl. Tracing) 51.15 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 51.18 kB - -
@sentry/browser (incl. Tracing, Profiling) 54.18 kB - -
@sentry/browser (incl. Tracing, Replay) 90.76 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 79.86 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 95.46 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 108.41 kB - -
@sentry/browser (incl. Feedback) 46.76 kB - -
@sentry/browser (incl. sendFeedback) 34.3 kB - -
@sentry/browser (incl. FeedbackAsync) 39.41 kB - -
@sentry/browser (incl. Metrics) 30.25 kB - -
@sentry/browser (incl. Logs) 30.53 kB - -
@sentry/browser (incl. Metrics & Logs) 31.2 kB - -
@sentry/react 31.08 kB - -
@sentry/react (incl. Tracing) 53.54 kB - -
@sentry/vue 36.78 kB - -
@sentry/vue (incl. Tracing) 53.74 kB - -
@sentry/svelte 29.26 kB - -
CDN Bundle 31.05 kB - -
CDN Bundle (incl. Tracing) 51.8 kB - -
CDN Bundle (incl. Logs, Metrics) 33.31 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 53.77 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 74.02 kB - -
CDN Bundle (incl. Tracing, Replay) 89.39 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 91.36 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 95.55 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 97.53 kB - -
CDN Bundle - uncompressed 91.7 kB - -
CDN Bundle (incl. Tracing) - uncompressed 154.08 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 98.27 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 160.04 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 227.84 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 273.81 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 279.75 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 287.51 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 293.44 kB - -
@sentry/nextjs (client) 55.78 kB - -
@sentry/sveltekit (client) 51.6 kB - -
@sentry/core/server 39.99 kB - -
@sentry/core/browser 13.63 kB - -
@sentry/node 144.03 kB +0.01% +8 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 82.98 kB - -
@sentry/node - without tracing 92.91 kB +0.01% +8 B 🔺
@sentry/node - without channel injection 122.41 kB +0.01% +9 B 🔺
@sentry/aws-serverless 101.17 kB -0.01% -10 B 🔽
@sentry/cloudflare (withSentry) - minified 206.65 kB -0.02% -39 B 🔽
@sentry/cloudflare (withSentry) 514.02 kB -0.03% -112 B 🔽

View base workflow run

@JPeer264

JPeer264 commented Oct 1, 2026

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 31904c7. Configure here.

This branch has not been deployed

No deployments
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