Skip to content

feat(capabilities)!: Add streaming to http capabilities - #2251

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 2 commits into
mainfrom
bob/streaming-http-capability
Jul 24, 2026
Merged

feat(capabilities)!: Add streaming to http capabilities#2251
gh-worker-dd-mergequeue-cf854d[bot] merged 2 commits into
mainfrom
bob/streaming-http-capability

Conversation

@bwoebi

@bwoebi bwoebi commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

This allows moving live-debugger sender impl to use http-capabilities.

@bwoebi
bwoebi requested review from a team as code owners July 17, 2026 16:52
@bwoebi bwoebi changed the title Add streaming to http capabilities feat(capabilities): Add streaming to http capabilities Jul 17, 2026
@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Jul 17, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 0.00%
Overall Coverage: 74.54% (-0.14%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 6acb322 | Docs | Datadog PR Page | Give us feedback!

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 503b5ad27e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread datadog-live-debugger/src/sender.rs Outdated
Comment on lines +438 to +439
let result = tokio::time::timeout(Duration::from_millis(self.timeout_ms), async {
let response = future.await??;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Abort the spawned request when the timeout fires

When the endpoint stalls after the body is sent, this timeout returns an error but the inner value being timed out is a JoinHandle; dropping a Tokio JoinHandle detaches the task rather than cancelling it. In that scenario each timed-out debugger upload leaves the spawned HTTP request running in the background, holding its task/connection until the server eventually responds, so repeated timeouts can accumulate stuck work. Abort the handle on timeout or avoid spawning so the request future is actually cancelled.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the Codex assessment is correct, even if the issue hasn't been introduced by the PR but seems to be pre-existing. I suppose we'd have to call abort on timeout to make sure the task doesn't run in the background.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, indeed. Had fixed it locally after codex complained, but forgot to commit.

Comment thread libdd-capabilities/src/http.rs Outdated
Comment on lines +55 to +58
pub type ResponseFuture =
Pin<Box<dyn Future<Output = Result<http::Response<bytes::Bytes>, HttpError>> + Send>>;

pub type BodySender = Box<dyn StreamingBodySender + Send>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve MaybeSend for streaming capabilities

On wasm clients the HTTP capability is expected to wrap JS futures that are not Send, but these new public streaming aliases require both the response future and body sender to be Send. That means any wasm WasmCapabilities implementation cannot use or override the new streaming API despite MaybeSend existing specifically to keep these capability traits portable; use the conditional MaybeSend bound here as with request().

Useful? React with 👍 / 👎.

@bwoebi
bwoebi force-pushed the bob/streaming-http-capability branch 2 times, most recently from de3528f to da204ee Compare July 17, 2026 17:13
@pr-commenter

pr-commenter Bot commented Jul 17, 2026

Copy link
Copy Markdown

Benchmarks

Comparison

Benchmark execution time: 2026-07-24 14:26:17

Comparing candidate commit 6acb322 in PR branch bob/streaming-http-capability with baseline commit 9f2ddb4 in branch main.

Found 16 performance improvements and 1 performance regressions! Performance is the same for 160 metrics, 10 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

scenario:alloc_free/sampled_system_fast_path/4096

  • 🟥 execution_time [+5.342ns; +5.501ns] or [+5.870%; +6.045%]

scenario:credit_card/is_card_number/ 378282246310005

  • 🟩 execution_time [-5.365µs; -5.287µs] or [-7.283%; -7.177%]
  • 🟩 throughput [+1050460.259op/s; +1065096.431op/s] or [+7.738%; +7.846%]

scenario:credit_card/is_card_number/378282246310005

  • 🟩 execution_time [-5.712µs; -5.623µs] or [-8.066%; -7.940%]
  • 🟩 throughput [+1219017.819op/s; +1236979.679op/s] or [+8.633%; +8.760%]

scenario:credit_card/is_card_number/37828224631000521389798

  • 🟩 execution_time [-7.428µs; -7.383µs] or [-13.968%; -13.882%]
  • 🟩 throughput [+3031920.726op/s; +3052078.201op/s] or [+16.125%; +16.232%]

scenario:credit_card/is_card_number_no_luhn/ 378282246310005

  • 🟩 execution_time [-4.973µs; -4.934µs] or [-8.508%; -8.440%]
  • 🟩 throughput [+1577725.665op/s; +1589683.119op/s] or [+9.223%; +9.293%]

scenario:credit_card/is_card_number_no_luhn/378282246310005

  • 🟩 execution_time [-5.337µs; -5.291µs] or [-9.599%; -9.516%]
  • 🟩 throughput [+1892872.594op/s; +1908136.267op/s] or [+10.524%; +10.609%]

scenario:credit_card/is_card_number_no_luhn/37828224631000521389798

  • 🟩 execution_time [-7.366µs; -7.307µs] or [-13.868%; -13.758%]
  • 🟩 throughput [+3005196.933op/s; +3030195.236op/s] or [+15.961%; +16.094%]

scenario:glob_matcher/ascii_case_insensitive_match/wall_time

  • 🟩 execution_time [-2.637ns; -2.578ns] or [-8.996%; -8.794%]

scenario:glob_matcher/ascii_exact_match/wall_time

  • 🟩 execution_time [-2.646ns; -2.613ns] or [-9.020%; -8.909%]

scenario:glob_matcher/ascii_exact_miss/wall_time

  • 🟩 execution_time [-2.059ns; -2.017ns] or [-15.070%; -14.761%]

scenario:sql/obfuscate_sql_string

  • 🟩 execution_time [-17.142µs; -16.871µs] or [-5.805%; -5.713%]

Candidate

Omitted due to size.

Baseline

Omitted due to size.

@dd-octo-sts

dd-octo-sts Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Artifact Size Benchmark Report

aarch64-alpine-linux-musl
Artifact Baseline Commit Change
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.so 8.13 MB 8.01 MB --1.53% (-128.00 KB) 💪
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.a 88.33 MB 87.12 MB --1.37% (-1.21 MB) 💪
aarch64-unknown-linux-gnu
Artifact Baseline Commit Change
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.a 99.62 MB 98.37 MB --1.25% (-1.24 MB) 💪
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.94 MB 10.77 MB --1.53% (-171.66 KB) 💪
libdatadog-x64-windows
Artifact Baseline Commit Change
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.dll 26.65 MB 25.99 MB --2.46% (-674.00 KB) 💪
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.lib 89.18 KB 89.18 KB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.pdb 191.80 MB 187.87 MB --2.04% (-3.92 MB) 💪
/libdatadog-x64-windows/debug/static/datadog_profiling_ffi.lib 1.04 GB 977.90 MB --8.67% (-92.88 MB) 💪
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.dll 8.68 MB 8.47 MB --2.41% (-215.00 KB) 💪
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.lib 89.18 KB 89.18 KB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.pdb 25.40 MB 25.05 MB --1.38% (-360.00 KB) 💪
/libdatadog-x64-windows/release/static/datadog_profiling_ffi.lib 50.65 MB 49.83 MB --1.61% (-838.79 KB) 💪
libdatadog-x86-windows
Artifact Baseline Commit Change
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.dll 23.21 MB 22.60 MB --2.62% (-624.50 KB) 💪
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.lib 90.58 KB 90.58 KB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.pdb 196.50 MB 192.45 MB --2.05% (-4.04 MB) 💪
/libdatadog-x86-windows/debug/static/datadog_profiling_ffi.lib 1.03 GB 966.88 MB --8.71% (-92.31 MB) 💪
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.dll 6.70 MB 6.54 MB --2.34% (-161.00 KB) 💪
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.lib 90.58 KB 90.58 KB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.pdb 27.32 MB 26.91 MB --1.51% (-424.00 KB) 💪
/libdatadog-x86-windows/release/static/datadog_profiling_ffi.lib 48.23 MB 47.42 MB --1.66% (-824.24 KB) 💪
x86_64-alpine-linux-musl
Artifact Baseline Commit Change
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.a 78.78 MB 77.73 MB --1.33% (-1.05 MB) 💪
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.so 9.07 MB 8.91 MB --1.76% (-164.00 KB) 💪
x86_64-unknown-linux-gnu
Artifact Baseline Commit Change
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.a 94.28 MB 93.20 MB --1.14% (-1.07 MB) 💪
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.so 11.04 MB 10.84 MB --1.76% (-199.43 KB) 💪

@bwoebi
bwoebi force-pushed the bob/streaming-http-capability branch from da204ee to bf5b07c Compare July 17, 2026 18:19

@yannham yannham left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I initially thought it could be separated out of the capabilities impl, but indeed implementing efficient streaming requires http-backend-specific tools. Plus the default impl makes it ok for WASM that doesn't have to implement it for now 👍

Other(anyhow::Error),
}

pub type ChunkFuture<'a> = Pin<Box<dyn MaybeSendFuture<Result<(), HttpError>> + 'a>>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we really need to spell this type out manually? I think this would be a use-case for async_trait that we use elsewhere in libdatadog (IIRC, the runtime result will be the same, but you just wouldn't have to write those types like ChunkFuture manually)

@bwoebi bwoebi Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can't get the MaybeSend into the async_trait though, right? async_trait forces Pin<Box<dyn Future<T> + Send + 'a>>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In fact async trait is probably not even needed. What I mean instead of defining ChunkFuture<'a> explicitly, couldn't you just define fn send_chunk(&mut self, data: bytes::Bytes) -> impl MaybeSendFuture<Result<(), HttpError>> + 'a? Maybe that doesn't work for some reason, but just trying to understand if spelling this type out is really needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, that's why async_trait exists in the first place, you can't do that on trait functions like that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, because StreamingBodySender needs to be returned and thus put in a box as a dyn StreamingBodySender, ack 👍 (because you can definitely use an impl in return position in a trait without async_trait, that's what HttpClientCapability does for request, but then you can't use as a dyn Trait anymore...)

Comment thread libdd-capabilities/src/http.rs
.await
.map_err(|e| HttpClientError::IoError(e.to_string()))?
.to_bytes();
let (status, headers, body_bytes) = tokio::time::timeout(timeout, async {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This change looks unrelated (or at least separate), since virtually no code is using libdd-http-client yet (beside Python is starting to do so). Is it to align with the behavior of NativeHttpClient? Also, shouldn't we also adapt the reqwest backend to behave the same?

@bwoebi bwoebi Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The reqwest backend already behaves this way, just that timeout is native to reqwest:

        if let Some(timeout) = request.timeout {
            builder = builder.timeout(timeout);
        }

This aligns it with reqwest, where the timeout is end-to-end.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fair enough!

Comment thread datadog-live-debugger/src/sender.rs Outdated
Comment on lines +438 to +439
let result = tokio::time::timeout(Duration::from_millis(self.timeout_ms), async {
let response = future.await??;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the Codex assessment is correct, even if the issue hasn't been introduced by the PR but seems to be pre-existing. I suppose we'd have to call abort on timeout to make sure the task doesn't run in the background.

Comment thread libdd-capabilities/src/maybe_send.rs
Other(anyhow::Error),
}

pub type ChunkFuture<'a> = Pin<Box<dyn MaybeSendFuture<Result<(), HttpError>> + 'a>>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's essentially a MaybeSendBoxFuture, right ? Do you think it'd be useful to have it in maybe_send.rs as MaybeSend and MaybeSendFuture ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't know... Let's add it once we have more usages? E.g. ChunkFuture and ResponseFuture are subtly different that the one has a lifetime and the other doesn't.

@bwoebi bwoebi changed the title feat(capabilities): Add streaming to http capabilities feat(capabilities)!: Add streaming to http capabilities Jul 20, 2026
bwoebi added 2 commits July 24, 2026 15:37
This allows moving live-debugger sender impl to use http-capabilities.

Signed-off-by: Bob Weinand <bob.weinand@datadoghq.com>

# Conflicts:
#	libdd-capabilities/src/lib.rs
Signed-off-by: Bob Weinand <bob.weinand@datadoghq.com>
@bwoebi
bwoebi force-pushed the bob/streaming-http-capability branch from aa3098b to 6acb322 Compare July 24, 2026 13:38
@bwoebi

bwoebi commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Jul 24, 2026

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-07-24 14:59:07 UTC ℹ️ Start processing command /merge


2026-07-24 14:59:12 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in main is approximately 1h (p90).


2026-07-24 15:44:17 UTC ℹ️ MergeQueue: This merge request was merged

@pr-shepherd-6ad11f

Copy link
Copy Markdown

PR Shepherd is now watching this PR: fixing basic CI failures, rebasing when it falls behind, and re-queueing after transient merge-queue failures. To disable it, add the pr-shepherd:ignore label.

If you have any questions, reach the team in #ai-devx-flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants