Repository navigation
fix(id): reset DogStatsD and exporter state on refresh #10323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -151,10 +151,11 @@ class DogStatsDClient { | |
| * before the swap. | ||
| * | ||
| * @param {string[]} tags - DogStatsD-formatted tags (e.g. `['key:value']`) | ||
| * @returns {boolean} True if the tag prefix actually changed (and buffered lines were dropped) | ||
| */ | ||
| updateTags (tags) { | ||
| const tagsPrefix = tags.length ? `|#${tags.join(',')}` : '' | ||
| if (tagsPrefix === this.#tagsPrefix) return | ||
| if (tagsPrefix === this.#tagsPrefix) return false | ||
|
|
||
| this.#updateTagPrefixes(tagsPrefix) | ||
| this.#metrics.queue = [] | ||
|
|
@@ -167,6 +168,8 @@ class DogStatsDClient { | |
| payload.message = '' | ||
| payload.offset = 0 | ||
| } | ||
|
|
||
| return true | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -595,11 +598,16 @@ class MetricsAggregationClient { | |
| } | ||
|
|
||
| /** | ||
| * Recomputes the wrapped client's cached tags (e.g. after a MicroVM clone resume). | ||
| * Recomputes the wrapped client's cached tags (e.g. after a MicroVM clone resume). Pending | ||
| * counters/gauges/histograms were aggregated under the old identity, so they're reset along | ||
| * with the client's buffered lines — but only if the tags actually changed, so a no-op resume | ||
| * doesn't discard in-flight aggregation for nothing. | ||
| * @param {string[]} tags - DogStatsD-formatted tags (e.g. `['key:value']`) | ||
| */ | ||
| updateTags (tags) { | ||
| this._client.updateTags(tags) | ||
| if (this._client.updateTags(tags)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
MicroVM clones can send duplicate aggregate metrics from the snapshot. Assertion details
Was this helpful? React 👍 or 👎 |
||
| this.reset() | ||
| } | ||
|
Comment on lines
+608
to
+610
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,11 +3,19 @@ | |
| const { URL } = require('node:url') | ||
| const os = require('node:os') | ||
|
|
||
| const { channel } = require('dc-polyfill') | ||
|
|
||
| const log = require('../../log') | ||
| const { containerId } = require('../common/docker') | ||
| const Writer = require('./writer') | ||
| const { computeIntakeUrl } = require('./intake') | ||
|
|
||
| const identityRefreshChannel = channel('datadog:identity:refresh') | ||
|
|
||
| // Only one AgentlessExporter is ever live in a real process, so replacing the subscription on | ||
| // construction is safe - it just keeps tests (which build several) from piling up listeners. | ||
| let unsubscribeBatchReset = null | ||
|
|
||
| /** | ||
| * Agentless exporter for APM trace intake. | ||
| * Sends traces directly to the Datadog intake without requiring a local agent. | ||
|
|
@@ -52,6 +60,12 @@ class AgentlessExporter { | |
| metadata, | ||
| }) | ||
|
|
||
| // A clone resume shouldn't flush spans buffered before the snapshot under its own identity. | ||
| unsubscribeBatchReset?.() | ||
| const onIdentityRefresh = () => this._writer.resetPendingBatch() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a MicroVM uses the normal Agent exporter—for example through a local Agent or Lambda extension— AGENTS.md reference: AGENTS.md:L79-L79 Useful? React with 👍 / 👎. |
||
| identityRefreshChannel.subscribe(onIdentityRefresh) | ||
| unsubscribeBatchReset = () => identityRefreshChannel.unsubscribe(onIdentityRefresh) | ||
|
|
||
| const ddTrace = globalThis[Symbol.for('dd-trace')] | ||
| if (ddTrace?.beforeExitHandlers) { | ||
| ddTrace.beforeExitHandlers.add(this.flush.bind(this)) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,8 +1,10 @@ | ||||||
| 'use strict' | ||||||
|
|
||||||
| const os = require('node:os') | ||||||
| const pkg = require('../../../package.json') | ||||||
|
|
||||||
| const { channel } = require('dc-polyfill') | ||||||
|
|
||||||
| const pkg = require('../../../package.json') | ||||||
| const { LogCollapsingLowestDenseDDSketch } = require('../../../vendor/dist/@datadog/sketches-js') | ||||||
| const { | ||||||
| MEASURED, | ||||||
|
|
@@ -29,6 +31,12 @@ const { | |||||
| DEFAULT_SERVICE_NAME, | ||||||
| } = require('./encode/tags-processors') | ||||||
|
|
||||||
| const identityRefreshChannel = channel('datadog:identity:refresh') | ||||||
|
|
||||||
| // Only one SpanStatsProcessor is ever live in a real process, so replacing the subscription on | ||||||
| // construction is safe - it just keeps tests (which build several) from piling up listeners. | ||||||
| let unsubscribeBucketReset = null | ||||||
|
|
||||||
| class SpanAggStats { | ||||||
| constructor (aggKey) { | ||||||
| this.aggKey = aggKey | ||||||
|
|
@@ -229,6 +237,14 @@ class SpanStatsProcessor { | |||||
| this.timer = setInterval(this.onInterval.bind(this), intervalMs) | ||||||
| this.timer.unref?.() | ||||||
| } | ||||||
|
|
||||||
| // A clone resume shouldn't export buckets accumulated before the snapshot under its own identity. | ||||||
| unsubscribeBucketReset?.() | ||||||
| const onIdentityRefresh = () => { | ||||||
| this.buckets = new TimeBuckets() | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
OTLP span metrics become incorrect after the first identity refresh. Assertion details
Suggested change
Was this helpful? React 👍 or 👎 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When OTLP span statistics are enabled and the MicroVM identity-refresh channel fires, this replaces the original AGENTS.md reference: AGENTS.md:L79-L80 Useful? React with 👍 / 👎. |
||||||
| } | ||||||
| identityRefreshChannel.subscribe(onIdentityRefresh) | ||||||
| unsubscribeBucketReset = () => identityRefreshChannel.unsubscribe(onIdentityRefresh) | ||||||
| } | ||||||
|
|
||||||
| onInterval () { | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the refreshed tags do change, this method drops the encoded telemetry payload but leaves
telemetry.metricsByType,aggregatedContextsByType,bytesSent/bytesDropped, andpacketsSent/packetsDroppeduntouched. If the image is snapshotted before the telemetry interval flushes, every clone later reports those inherited pre-snapshot counts under the refreshed prefix, duplicating DogStatsD client telemetry even though the application metric buffers were reset; clear these interval accumulators at the same identity boundary.AGENTS.md reference: AGENTS.md:L79-L79
Useful? React with 👍 / 👎.