Skip to content

Add StatsConcentrator for time-bucketed span aggregation#2871

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 4 commits into
feature/client-side-statsfrom
kelvin.bui/css-stats-concentrator
May 11, 2026
Merged

Add StatsConcentrator for time-bucketed span aggregation#2871
gh-worker-dd-mergequeue-cf854d[bot] merged 4 commits into
feature/client-side-statsfrom
kelvin.bui/css-stats-concentrator

Conversation

@tienquocbui
Copy link
Copy Markdown
Member

@tienquocbui tienquocbui commented Apr 28, 2026

What and why?

Implements the StatsConcentrator, the core aggregation engine for client-side stats. This is the component that receives span snapshots from the onSpanFinished callback and groups them into time-bucketed stats (hit counts, error counts, duration totals) keyed by aggregation dimensions (service, operation, resource, HTTP status, span kind, etc.).

This PR also wires the concentrator into the existing ClientStatsFeature with periodic timer-based flushing and Flushable conformance for SDK teardown.

How?

New types (StatsConcentrator.swift):

  • StatsConcentrator: aggregates SpanSnapshots into 10-second time buckets, matching the Go reference implementation. Uses @ReadWriteLock for thread safety. Supports eligibility filtering (top-level, measured, or qualifying span kind), oldestTs tracking to prevent late-arriving spans from creating already-flushed buckets, and periodic + force flush.
  • AggregationKey: the set of dimensions by which spans are grouped (service, operation, resource, HTTP status, type, span kind, is_trace_root, peer tags hash, service source).
  • Trilean: matches the protobuf enum for is_trace_root (notSet / true / false).
  • GroupedStats, StatsBucket: internal mutable types for running counters within a bucket.
  • ExportedBucket, ExportedGroupedStats: immutable Encodable + Sendable structs for serialization.
  • fnv64a(): FNV-64a hash for peer tag aggregation keys, matching Go's tagsFnvHash.
  • stochasticRound(): converts floating-point counts to UInt64 (effectively truncation since weight is always 1.0 for now).

Wiring changes:

  • ClientStatsFeature now holds a StatsConcentrator, runs a 30-second flush timer, conforms to Flushable, and writes exported buckets to feature storage via eventWriteContext.
  • Trace.enableOrThrow() wires trace.tracer.onSpanFinished to stats.concentrator.add(snapshot) and passes configuration.dateProvider to ensure the stats pipeline shares the same clock as the tracer.
  • SpanSnapshot gains a type field (populated from tags["span.type"] or defaulting to "custom" in DDSpan.createSnapshot()).
  • SpanSnapshot.mockAny() / .mockWith() helpers added to TestUtilities.

Known limitations (to be addressed in subsequent PRs):

  • okSummary / errorSummary are Data() placeholders; DDSketch implementation is next.
  • StatsRequestBuilder still concatenates raw event bytes with application/json content type. Multi-bucket uploads will produce invalid JSON. The real MessagePack encoder replaces this in a later PR.
  • When Trace.Configuration.service is nil, snapshot.service resolves to "" instead of falling back to DatadogContext.service (which is only available asynchronously via eventWriteContext). This means stats may be grouped under an empty service name for default-config users. A proper fix requires resolving the core context service at snapshot creation or aggregation time, tracked for a follow-up PR.

Review checklist

  • Feature or bugfix MUST have appropriate tests (unit, integration)
  • Make sure each commit and the PR mention the Issue number or JIRA reference
  • Add CHANGELOG entry for user facing changes
  • Add Objective-C interface for public APIs
  • Run make api-surface when adding new APIs

@tienquocbui tienquocbui self-assigned this Apr 28, 2026
@tienquocbui
Copy link
Copy Markdown
Member Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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: c6a83a9e71

ℹ️ 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 DatadogTrace/Sources/Feature/StatsConcentrator.swift
Comment thread DatadogTrace/Sources/Feature/StatsConcentrator.swift Outdated
@tienquocbui
Copy link
Copy Markdown
Member Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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: 04ead6a61d

ℹ️ 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 DatadogTrace/Sources/Feature/ClientStatsFeature.swift
@tienquocbui tienquocbui marked this pull request as ready for review April 29, 2026 14:00
@tienquocbui tienquocbui requested review from a team as code owners April 29, 2026 14:00
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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: 04ead6a61d

ℹ️ 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 DatadogTrace/Sources/Feature/StatsConcentrator.swift
Comment thread DatadogTrace/Sources/DDSpan.swift
Comment thread DatadogTrace/Sources/DDSpan.swift
Comment thread DatadogTrace/Sources/Feature/ClientStatsFeature.swift
Comment thread DatadogTrace/Sources/Feature/StatsConcentrator.swift
Comment thread DatadogTrace/Sources/Feature/StatsConcentrator.swift Outdated
Comment thread DatadogTrace/Sources/Feature/StatsConcentrator.swift Outdated
Comment thread DatadogTrace/Sources/Feature/StatsConcentrator.swift
Comment thread DatadogTrace/Sources/Feature/StatsConcentrator.swift Outdated
Comment thread DatadogTrace/Sources/Feature/StatsConcentrator.swift Outdated
Comment thread DatadogTrace/Sources/Feature/StatsConcentrator.swift
Comment thread DatadogTrace/Sources/Trace.swift Outdated
Comment thread DatadogTrace/Sources/Feature/StatsConcentrator.swift Outdated
@tienquocbui tienquocbui force-pushed the kelvin.bui/css-stats-concentrator branch from 2119ad8 to af06d58 Compare May 4, 2026 10:23
@tienquocbui
Copy link
Copy Markdown
Member Author

@codex review

@tienquocbui tienquocbui requested review from arroz and ncreated May 4, 2026 10:24
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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: af06d58af8

ℹ️ 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 on lines +70 to +71
for bucket in exportedBuckets {
writer.write(value: bucket)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Batch buckets before writing stats events

When a flush returns more than one bucket, this loop writes each bucket as a separate storage event, but StatsRequestBuilder.request(for:) builds the upload body by raw-concatenating Event.data with no delimiter or array wrapper. A normal 30s flush can export multiple 10s buckets, so the request body becomes adjacent JSON objects like {...}{...}, which is not valid application/json and causes the whole stats upload for that batch to be rejected; write a single encoded stats payload/batch instead of one event per bucket.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@arroz arroz left a comment

Choose a reason for hiding this comment

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

Looks good! :shipit:

@tienquocbui tienquocbui force-pushed the kelvin.bui/css-stats-concentrator branch from af06d58 to 2a2b5cb Compare May 5, 2026 14:41
@tienquocbui
Copy link
Copy Markdown
Member Author

/merge

@gh-worker-devflow-routing-ef8351
Copy link
Copy Markdown

gh-worker-devflow-routing-ef8351 Bot commented May 7, 2026

View all feedbacks in Devflow UI.

2026-05-07 09:48:03 UTC ℹ️ Start processing command /merge


2026-05-07 09:48:07 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in feature/client-side-stats is approximately 1h (p90).


2026-05-07 10:04:48 UTCMergeQueue: The build pipeline contains failing jobs for this merge request

Build pipeline has failing jobs for a06bebf:

⚠️ Do NOT retry failed jobs directly (why?).

What to do next?

  • Investigate the failures and when ready, re-add your pull request to the queue!
  • If your PR checks are green, try to rebase/merge. It might be because the CI run is a bit old.
  • Any question, go check the FAQ.
Details

Since those jobs are not marked as being allowed to fail, the pipeline will most likely fail.
Therefore, and to allow other builds to be processed, this merge request has been rejected and the pipeline got canceled.

@tienquocbui
Copy link
Copy Markdown
Member Author

/merge

@gh-worker-devflow-routing-ef8351
Copy link
Copy Markdown

gh-worker-devflow-routing-ef8351 Bot commented May 9, 2026

View all feedbacks in Devflow UI.

2026-05-09 14:54:13 UTC ℹ️ Start processing command /merge


2026-05-09 14:54:19 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in feature/client-side-stats is approximately 1h (p90).


2026-05-09 15:09:56 UTCMergeQueue: The build pipeline contains failing jobs for this merge request

Build pipeline has failing jobs for 9ed7ead:

⚠️ Do NOT retry failed jobs directly (why?).

What to do next?

  • Investigate the failures and when ready, re-add your pull request to the queue!
  • If your PR checks are green, try to rebase/merge. It might be because the CI run is a bit old.
  • Any question, go check the FAQ.
Details

Since those jobs are not marked as being allowed to fail, the pipeline will most likely fail.
Therefore, and to allow other builds to be processed, this merge request has been rejected and the pipeline got canceled.

- Replace @ReadWriteLock with serial DispatchQueue so add() never
  blocks the caller and flush() drains pending work synchronously
- Move fnv64a and stochasticRound into StatsUtils enum namespace
- Simplify flush cutoff using force ternary, document bufferLen
- Improve doc comments on flushStats(force:) and stochasticRound
- Use weak capture for stats in onSpanFinished closure
@tienquocbui tienquocbui force-pushed the kelvin.bui/css-stats-concentrator branch from 2a2b5cb to 3e58d7b Compare May 11, 2026 13:22
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot merged commit e061102 into feature/client-side-stats May 11, 2026
21 checks passed
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot deleted the kelvin.bui/css-stats-concentrator branch May 11, 2026 16:48
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