dd-trace 6.13.0 → 6.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +3 -1
- package/ci/init.js +8 -3
- package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
- package/ci/test-optimization-validation/ci-remediation.js +6 -6
- package/ci/test-optimization-validation/command-blocker.js +7 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
- package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
- package/ci/test-optimization-validation/package-check.js +1 -1
- package/ci/test-optimization-validation/report-writer.js +7 -5
- package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
- package/ci/vitest-no-worker-init-setup.mjs +13 -13
- package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
- package/index.d.ts +8 -0
- package/package.json +13 -10
- package/packages/datadog-esbuild/index.js +3 -1
- package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
- package/packages/datadog-instrumentations/src/fs.js +24 -20
- package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +16 -16
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +265 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +14 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +251 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +11 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
- package/packages/datadog-instrumentations/src/jest.js +166 -45
- package/packages/datadog-instrumentations/src/knex.js +76 -0
- package/packages/datadog-instrumentations/src/mariadb.js +6 -0
- package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +66 -20
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
- package/packages/datadog-instrumentations/src/net.js +14 -17
- package/packages/datadog-instrumentations/src/next.js +212 -62
- package/packages/datadog-instrumentations/src/openai.js +42 -40
- package/packages/datadog-instrumentations/src/pg.js +15 -0
- package/packages/datadog-instrumentations/src/playwright.js +113 -13
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
- package/packages/datadog-instrumentations/src/sequelize.js +94 -2
- package/packages/datadog-instrumentations/src/undici.js +53 -4
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +37 -3
- package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
- package/packages/datadog-instrumentations/src/vitest-worker.js +71 -14
- package/packages/datadog-instrumentations/src/webdriverio.js +965 -6
- package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/util.js +2 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
- package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
- package/packages/datadog-plugin-cucumber/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
- package/packages/datadog-plugin-graphql/src/execute.js +998 -196
- package/packages/datadog-plugin-graphql/src/index.js +27 -7
- package/packages/datadog-plugin-graphql/src/jit.js +136 -0
- package/packages/datadog-plugin-graphql/src/request.js +18 -27
- package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
- package/packages/datadog-plugin-graphql/src/utils.js +208 -120
- package/packages/datadog-plugin-graphql/src/validate.js +10 -23
- package/packages/datadog-plugin-jest/src/index.js +0 -6
- package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
- package/packages/datadog-plugin-mocha/src/index.js +403 -75
- package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
- package/packages/datadog-plugin-mysql/src/index.js +41 -15
- package/packages/datadog-plugin-net/src/tcp.js +3 -1
- package/packages/datadog-plugin-next/src/index.js +63 -29
- package/packages/datadog-plugin-playwright/src/index.js +167 -27
- package/packages/datadog-plugin-undici/src/index.js +27 -41
- package/packages/datadog-plugin-vitest/src/index.js +6 -12
- package/packages/dd-trace/src/aiguard/evaluation.js +2 -1
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
- package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
- package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
- package/packages/dd-trace/src/appsec/graphql.js +6 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +531 -95
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/lambda.js +44 -26
- package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +5 -173
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +394 -225
- package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
- package/packages/dd-trace/src/ci-visibility/final-flush.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
- package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -0
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +180 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
- package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/index.js +73 -13
- package/packages/dd-trace/src/config/parsers.js +11 -1
- package/packages/dd-trace/src/config/stable.js +3 -3
- package/packages/dd-trace/src/config/supported-configurations.json +22 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
- package/packages/dd-trace/src/debugger/config.js +12 -1
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +28 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -40
- package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
- package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
- package/packages/dd-trace/src/debugger/index.js +23 -3
- package/packages/dd-trace/src/dogstatsd.js +510 -76
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
- package/packages/dd-trace/src/encode/0.4.js +5 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
- package/packages/dd-trace/src/evp_proxy/direct.js +3 -25
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +24 -7
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
- package/packages/dd-trace/src/exporters/agentless/writer.js +103 -119
- package/packages/dd-trace/src/exporters/common/agents.js +28 -6
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
- package/packages/dd-trace/src/exporters/common/proxy.js +52 -0
- package/packages/dd-trace/src/exporters/common/request.js +42 -14
- package/packages/dd-trace/src/exporters/common/url.js +40 -1
- package/packages/dd-trace/src/exporters/common/writer.js +9 -7
- package/packages/dd-trace/src/flush.js +6 -3
- package/packages/dd-trace/src/id.js +14 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +2 -1
- package/packages/dd-trace/src/llmobs/index.js +32 -32
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
- package/packages/dd-trace/src/llmobs/util.js +0 -33
- package/packages/dd-trace/src/llmobs/writers/base.js +49 -32
- package/packages/dd-trace/src/log/index.js +7 -1
- package/packages/dd-trace/src/{ci-visibility/log-submission → log-submission}/log-submission-plugin.js +48 -19
- package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -24
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +15 -22
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +10 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +5 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +101 -20
- package/packages/dd-trace/src/payload-tagging/index.js +2 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
- package/packages/dd-trace/src/plugins/tracing.js +12 -10
- package/packages/dd-trace/src/plugins/util/ci.js +3 -1
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
- package/packages/dd-trace/src/plugins/util/test.js +83 -20
- package/packages/dd-trace/src/plugins/util/url.js +2 -2
- package/packages/dd-trace/src/plugins/util/web.js +4 -9
- package/packages/dd-trace/src/priority_sampler.js +4 -0
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/oom.js +7 -2
- package/packages/dd-trace/src/profiling/profiler.js +23 -13
- package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
- package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
- package/packages/dd-trace/src/proxy.js +75 -5
- package/packages/dd-trace/src/remote_config/index.js +319 -101
- package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
- package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -1
- package/packages/dd-trace/src/sampling_rule.js +39 -10
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +4 -8
- package/packages/dd-trace/src/serverless/vercel.js +53 -20
- package/packages/dd-trace/src/serverless.js +10 -3
- package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
- package/packages/dd-trace/src/span_processor.js +18 -3
- package/packages/dd-trace/src/span_stats.js +24 -28
- package/packages/dd-trace/src/startup-log.js +3 -2
- package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
- package/packages/dd-trace/src/telemetry/send-data.js +23 -20
- package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
- package/packages/dd-trace/src/tracer.js +4 -5
- package/packages/dd-trace/src/tracer_metadata.js +11 -3
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
- package/packages/dd-trace/src/encode/agentless-json.js +0 -210
|
@@ -9,7 +9,168 @@ const {
|
|
|
9
9
|
getRetryDelay,
|
|
10
10
|
isRetriableNetworkError,
|
|
11
11
|
} = require('../../exporters/common/retry')
|
|
12
|
+
const { FINAL_FLUSH_TIMEOUT_CODE } = require('../final-flush')
|
|
12
13
|
const { getRateLimitResetDelay } = require('../requests/rate-limit')
|
|
14
|
+
const { MAX_REQUEST_BUFFERED_BYTES } = require('./limits')
|
|
15
|
+
|
|
16
|
+
const BACKPRESSURE_RETRY_MS = 50
|
|
17
|
+
const REQUEST_BUFFER_FULL_CODE = 'ERR_DD_REQUEST_BUFFER_FULL'
|
|
18
|
+
|
|
19
|
+
let bufferedBytes = 0
|
|
20
|
+
let backpressureTimer
|
|
21
|
+
let backpressureTimerDue
|
|
22
|
+
let ownedBackpressureWaiters = 0
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {object} BackpressureWaiter
|
|
26
|
+
* @property {(attemptIndex: number) => void} attempt
|
|
27
|
+
* @property {number} attemptIndex
|
|
28
|
+
* @property {number} due
|
|
29
|
+
* @property {boolean} keepProcessAlive
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/** @type {Set<BackpressureWaiter>} */
|
|
33
|
+
const backpressureWaiters = new Set()
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Keeps the shared polling timer alive only while an owned request is waiting.
|
|
37
|
+
*
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
40
|
+
function updateBackpressureTimerRef () {
|
|
41
|
+
if (ownedBackpressureWaiters > 0) backpressureTimer.ref?.()
|
|
42
|
+
else backpressureTimer.unref?.()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Schedules the next shared backpressure poll.
|
|
47
|
+
*
|
|
48
|
+
* @param {number} due
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
51
|
+
function setBackpressureTimer (due) {
|
|
52
|
+
clearTimeout(backpressureTimer)
|
|
53
|
+
backpressureTimerDue = due
|
|
54
|
+
backpressureTimer = setTimeout(runBackpressureWaiters, Math.max(0, due - Date.now()))
|
|
55
|
+
updateBackpressureTimerRef()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Adds a request to the shared backpressure poll.
|
|
60
|
+
*
|
|
61
|
+
* @param {BackpressureWaiter} waiter
|
|
62
|
+
* @param {number} delay
|
|
63
|
+
* @param {number} attemptIndex
|
|
64
|
+
* @returns {void}
|
|
65
|
+
*/
|
|
66
|
+
function scheduleBackpressureRetry (waiter, delay, attemptIndex) {
|
|
67
|
+
waiter.attemptIndex = attemptIndex
|
|
68
|
+
waiter.due = Date.now() + delay
|
|
69
|
+
|
|
70
|
+
backpressureWaiters.add(waiter)
|
|
71
|
+
if (waiter.keepProcessAlive) ownedBackpressureWaiters++
|
|
72
|
+
|
|
73
|
+
if (backpressureTimer === undefined || waiter.due < backpressureTimerDue) {
|
|
74
|
+
setBackpressureTimer(waiter.due)
|
|
75
|
+
} else {
|
|
76
|
+
updateBackpressureTimerRef()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Removes a settled request from the shared backpressure poll.
|
|
82
|
+
*
|
|
83
|
+
* @param {BackpressureWaiter} waiter
|
|
84
|
+
* @returns {void}
|
|
85
|
+
*/
|
|
86
|
+
function removeBackpressureWaiter (waiter) {
|
|
87
|
+
if (!backpressureWaiters.delete(waiter)) return
|
|
88
|
+
if (waiter.keepProcessAlive) ownedBackpressureWaiters--
|
|
89
|
+
|
|
90
|
+
if (backpressureWaiters.size === 0) {
|
|
91
|
+
clearTimeout(backpressureTimer)
|
|
92
|
+
backpressureTimer = undefined
|
|
93
|
+
backpressureTimerDue = undefined
|
|
94
|
+
} else {
|
|
95
|
+
updateBackpressureTimerRef()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Retries every request whose shared backpressure delay has elapsed.
|
|
101
|
+
*
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
function runBackpressureWaiters () {
|
|
105
|
+
const now = Date.now()
|
|
106
|
+
const ready = []
|
|
107
|
+
backpressureTimer = undefined
|
|
108
|
+
backpressureTimerDue = undefined
|
|
109
|
+
|
|
110
|
+
for (const waiter of backpressureWaiters) {
|
|
111
|
+
if (waiter.due > now) continue
|
|
112
|
+
backpressureWaiters.delete(waiter)
|
|
113
|
+
if (waiter.keepProcessAlive) ownedBackpressureWaiters--
|
|
114
|
+
ready.push(waiter)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let nextDue = Infinity
|
|
118
|
+
for (const waiter of backpressureWaiters) {
|
|
119
|
+
if (waiter.due < nextDue) nextDue = waiter.due
|
|
120
|
+
}
|
|
121
|
+
if (nextDue !== Infinity) setBackpressureTimer(nextDue)
|
|
122
|
+
|
|
123
|
+
for (const waiter of ready) waiter.attempt(waiter.attemptIndex)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {Buffer|string|Array<Buffer|string>|(() => Readable)} data
|
|
128
|
+
* @returns {number}
|
|
129
|
+
*/
|
|
130
|
+
function getPayloadSize (data) {
|
|
131
|
+
// Factory-backed payloads are streamed by their custom transport and enforce their own concurrency limit.
|
|
132
|
+
if (typeof data === 'function') return 0
|
|
133
|
+
if (!Array.isArray(data)) return Buffer.byteLength(data)
|
|
134
|
+
|
|
135
|
+
let size = 0
|
|
136
|
+
for (const chunk of data) size += Buffer.byteLength(chunk)
|
|
137
|
+
return size
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @returns {Error & { code: string }}
|
|
142
|
+
*/
|
|
143
|
+
function createQueueFullError () {
|
|
144
|
+
const error = new Error('Test Optimization request queue reached its payload limit')
|
|
145
|
+
error.code = 'ERR_DD_TEST_OPTIMIZATION_QUEUE_FULL'
|
|
146
|
+
return error
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @returns {Error & { code: string }}
|
|
151
|
+
*/
|
|
152
|
+
function createBackpressureTimeoutError () {
|
|
153
|
+
const error = new Error('Test Optimization request remained blocked by exporter backpressure')
|
|
154
|
+
error.code = 'ERR_DD_TEST_OPTIMIZATION_BACKPRESSURE_TIMEOUT'
|
|
155
|
+
return error
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @returns {Error & { code: string }}
|
|
160
|
+
*/
|
|
161
|
+
function createRequestTimeoutError () {
|
|
162
|
+
const error = new Error('Test Optimization transport attempt timed out')
|
|
163
|
+
error.code = 'ERR_DD_TEST_OPTIMIZATION_REQUEST_TIMEOUT'
|
|
164
|
+
return error
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {number} statusCode
|
|
169
|
+
* @returns {boolean}
|
|
170
|
+
*/
|
|
171
|
+
function isRetriableHttpStatusCode (statusCode) {
|
|
172
|
+
return statusCode === 408 || statusCode === 429 || (statusCode >= 500 && statusCode <= 599)
|
|
173
|
+
}
|
|
13
174
|
|
|
14
175
|
/**
|
|
15
176
|
* @param {AbortSignal} signal
|
|
@@ -24,36 +185,57 @@ function getAbortError (signal) {
|
|
|
24
185
|
*
|
|
25
186
|
* @param {Readable} data
|
|
26
187
|
* @param {AbortSignal} [signal]
|
|
27
|
-
* @param {(error: Error|null, data?: Buffer) => void} callback
|
|
188
|
+
* @param {(error: Error|null, data?: Buffer, payloadSize?: number) => void} callback
|
|
28
189
|
* @returns {void}
|
|
29
190
|
*/
|
|
30
191
|
function bufferReadable (data, signal, callback) {
|
|
31
192
|
const chunks = []
|
|
193
|
+
let payloadSize = 0
|
|
32
194
|
let settled = false
|
|
33
195
|
|
|
196
|
+
const release = () => {
|
|
197
|
+
bufferedBytes -= payloadSize
|
|
198
|
+
payloadSize = 0
|
|
199
|
+
}
|
|
34
200
|
const cleanup = (keepErrorListener = false) => {
|
|
35
201
|
data.removeListener('data', onData)
|
|
36
202
|
data.removeListener('end', onEnd)
|
|
37
203
|
if (!keepErrorListener) data.removeListener('error', onError)
|
|
38
204
|
signal?.removeEventListener('abort', onAbort)
|
|
39
205
|
}
|
|
40
|
-
const onData = chunk =>
|
|
206
|
+
const onData = chunk => {
|
|
207
|
+
const chunkSize = Buffer.byteLength(chunk)
|
|
208
|
+
if (chunkSize > MAX_REQUEST_BUFFERED_BYTES - bufferedBytes) {
|
|
209
|
+
settled = true
|
|
210
|
+
cleanup(true)
|
|
211
|
+
release()
|
|
212
|
+
data.once('close', () => data.removeListener('error', onError))
|
|
213
|
+
data.destroy()
|
|
214
|
+
callback(createQueueFullError())
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
bufferedBytes += chunkSize
|
|
218
|
+
payloadSize += chunkSize
|
|
219
|
+
chunks.push(chunk)
|
|
220
|
+
}
|
|
41
221
|
const onEnd = () => {
|
|
42
222
|
if (settled) return
|
|
43
223
|
settled = true
|
|
44
224
|
cleanup()
|
|
45
|
-
callback(null,
|
|
225
|
+
callback(null, chunks, payloadSize)
|
|
46
226
|
}
|
|
47
227
|
const onError = error => {
|
|
48
228
|
if (settled) return
|
|
49
229
|
settled = true
|
|
50
230
|
cleanup()
|
|
231
|
+
release()
|
|
51
232
|
callback(error)
|
|
52
233
|
}
|
|
53
234
|
const onAbort = () => {
|
|
54
235
|
if (settled) return
|
|
55
236
|
settled = true
|
|
56
237
|
cleanup(true)
|
|
238
|
+
release()
|
|
57
239
|
data.once('close', () => data.removeListener('error', onError))
|
|
58
240
|
data.destroy()
|
|
59
241
|
callback(getAbortError(signal))
|
|
@@ -70,7 +252,7 @@ function bufferReadable (data, signal, callback) {
|
|
|
70
252
|
* Sends Test Optimization exporter data through the common single-attempt transport while
|
|
71
253
|
* keeping retry and finalization policy scoped to Test Optimization.
|
|
72
254
|
*
|
|
73
|
-
* @param {Buffer|string|Readable|Array<Buffer|string
|
|
255
|
+
* @param {Buffer|string|Readable|Array<Buffer|string>|(() => Readable)} data
|
|
74
256
|
* @param {object} options
|
|
75
257
|
* @param {(error: Error|null, result?: string|null, statusCode?: number,
|
|
76
258
|
* headers?: import('node:http').IncomingHttpHeaders) => void} callback
|
|
@@ -81,9 +263,9 @@ function request (data, options, callback) {
|
|
|
81
263
|
if (signal?.aborted) return callback(getAbortError(signal))
|
|
82
264
|
|
|
83
265
|
if (data instanceof Readable) {
|
|
84
|
-
bufferReadable(data, signal, (error, bufferedData) => {
|
|
266
|
+
bufferReadable(data, signal, (error, bufferedData, payloadSize) => {
|
|
85
267
|
if (error) return callback(error)
|
|
86
|
-
requestBuffered(bufferedData, options, callback)
|
|
268
|
+
requestBuffered(bufferedData, options, callback, payloadSize)
|
|
87
269
|
})
|
|
88
270
|
return
|
|
89
271
|
}
|
|
@@ -94,75 +276,180 @@ function request (data, options, callback) {
|
|
|
94
276
|
/**
|
|
95
277
|
* Applies the Test Optimization retry policy to replayable request data.
|
|
96
278
|
*
|
|
97
|
-
* @param {Buffer|string|Array<Buffer|string
|
|
279
|
+
* @param {Buffer|string|Array<Buffer|string>|(() => Readable)} data
|
|
98
280
|
* @param {object} options
|
|
99
281
|
* @param {(error: Error|null, result?: string|null, statusCode?: number,
|
|
100
282
|
* headers?: import('node:http').IncomingHttpHeaders) => void} callback
|
|
283
|
+
* @param {number} [reservedPayloadSize]
|
|
101
284
|
* @returns {void}
|
|
102
285
|
*/
|
|
103
|
-
function requestBuffered (data, options, callback) {
|
|
286
|
+
function requestBuffered (data, options, callback, reservedPayloadSize) {
|
|
104
287
|
const { signal } = options
|
|
288
|
+
const transport = options.transport || commonRequest
|
|
105
289
|
const timeout = options.timeout || 2000
|
|
290
|
+
const payloadSize = reservedPayloadSize ?? getPayloadSize(data)
|
|
106
291
|
let retryTimer
|
|
292
|
+
let attemptTimer
|
|
293
|
+
let attemptTimerImmediate
|
|
294
|
+
let attemptController
|
|
107
295
|
let settled = false
|
|
296
|
+
let lastError
|
|
297
|
+
let waitingForBackpressure = false
|
|
298
|
+
|
|
299
|
+
const backpressureWaiter = {
|
|
300
|
+
attempt,
|
|
301
|
+
attemptIndex: 1,
|
|
302
|
+
due: 0,
|
|
303
|
+
keepProcessAlive: options.keepProcessAlive === true,
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (reservedPayloadSize === undefined) {
|
|
307
|
+
if (payloadSize > MAX_REQUEST_BUFFERED_BYTES - bufferedBytes) {
|
|
308
|
+
callback(createQueueFullError())
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
bufferedBytes += payloadSize
|
|
312
|
+
}
|
|
108
313
|
|
|
109
314
|
const complete = (error, result, statusCode, headers) => {
|
|
110
315
|
if (settled) return
|
|
111
316
|
settled = true
|
|
112
317
|
clearTimeout(retryTimer)
|
|
318
|
+
clearTimeout(attemptTimer)
|
|
319
|
+
clearImmediate(attemptTimerImmediate)
|
|
320
|
+
removeBackpressureWaiter(backpressureWaiter)
|
|
113
321
|
signal?.removeEventListener('abort', onAbort)
|
|
322
|
+
bufferedBytes -= payloadSize
|
|
114
323
|
callback(error, result, statusCode, headers)
|
|
115
324
|
}
|
|
116
|
-
const onAbort = () =>
|
|
325
|
+
const onAbort = () => {
|
|
326
|
+
const abortError = getAbortError(signal)
|
|
327
|
+
const controller = attemptController
|
|
328
|
+
let error = lastError || abortError
|
|
329
|
+
|
|
330
|
+
if (abortError.code === FINAL_FLUSH_TIMEOUT_CODE) {
|
|
331
|
+
if (waitingForBackpressure) error = createBackpressureTimeoutError()
|
|
332
|
+
else if (controller) error = createRequestTimeoutError()
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
complete(error, null, error.status)
|
|
337
|
+
} finally {
|
|
338
|
+
controller?.abort(abortError)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
117
341
|
|
|
118
342
|
signal?.addEventListener('abort', onAbort, { once: true })
|
|
119
343
|
if (signal?.aborted) return onAbort()
|
|
120
344
|
|
|
121
|
-
/**
|
|
122
|
-
|
|
345
|
+
/**
|
|
346
|
+
* @param {number} attemptIndex
|
|
347
|
+
* @returns {void}
|
|
348
|
+
*/
|
|
349
|
+
function attempt (attemptIndex) {
|
|
123
350
|
if (settled) return
|
|
124
351
|
if (signal?.aborted) return onAbort()
|
|
125
352
|
|
|
126
353
|
const deadline = options.deadline
|
|
127
354
|
const remaining = deadline === undefined ? Infinity : deadline - Date.now()
|
|
128
355
|
if (remaining <= 0) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
356
|
+
if (waitingForBackpressure) {
|
|
357
|
+
complete(createBackpressureTimeoutError())
|
|
358
|
+
} else if (lastError) {
|
|
359
|
+
complete(lastError, null, lastError.status)
|
|
360
|
+
} else {
|
|
361
|
+
const error = new Error('Test Optimization request reached its finalization deadline')
|
|
362
|
+
error.code = FINAL_FLUSH_TIMEOUT_CODE
|
|
363
|
+
complete(error)
|
|
364
|
+
}
|
|
132
365
|
return
|
|
133
366
|
}
|
|
134
367
|
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
|
|
368
|
+
if (!transport.writable) {
|
|
369
|
+
waitingForBackpressure = true
|
|
370
|
+
scheduleBackpressureRetry(
|
|
371
|
+
backpressureWaiter,
|
|
372
|
+
Math.min(BACKPRESSURE_RETRY_MS, remaining),
|
|
373
|
+
attemptIndex
|
|
374
|
+
)
|
|
138
375
|
return
|
|
139
376
|
}
|
|
377
|
+
waitingForBackpressure = false
|
|
140
378
|
|
|
141
379
|
const attemptOptions = {
|
|
142
380
|
...options,
|
|
381
|
+
deferTimeoutAbort: true,
|
|
143
382
|
headers: options.headers ? { ...options.headers } : undefined,
|
|
144
383
|
retry: false,
|
|
145
384
|
}
|
|
385
|
+
delete attemptOptions.transport
|
|
146
386
|
if (deadline !== undefined) attemptOptions.timeout = Math.max(1, Math.min(timeout, remaining))
|
|
147
387
|
|
|
148
|
-
|
|
388
|
+
let attemptData
|
|
389
|
+
try {
|
|
390
|
+
attemptData = typeof data === 'function' ? data() : data
|
|
391
|
+
} catch (error) {
|
|
392
|
+
complete(error)
|
|
393
|
+
return
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const controller = new AbortController()
|
|
397
|
+
const attemptTimeout = attemptOptions.timeout || timeout
|
|
398
|
+
let attemptTimedOut = false
|
|
399
|
+
|
|
400
|
+
attemptController = controller
|
|
401
|
+
attemptOptions.signal = controller.signal
|
|
402
|
+
if (options.timeoutFromCreation !== false) {
|
|
403
|
+
attemptTimer = setTimeout(() => {
|
|
404
|
+
// Let a response that became ready while the event loop was blocked win before aborting it.
|
|
405
|
+
attemptTimerImmediate = setImmediate(() => {
|
|
406
|
+
if (settled || attemptController !== controller) return
|
|
407
|
+
attemptTimedOut = true
|
|
408
|
+
controller.abort(createRequestTimeoutError())
|
|
409
|
+
})
|
|
410
|
+
if (!options.keepProcessAlive) attemptTimerImmediate.unref?.()
|
|
411
|
+
}, attemptTimeout)
|
|
412
|
+
if (!options.keepProcessAlive) attemptTimer.unref?.()
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
transport(attemptData, attemptOptions, (error, result, statusCode, headers) => {
|
|
416
|
+
clearTimeout(attemptTimer)
|
|
417
|
+
clearImmediate(attemptTimerImmediate)
|
|
418
|
+
if (attemptController === controller) attemptController = undefined
|
|
149
419
|
if (settled) return
|
|
150
420
|
if (!error) {
|
|
151
421
|
complete(null, result, statusCode, headers)
|
|
152
422
|
return
|
|
153
423
|
}
|
|
154
424
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
425
|
+
if (error.code === REQUEST_BUFFER_FULL_CODE) {
|
|
426
|
+
waitingForBackpressure = true
|
|
427
|
+
scheduleBackpressureRetry(
|
|
428
|
+
backpressureWaiter,
|
|
429
|
+
Math.min(BACKPRESSURE_RETRY_MS, remaining),
|
|
430
|
+
attemptIndex
|
|
431
|
+
)
|
|
161
432
|
return
|
|
162
433
|
}
|
|
163
434
|
|
|
164
|
-
|
|
165
|
-
|
|
435
|
+
const requestError = attemptTimedOut ? createRequestTimeoutError() : error
|
|
436
|
+
lastError = requestError
|
|
437
|
+
|
|
438
|
+
const responseStatus = statusCode ?? error.status
|
|
439
|
+
const isUnknownNetworkError = responseStatus === undefined && error.code === undefined
|
|
440
|
+
const isRetriableError = error.retryable !== false &&
|
|
441
|
+
(attemptTimedOut || isRetriableNetworkError(error) || isUnknownNetworkError ||
|
|
442
|
+
isRetriableHttpStatusCode(responseStatus))
|
|
443
|
+
const retryUntilDeadline = options.deadline !== undefined && options.retryUntilDeadline !== false
|
|
444
|
+
const reachedAttemptLimit = !retryUntilDeadline && attemptIndex >= getMaxAttempts(attemptOptions)
|
|
445
|
+
const reachedUnknownNetworkAttemptLimit = isUnknownNetworkError && attemptIndex >= 2
|
|
446
|
+
if (
|
|
447
|
+
options.retry === false ||
|
|
448
|
+
!isRetriableError ||
|
|
449
|
+
reachedAttemptLimit ||
|
|
450
|
+
reachedUnknownNetworkAttemptLimit
|
|
451
|
+
) {
|
|
452
|
+
complete(requestError, result, statusCode, headers)
|
|
166
453
|
return
|
|
167
454
|
}
|
|
168
455
|
|
|
@@ -170,9 +457,14 @@ function requestBuffered (data, options, callback) {
|
|
|
170
457
|
if (responseStatus === 429) {
|
|
171
458
|
const resetDelay = getRateLimitResetDelay(headers)
|
|
172
459
|
if (Number.isFinite(resetDelay)) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
460
|
+
if (options.deadline !== undefined) {
|
|
461
|
+
const retryRemaining = options.deadline - Date.now()
|
|
462
|
+
if (resetDelay >= retryRemaining) {
|
|
463
|
+
complete(requestError, result, statusCode, headers)
|
|
464
|
+
return
|
|
465
|
+
}
|
|
466
|
+
} else if (resetDelay > RATE_LIMIT_MAX_WAIT_MS) {
|
|
467
|
+
complete(requestError, result, statusCode, headers)
|
|
176
468
|
return
|
|
177
469
|
}
|
|
178
470
|
retryDelay = resetDelay
|
|
@@ -183,7 +475,7 @@ function requestBuffered (data, options, callback) {
|
|
|
183
475
|
if (options.deadline !== undefined && retryDelay === undefined) {
|
|
184
476
|
const retryRemaining = options.deadline - Date.now()
|
|
185
477
|
if (retryRemaining <= 0) {
|
|
186
|
-
complete(
|
|
478
|
+
complete(requestError, result, statusCode, headers)
|
|
187
479
|
return
|
|
188
480
|
}
|
|
189
481
|
const retryAttemptTimeout = timeout < retryRemaining ? timeout : Math.ceil(retryRemaining / 2)
|
|
@@ -191,7 +483,7 @@ function requestBuffered (data, options, callback) {
|
|
|
191
483
|
}
|
|
192
484
|
|
|
193
485
|
retryTimer = setTimeout(attempt, delay, attemptIndex + 1)
|
|
194
|
-
retryTimer.unref?.()
|
|
486
|
+
if (!options.keepProcessAlive) retryTimer.unref?.()
|
|
195
487
|
})
|
|
196
488
|
}
|
|
197
489
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { version: tracerVersion } = require('../../../../../package.json')
|
|
4
|
+
const { buildCacheKey } = require('../requests/fs-cache')
|
|
5
|
+
const getConfig = require('../../config')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Builds the cross-process filesystem cache key for the settings request from the
|
|
9
|
+
* request configuration. The key also isolates the tracer version and local flags
|
|
10
|
+
* applied while parsing the response.
|
|
11
|
+
*
|
|
12
|
+
* @param {object} configuration - Request configuration for the settings endpoint.
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
function buildSettingsCacheKey (configuration) {
|
|
16
|
+
const config = getConfig()
|
|
17
|
+
const { testOptimization } = config
|
|
18
|
+
return buildCacheKey('settings', [
|
|
19
|
+
tracerVersion,
|
|
20
|
+
configuration.url?.href,
|
|
21
|
+
configuration.isEvpProxy,
|
|
22
|
+
configuration.evpProxyPrefix,
|
|
23
|
+
configuration.sha,
|
|
24
|
+
configuration.service,
|
|
25
|
+
configuration.env,
|
|
26
|
+
configuration.repositoryUrl,
|
|
27
|
+
configuration.branch,
|
|
28
|
+
configuration.tag,
|
|
29
|
+
configuration.testLevel,
|
|
30
|
+
configuration.osPlatform,
|
|
31
|
+
configuration.osVersion,
|
|
32
|
+
configuration.osArchitecture,
|
|
33
|
+
configuration.runtimeName,
|
|
34
|
+
configuration.runtimeVersion,
|
|
35
|
+
configuration.custom,
|
|
36
|
+
testOptimization.DD_CIVISIBILITY_DANGEROUSLY_FORCE_COVERAGE,
|
|
37
|
+
testOptimization.DD_CIVISIBILITY_DANGEROUSLY_FORCE_TEST_SKIPPING,
|
|
38
|
+
testOptimization.DD_CIVISIBILITY_CODE_COVERAGE_REPORT_UPLOAD_ENABLED,
|
|
39
|
+
])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = buildSettingsCacheKey
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const FINAL_FLUSH_TIMEOUT =
|
|
3
|
+
const FINAL_FLUSH_TIMEOUT = 60_000
|
|
4
4
|
const FINAL_FLUSH_FALLBACK_DELAY = 100
|
|
5
5
|
const FINAL_FLUSH_TIMEOUT_CODE = 'ERR_DD_TEST_OPTIMIZATION_FLUSH_TIMEOUT'
|
|
6
6
|
|
|
@@ -17,4 +17,5 @@ module.exports = {
|
|
|
17
17
|
createFinalFlushTimeoutError,
|
|
18
18
|
FINAL_FLUSH_FALLBACK_DELAY,
|
|
19
19
|
FINAL_FLUSH_TIMEOUT,
|
|
20
|
+
FINAL_FLUSH_TIMEOUT_CODE,
|
|
20
21
|
}
|