dd-trace 6.9.0 → 6.11.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.
Files changed (175) hide show
  1. package/README.md +0 -17
  2. package/ci/vitest-no-worker-init-setup.mjs +112 -10
  3. package/index.d.ts +150 -3
  4. package/initialize.mjs +28 -17
  5. package/openfeature.js +2 -2
  6. package/package.json +14 -10
  7. package/packages/datadog-esbuild/index.js +0 -34
  8. package/packages/datadog-instrumentations/src/anthropic.js +210 -15
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
  10. package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
  11. package/packages/datadog-instrumentations/src/bunyan.js +3 -16
  12. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
  13. package/packages/datadog-instrumentations/src/cucumber.js +26 -11
  14. package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
  15. package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
  16. package/packages/datadog-instrumentations/src/cypress.js +2 -0
  17. package/packages/datadog-instrumentations/src/fastify.js +2 -0
  18. package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
  20. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +634 -0
  21. package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
  26. package/packages/datadog-instrumentations/src/jest.js +26 -18
  27. package/packages/datadog-instrumentations/src/mariadb.js +278 -21
  28. package/packages/datadog-instrumentations/src/mocha/main.js +540 -56
  29. package/packages/datadog-instrumentations/src/mocha/utils.js +132 -25
  30. package/packages/datadog-instrumentations/src/mysql.js +45 -15
  31. package/packages/datadog-instrumentations/src/mysql2.js +119 -18
  32. package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
  33. package/packages/datadog-instrumentations/src/pg.js +116 -92
  34. package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
  35. package/packages/datadog-instrumentations/src/playwright.js +149 -2
  36. package/packages/datadog-instrumentations/src/router.js +2 -0
  37. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
  38. package/packages/datadog-instrumentations/src/vitest-main.js +180 -20
  39. package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
  40. package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
  41. package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
  44. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
  45. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -13
  46. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
  47. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  48. package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
  49. package/packages/datadog-plugin-bunyan/src/index.js +5 -8
  50. package/packages/datadog-plugin-cucumber/src/index.js +11 -0
  51. package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
  52. package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
  53. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +82 -30
  54. package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
  55. package/packages/datadog-plugin-cypress/src/index.js +12 -6
  56. package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
  57. package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
  58. package/packages/datadog-plugin-electron/src/index.js +2 -0
  59. package/packages/datadog-plugin-fs/src/index.js +1 -1
  60. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
  61. package/packages/datadog-plugin-jest/src/index.js +11 -4
  62. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
  63. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
  64. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
  65. package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
  66. package/packages/datadog-plugin-mocha/src/index.js +606 -40
  67. package/packages/datadog-plugin-mysql/src/index.js +37 -0
  68. package/packages/datadog-plugin-nats/src/index.js +1 -1
  69. package/packages/datadog-plugin-next/src/index.js +1 -19
  70. package/packages/datadog-plugin-openai/src/tracing.js +41 -30
  71. package/packages/datadog-plugin-openai-agents/src/integration.js +279 -69
  72. package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
  73. package/packages/datadog-plugin-pg/src/index.js +1 -0
  74. package/packages/datadog-plugin-playwright/src/index.js +42 -13
  75. package/packages/datadog-plugin-vitest/src/index.js +14 -20
  76. package/packages/datadog-plugin-ws/src/util.js +2 -1
  77. package/packages/datadog-webpack/index.js +0 -11
  78. package/packages/dd-trace/index.js +0 -1
  79. package/packages/dd-trace/src/agent/info.js +6 -5
  80. package/packages/dd-trace/src/aiguard/client.js +100 -1
  81. package/packages/dd-trace/src/aiguard/errors.js +41 -0
  82. package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
  83. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
  84. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
  85. package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
  86. package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
  87. package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
  88. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
  89. package/packages/dd-trace/src/aiguard/sdk.js +22 -278
  90. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
  91. package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
  92. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
  93. package/packages/dd-trace/src/carrier.js +356 -0
  94. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
  95. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
  96. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
  97. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
  100. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
  101. package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
  102. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
  103. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
  104. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
  105. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
  106. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
  107. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
  108. package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -0
  109. package/packages/dd-trace/src/config/index.js +13 -2
  110. package/packages/dd-trace/src/config/major-overrides.js +7 -0
  111. package/packages/dd-trace/src/config/supported-configurations.json +24 -0
  112. package/packages/dd-trace/src/datastreams/context.js +6 -1
  113. package/packages/dd-trace/src/datastreams/index.js +2 -0
  114. package/packages/dd-trace/src/datastreams/manager.js +14 -3
  115. package/packages/dd-trace/src/datastreams/pathway.js +23 -22
  116. package/packages/dd-trace/src/datastreams/processor.js +16 -13
  117. package/packages/dd-trace/src/datastreams/size.js +5 -0
  118. package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
  119. package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
  120. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
  121. package/packages/dd-trace/src/exporters/common/writer.js +11 -6
  122. package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
  123. package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
  124. package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
  125. package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
  126. package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
  127. package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
  128. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  129. package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
  130. package/packages/dd-trace/src/llmobs/index.js +34 -2
  131. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
  132. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
  133. package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
  134. package/packages/dd-trace/src/llmobs/tagger.js +96 -0
  135. package/packages/dd-trace/src/llmobs/util.js +78 -0
  136. package/packages/dd-trace/src/noop/proxy.js +3 -4
  137. package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
  138. package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
  139. package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
  140. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
  141. package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
  142. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
  143. package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
  144. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
  145. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
  146. package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
  147. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
  148. package/packages/dd-trace/src/opentracing/tracer.js +5 -2
  149. package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
  150. package/packages/dd-trace/src/plugin_manager.js +5 -3
  151. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  152. package/packages/dd-trace/src/plugins/index.js +1 -0
  153. package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +5 -65
  154. package/packages/dd-trace/src/plugins/util/test.js +93 -14
  155. package/packages/dd-trace/src/plugins/util/web.js +56 -2
  156. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  157. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  158. package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
  159. package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
  160. package/packages/dd-trace/src/proxy.js +38 -29
  161. package/packages/dd-trace/src/ritm.js +9 -2
  162. package/packages/dd-trace/src/serverless.js +37 -0
  163. package/packages/dd-trace/src/span_format.js +1 -0
  164. package/packages/dd-trace/src/span_stats.js +30 -3
  165. package/packages/dd-trace/src/storage-channels.js +86 -0
  166. package/packages/dd-trace/src/tracer.js +9 -2
  167. package/packages/dd-trace/src/tracer_metadata.js +14 -1
  168. package/packages/dd-trace/src/web-tags-cache.js +132 -0
  169. package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
  170. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
  171. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
  172. package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
  173. package/packages/dd-trace/src/feature-registry.js +0 -29
  174. package/packages/dd-trace/src/openfeature/register.js +0 -35
  175. package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
@@ -172,4 +172,85 @@ module.exports = [
172
172
  },
173
173
  channelName: 'testFrameworkFnWrapper',
174
174
  },
175
+ {
176
+ module: {
177
+ name: '@wdio/utils',
178
+ versionRange: '>=9.0.0',
179
+ filePath: 'build/index.js',
180
+ },
181
+ functionQuery: {
182
+ functionName: 'executeAsync',
183
+ kind: 'Async',
184
+ },
185
+ channelName: 'executeAsync',
186
+ },
187
+ {
188
+ module: {
189
+ name: '@wdio/utils',
190
+ versionRange: '>=9.0.0',
191
+ filePath: 'build/index.js',
192
+ },
193
+ astQuery: 'FunctionDeclaration[id.name="executeAsync"] CatchClause ' +
194
+ 'IfStatement[test.operator=">"][test.left.object.name="retries"]' +
195
+ '[test.left.property.name="limit"][test.right.object.name="retries"]' +
196
+ '[test.right.property.name="attempts"]',
197
+ channelName: 'executeAsync',
198
+ transform: 'awaitContextCallback',
199
+ transformOptions: {
200
+ callbackArgumentNames: ['err'],
201
+ callbackName: 'retryCallback',
202
+ },
203
+ },
204
+ {
205
+ module: {
206
+ name: 'jasmine-core',
207
+ versionRange: '>=5.0.0 <6.0.0',
208
+ filePath: 'lib/jasmine-core/jasmine.js',
209
+ },
210
+ astQuery: 'AssignmentExpression[left.object.object.name="Spec"]' +
211
+ '[left.object.property.name="prototype"][left.property.name="execute"] > FunctionExpression',
212
+ functionQuery: {
213
+ kind: 'Sync',
214
+ },
215
+ channelName: 'Spec_execute',
216
+ },
217
+ {
218
+ module: {
219
+ name: 'jasmine-core',
220
+ versionRange: '>=5.0.0 <6.0.0',
221
+ filePath: 'lib/jasmine-core/jasmine.js',
222
+ },
223
+ astQuery: 'AssignmentExpression[left.object.object.name="Spec"]' +
224
+ '[left.object.property.name="prototype"][left.property.name="status"] > FunctionExpression',
225
+ functionQuery: {
226
+ kind: 'Sync',
227
+ },
228
+ channelName: 'Spec_attemptDone',
229
+ },
230
+ {
231
+ module: {
232
+ name: 'jasmine-core',
233
+ versionRange: '>=5.0.0 <6.0.0',
234
+ filePath: 'lib/jasmine-core/jasmine.js',
235
+ },
236
+ functionQuery: {
237
+ className: 'Spec',
238
+ methodName: 'executionFinished',
239
+ kind: 'Sync',
240
+ },
241
+ channelName: 'Spec_attemptDone',
242
+ },
243
+ {
244
+ module: {
245
+ name: 'jasmine-core',
246
+ versionRange: '>=5.0.0 <6.0.0',
247
+ filePath: 'lib/jasmine-core/jasmine.js',
248
+ },
249
+ functionQuery: {
250
+ className: 'TreeRunner',
251
+ methodName: '_executeSpec',
252
+ kind: 'Sync',
253
+ },
254
+ channelName: 'Spec_execute',
255
+ },
175
256
  ]
@@ -54,6 +54,7 @@
54
54
  "graphql/utilities/index.mjs": "graphql",
55
55
  "graphql/validation/validate.js": "graphql",
56
56
  "graphql/validation/validate.mjs": "graphql",
57
+ "jasmine-core/lib/jasmine-core/jasmine.js": "jasmine-core",
57
58
  "mercurius/index.js": "mercurius",
58
59
  "playwright-core/lib/coreBundle.js": "playwright-core",
59
60
  "playwright/lib/index.js": "playwright",
@@ -14,7 +14,89 @@ const clone = require('../../../../../vendor/dist/rfdc')({ proto: false, circles
14
14
 
15
15
  const { parse, query } = require('./compiler')
16
16
 
17
- module.exports = { waitForAsyncEnd }
17
+ const functionTypes = new Set(['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression'])
18
+ const identifierPattern = /^[$A-Z_a-z][$\w]*$/
19
+
20
+ module.exports = { awaitContextCallback, waitForAsyncEnd }
21
+
22
+ /**
23
+ * Awaits an optional context callback before continuing through a matched conditional branch.
24
+ *
25
+ * The branch condition is checked again after the callback settles so the
26
+ * original body does not run against state that changed while awaiting.
27
+ *
28
+ * @param {{
29
+ * transformOptions?: {
30
+ * callbackArgumentNames?: string[],
31
+ * callbackName?: string
32
+ * }
33
+ * }} state
34
+ * @param {import('estree').IfStatement} node
35
+ * @param {import('estree').Node} _parent
36
+ * @param {import('estree').Node[]} ancestry
37
+ * @returns {void}
38
+ */
39
+ function awaitContextCallback (state, node, _parent, ancestry) {
40
+ assert(node.type === 'IfStatement' && node.consequent?.type === 'BlockStatement',
41
+ 'awaitContextCallback: expected an if statement with a block body')
42
+
43
+ const { callbackArgumentNames = [], callbackName } = state.transformOptions ?? {}
44
+
45
+ assert(identifierPattern.test(callbackName), 'awaitContextCallback: callbackName must be an identifier')
46
+ assert(callbackArgumentNames.every(name => identifierPattern.test(name)),
47
+ 'awaitContextCallback: callbackArgumentNames must be identifiers')
48
+
49
+ let enclosingFunction
50
+ let hasTraceWrapper = false
51
+ for (const ancestor of ancestry) {
52
+ if (!functionTypes.has(ancestor.type)) continue
53
+
54
+ enclosingFunction ??= ancestor
55
+ if (ancestor.body?.type !== 'BlockStatement') continue
56
+
57
+ let hasContextBinding = false
58
+ let hasTracedBinding = false
59
+ for (const statement of ancestor.body.body) {
60
+ if (statement.type !== 'VariableDeclaration') continue
61
+
62
+ for (const declaration of statement.declarations) {
63
+ hasContextBinding ||= declaration.id?.name === '__apm$ctx'
64
+ hasTracedBinding ||= declaration.id?.name === '__apm$traced'
65
+ }
66
+ }
67
+ hasTraceWrapper ||= hasContextBinding && hasTracedBinding
68
+ }
69
+
70
+ assert(enclosingFunction?.async, 'awaitContextCallback: expected an enclosing async function')
71
+ assert(hasTraceWrapper, 'awaitContextCallback: expected an enclosing trace wrapper')
72
+
73
+ const callbackVariable = `__apm$${callbackName}`
74
+ if (query(node, `[id.name="${callbackVariable}"]`).length > 0) {
75
+ return
76
+ }
77
+
78
+ const originalStatements = node.consequent.body
79
+ const callbackStatements = parse(`
80
+ async function wrapper () {
81
+ const ${callbackVariable} = __apm$ctx.${callbackName};
82
+ if (typeof ${callbackVariable} === 'function') {
83
+ try {
84
+ await ${callbackVariable}(${callbackArgumentNames.join(', ')});
85
+ } catch {}
86
+ if (true) {}
87
+ } else {
88
+ }
89
+ }
90
+ `).body[0].body.body
91
+
92
+ const callbackBranch = callbackStatements[1]
93
+ const recheckedBranch = callbackBranch.consequent.body[1]
94
+ recheckedBranch.test = clone(node.test)
95
+ recheckedBranch.consequent.body.push(...clone(originalStatements))
96
+ recheckedBranch.alternate = clone(node.alternate)
97
+ callbackBranch.alternate.body.push(...originalStatements)
98
+ node.consequent.body = callbackStatements
99
+ }
18
100
 
19
101
  /**
20
102
  * Injects settlement-specific asyncEnd waits into a generated `tracePromise`
@@ -49,7 +49,7 @@ const {
49
49
  getRawJestTestName,
50
50
  getJestSuitesToRun,
51
51
  removeSeedSuffixFromTestName,
52
- } = require('../../datadog-plugin-jest/src/util')
52
+ } = require('../../dd-trace/src/plugins/util/jest')
53
53
  const {
54
54
  addCoverageBackfillUntestedFiles,
55
55
  getCoverageBackfillFiles,
@@ -97,7 +97,9 @@ const itrSkippedSuitesCh = channel('ci:jest:itr:skipped-suites')
97
97
  const CHILD_MESSAGE_CALL = 1
98
98
 
99
99
  // Maximum time we'll wait for the tracer to flush
100
- const FLUSH_TIMEOUT = 10_000
100
+ // The exporter has a 10-second bounded final-flush deadline. Leave enough time
101
+ // for its completion callback before Jest's --forceExit fallback takes over.
102
+ const FLUSH_TIMEOUT = 12_000
101
103
  const JEST_SESSION_STATE = Symbol.for('dd-trace:jest:session')
102
104
  const JEST_BAIL_REPORTER_PATH = require.resolve('./jest/bail-reporter')
103
105
  const DD_JEST_HANDLE_TEST_EVENT_WRAPPED = Symbol('dd-trace:jest:handle-test-event-wrapped')
@@ -110,7 +112,7 @@ const jestSessionState = (globalThis[JEST_SESSION_STATE] ||= {})
110
112
  const RETRY_TIMES = Symbol.for('RETRY_TIMES')
111
113
 
112
114
  let skippableSuites = []
113
- let skippableSuitesCoverage = {}
115
+ let skippableSuitesCoverage
114
116
  let skippedSuitesCoverage = {}
115
117
  let knownTests = {}
116
118
  let isCodeCoverageEnabled = false
@@ -131,7 +133,7 @@ let isTestManagementTestsEnabled = false
131
133
  let testManagementTests = {}
132
134
  let testManagementAttemptToFixRetries = 0
133
135
  let isImpactedTestsEnabled = false
134
- let modifiedFiles = {}
136
+ let modifiedFiles
135
137
  let repositoryRoot
136
138
  let lastCoverageMap
137
139
  let lastCoverageMapRootDir
@@ -688,8 +690,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
688
690
 
689
691
  if (this.isImpactedTestsEnabled) {
690
692
  try {
691
- const hasImpactedTests = Object.keys(modifiedFiles).length > 0
692
- this.modifiedFiles = hasImpactedTests ? modifiedFiles : this.testEnvironmentOptions._ddModifiedFiles
693
+ this.modifiedFiles = modifiedFiles ?? this.testEnvironmentOptions._ddModifiedFiles
693
694
  } catch (e) {
694
695
  log.error('Error parsing impacted tests', e)
695
696
  this.isImpactedTestsEnabled = false
@@ -2489,12 +2490,6 @@ function getRepositoryRootFromTest (test, fallbackRootDir) {
2489
2490
  return getRepositoryRootFromConfig(test?.context?.config, fallbackRootDir)
2490
2491
  }
2491
2492
 
2492
- function hasSkippableSuitesCoverage () {
2493
- return skippableSuitesCoverage &&
2494
- typeof skippableSuitesCoverage === 'object' &&
2495
- Object.keys(skippableSuitesCoverage).length > 0
2496
- }
2497
-
2498
2493
  function shouldCollectJestCoverageForTia () {
2499
2494
  return shouldReportJestSuiteCoverageForTia() ||
2500
2495
  (isJestCoverageBackfillSupported && isItrEnabled && isCoverageReportUploadEnabled)
@@ -2605,7 +2600,7 @@ function resetLibraryConfiguration () {
2605
2600
  testManagementTests = {}
2606
2601
  testManagementAttemptToFixRetries = 0
2607
2602
  isImpactedTestsEnabled = false
2608
- modifiedFiles = {}
2603
+ modifiedFiles = undefined
2609
2604
  repositoryRoot = undefined
2610
2605
  }
2611
2606
 
@@ -2627,13 +2622,14 @@ function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
2627
2622
 
2628
2623
  isSuitesSkipped ||= jestSuitesToRun.suitesToRun.length !== originalTests.length
2629
2624
  numSkippedSuites += jestSuitesToRun.skippedSuites.length
2630
- skippedSuitesCoverage = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage()
2625
+ const hasSkippableSuitesCoverage = skippableSuitesCoverage !== undefined
2626
+ skippedSuitesCoverage = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage
2631
2627
  ? skippableSuitesCoverage
2632
2628
  : {}
2633
2629
  coverageBackfillContexts = isSuitesSkipped && isTiaCoverageBackfillEnabled()
2634
2630
  ? getTestContexts(originalTests)
2635
2631
  : undefined
2636
- coverageBackfillFiles = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage()
2632
+ coverageBackfillFiles = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage
2637
2633
  ? getCoverageBackfillFiles(skippableSuitesCoverage, repositoryRoot, getTestSuitePath)
2638
2634
  : undefined
2639
2635
 
@@ -3022,10 +3018,10 @@ function getCliWrapper (isNewJestVersion) {
3022
3018
  skippableSuitesCoverage: receivedSkippableSuitesCoverage,
3023
3019
  } = skippableSuitesResponse || await getChannelPromise(skippableSuitesCh)
3024
3020
  if (err) {
3025
- skippableSuitesCoverage = {}
3021
+ skippableSuitesCoverage = undefined
3026
3022
  } else {
3027
3023
  skippableSuites = receivedSkippableSuites
3028
- skippableSuitesCoverage = receivedSkippableSuitesCoverage || {}
3024
+ skippableSuitesCoverage = receivedSkippableSuitesCoverage
3029
3025
  }
3030
3026
  skippedSuitesCoverage = {}
3031
3027
  } catch (err) {
@@ -3066,7 +3062,19 @@ function getCliWrapper (isNewJestVersion) {
3066
3062
  frameworkVersion: jestVersion,
3067
3063
  })
3068
3064
 
3069
- const result = await runCLI.apply(this, arguments)
3065
+ let result
3066
+ try {
3067
+ result = await runCLI.apply(this, arguments)
3068
+ } catch (error) {
3069
+ try {
3070
+ await waitForTestSessionFinish(getTestSessionFinishPayload('fail', error, {
3071
+ isTestSessionFinalizationError: true,
3072
+ }))
3073
+ } catch (finalizationError) {
3074
+ log.error('Jest test session finalization error: %s', finalizationError)
3075
+ }
3076
+ throw error
3077
+ }
3070
3078
 
3071
3079
  const {
3072
3080
  results: {
@@ -1,7 +1,19 @@
1
1
  'use strict'
2
2
 
3
+ const { performance } = require('node:perf_hooks')
4
+
3
5
  const shimmer = require('../../datadog-shimmer')
4
6
  const { channel, addHook } = require('./helpers/instrument')
7
+ const {
8
+ acquireWait,
9
+ clearPoolWaitTime,
10
+ isPoolQueryAcquire,
11
+ runOutsidePoolQueryAcquire,
12
+ runPoolAcquireError,
13
+ runWithPoolWait,
14
+ takePoolWaitTime,
15
+ wrapPoolQueryMethod,
16
+ } = require('./helpers/pool-acquire')
5
17
 
6
18
  const commandAddCh = channel('apm:mariadb:command:add')
7
19
  const connectionStartCh = channel('apm:mariadb:connection:start')
@@ -10,11 +22,37 @@ const startCh = channel('apm:mariadb:query:start')
10
22
  const finishCh = channel('apm:mariadb:query:finish')
11
23
  const errorCh = channel('apm:mariadb:query:error')
12
24
  const skipCh = channel('apm:mariadb:pool:skip')
25
+ const acquireStartCh = channel('apm:mariadb:pool:acquire:start')
26
+ const acquireFinishCh = channel('apm:mariadb:pool:acquire:finish')
27
+ const poolAcquireChannels = {
28
+ connectionFinishCh,
29
+ acquireStartCh,
30
+ acquireFinishCh,
31
+ }
32
+ const wrappedPools = new WeakSet()
33
+
34
+ /**
35
+ * @typedef {object} PoolAcquireTiming
36
+ * @property {number} [poolAcquireStart]
37
+ * @property {number} [poolAcquireStartedAt]
38
+ * @property {number} [poolWaitTime]
39
+ */
40
+
41
+ /** @typedef {{ length: number, [index: number]: unknown } & Iterable<unknown>} ArgumentsLike */
42
+
43
+ /** @type {PoolAcquireTiming|undefined} */
44
+ let currentPoolAcquireTiming
45
+ let explicitPoolAcquire = false
13
46
 
14
47
  function wrapCommandStart (start, ctx) {
15
48
  return shimmer.wrapFunction(start, start => function (...args) {
16
49
  if (!startCh.hasSubscribers) return start.apply(this, args)
17
50
 
51
+ const poolWaitTime = takePoolWaitTime(args[0])
52
+ if (poolWaitTime !== undefined) {
53
+ ctx.poolWaitTime = poolWaitTime
54
+ }
55
+
18
56
  const { reject, resolve } = this
19
57
  shimmer.wrap(this, 'resolve', function wrapResolve () {
20
58
  return function (...args) {
@@ -133,43 +171,262 @@ function wrapPoolMethod (createConnection) {
133
171
  }
134
172
  }
135
173
 
174
+ /**
175
+ * @param {Function} getConnection
176
+ * @returns {Function}
177
+ */
178
+ function wrapExplicitPoolGetConnection (getConnection) {
179
+ return function wrappedExplicitPoolGetConnection () {
180
+ if (!connectionStartCh.hasSubscribers) {
181
+ return getConnection.apply(this, arguments)
182
+ }
183
+
184
+ const previous = explicitPoolAcquire
185
+ explicitPoolAcquire = true
186
+ try {
187
+ return runOutsidePoolQueryAcquire(getConnection, this, arguments)
188
+ } finally {
189
+ explicitPoolAcquire = previous
190
+ }
191
+ }
192
+ }
193
+
194
+ /**
195
+ * @param {Function} method
196
+ * @returns {Function}
197
+ */
198
+ function wrapPoolQuery (method) {
199
+ return wrapPoolQueryMethod(method, connectionStartCh)
200
+ }
201
+
202
+ /**
203
+ * @param {Function} Pool
204
+ * @returns {boolean}
205
+ */
206
+ function claimPoolWrap (Pool) {
207
+ if (wrappedPools.has(Pool)) return false
208
+ wrappedPools.add(Pool)
209
+ return true
210
+ }
211
+
212
+ /**
213
+ * @param {Function} Pool
214
+ * @returns {Function}
215
+ */
216
+ function wrapPublicPool (Pool) {
217
+ if (!claimPoolWrap(Pool)) return Pool
218
+
219
+ shimmer.wrap(Pool.prototype, 'getConnection', wrapExplicitPoolGetConnection)
220
+ shimmer.wrap(Pool.prototype, 'query', wrapPoolQuery)
221
+ shimmer.wrap(Pool.prototype, 'execute', wrapPoolQuery)
222
+ return Pool
223
+ }
224
+
225
+ /**
226
+ * @param {Function} Pool
227
+ * @returns {Function}
228
+ */
229
+ function wrapInternalPool (Pool) {
230
+ if (!claimPoolWrap(Pool)) return Pool
231
+
232
+ // The idle callback must be replaced before validation and observed before the method returns,
233
+ // which cannot be expressed by Orchestrion's subscriber lifecycle.
234
+ shimmer.wrap(Pool.prototype, '_acquireIdleConnection', wrapPoolAcquireIdleMethod)
235
+ shimmer.wrap(Pool.prototype, 'getConnection', wrapPoolGetConnectionMethod)
236
+ shimmer.wrap(Pool.prototype, '_createPoolConnection', wrapPoolMethod)
237
+ return Pool
238
+ }
239
+
240
+ /**
241
+ * @param {Record<string, unknown>} acquireCtx
242
+ * @param {Function} callback
243
+ * @param {unknown} thisArg
244
+ * @param {ArgumentsLike} args
245
+ * @returns {unknown}
246
+ */
247
+ function finishExplicitPoolAcquire (acquireCtx, callback, thisArg, args) {
248
+ acquireFinishCh.publish(acquireCtx)
249
+ return callback.apply(thisArg, args)
250
+ }
251
+
252
+ /**
253
+ * @param {PoolAcquireTiming} timing
254
+ */
255
+ function startPoolAcquireTiming (timing) {
256
+ const start = performance.now()
257
+ timing.poolAcquireStart = start
258
+ timing.poolAcquireStartedAt ??= start
259
+ }
260
+
261
+ /**
262
+ * @param {PoolAcquireTiming} timing
263
+ * @returns {number}
264
+ */
265
+ function finishPoolAcquireTiming (timing) {
266
+ const poolWaitTime = (timing.poolWaitTime ?? 0) + acquireWait(timing.poolAcquireStart)
267
+ timing.poolAcquireStart = undefined
268
+ timing.poolWaitTime = poolWaitTime
269
+ return poolWaitTime
270
+ }
271
+
272
+ /**
273
+ * @param {Function} acquireIdleConnection
274
+ * @returns {Function}
275
+ */
276
+ function wrapPoolAcquireIdleMethod (acquireIdleConnection) {
277
+ return function wrappedAcquireIdleConnection (callback) {
278
+ const timing = currentPoolAcquireTiming
279
+ if (timing === undefined) {
280
+ return acquireIdleConnection.apply(this, arguments)
281
+ }
282
+ currentPoolAcquireTiming = undefined
283
+
284
+ let completed = false
285
+ let synchronous = true
286
+
287
+ /**
288
+ * @param {unknown} error
289
+ * @returns {unknown}
290
+ */
291
+ arguments[0] = function wrappedCallback (error) {
292
+ completed = true
293
+ if (error) {
294
+ if (synchronous) {
295
+ startPoolAcquireTiming(timing)
296
+ }
297
+ } else if (synchronous) {
298
+ timing.poolWaitTime = 0
299
+ } else {
300
+ finishPoolAcquireTiming(timing)
301
+ }
302
+ return callback.apply(this, arguments)
303
+ }
304
+
305
+ const result = acquireIdleConnection.apply(this, arguments)
306
+ synchronous = false
307
+ if (!completed) {
308
+ startPoolAcquireTiming(timing)
309
+ }
310
+ return result
311
+ }
312
+ }
313
+
314
+ /**
315
+ * @param {Function} getConnection
316
+ * @returns {Function}
317
+ */
136
318
  function wrapPoolGetConnectionMethod (getConnection) {
137
- return function wrappedGetConnection (...args) {
138
- const cb = args.at(-1)
139
- if (typeof cb !== 'function') return getConnection.apply(this, args)
319
+ const poolWaitTransfer = { deferred: false }
320
+
321
+ return function wrappedGetConnection (cmdParam, callback) {
322
+ if (!connectionStartCh.hasSubscribers) {
323
+ return getConnection.apply(this, arguments)
324
+ }
140
325
 
141
326
  const ctx = {}
327
+ const poolQueryAcquire = isPoolQueryAcquire()
328
+ const acquireCtx = !poolQueryAcquire && explicitPoolAcquire && acquireStartCh.hasSubscribers
329
+ ? { conf: this.opts.connOptions }
330
+ : undefined
331
+ const pool = this
332
+
333
+ if (acquireCtx !== undefined) {
334
+ acquireStartCh.publish(acquireCtx)
335
+ }
142
336
 
143
- args[args.length - 1] = function (...args) {
144
- return connectionFinishCh.runStores(ctx, cb, this, ...args)
337
+ /**
338
+ * @param {unknown} error
339
+ * @param {{ streamOut: object }|undefined} connection
340
+ * @returns {unknown}
341
+ */
342
+ arguments[1] = function wrappedCallback (error, connection) {
343
+ const poolWaitTime = poolQueryAcquire || acquireCtx !== undefined
344
+ ? finishPoolAcquireTiming(ctx)
345
+ : undefined
346
+
347
+ if (poolQueryAcquire) {
348
+ if (error) {
349
+ return runPoolAcquireError(
350
+ ctx.poolAcquireStartedAt,
351
+ error,
352
+ { conf: pool.opts.connOptions },
353
+ poolAcquireChannels,
354
+ ctx,
355
+ callback,
356
+ this,
357
+ arguments,
358
+ poolWaitTime
359
+ )
360
+ }
361
+
362
+ return runWithPoolWait(
363
+ poolWaitTransfer,
364
+ connection.streamOut,
365
+ poolWaitTime,
366
+ connectionFinishCh,
367
+ ctx,
368
+ callback,
369
+ this,
370
+ arguments
371
+ )
372
+ }
373
+
374
+ if (acquireCtx !== undefined) {
375
+ if (!error && connection !== undefined) {
376
+ clearPoolWaitTime(connection.streamOut)
377
+ }
378
+ acquireCtx.error = error
379
+ acquireCtx.poolWaitTime = poolWaitTime
380
+ return connectionFinishCh.runStores(
381
+ ctx,
382
+ finishExplicitPoolAcquire,
383
+ undefined,
384
+ acquireCtx,
385
+ callback,
386
+ this,
387
+ arguments
388
+ )
389
+ }
390
+ return connectionFinishCh.runStores(ctx, callback, this, ...arguments)
145
391
  }
146
392
 
147
393
  connectionStartCh.publish(ctx)
148
394
 
149
- return getConnection.apply(this, args)
395
+ if (!poolQueryAcquire && acquireCtx === undefined) {
396
+ return getConnection.apply(pool, arguments)
397
+ }
398
+
399
+ const previousTiming = currentPoolAcquireTiming
400
+ const previousExplicitPoolAcquire = explicitPoolAcquire
401
+ currentPoolAcquireTiming = ctx
402
+ if (previousExplicitPoolAcquire) explicitPoolAcquire = false
403
+ try {
404
+ return poolQueryAcquire
405
+ ? runOutsidePoolQueryAcquire(getConnection, pool, arguments)
406
+ : getConnection.apply(pool, arguments)
407
+ } finally {
408
+ currentPoolAcquireTiming = previousTiming
409
+ if (previousExplicitPoolAcquire) explicitPoolAcquire = true
410
+ }
150
411
  }
151
412
  }
152
413
 
153
414
  const name = 'mariadb'
154
415
 
155
- addHook({ name, file: 'lib/cmd/query.js', versions: ['>=3'] }, (Query) => {
156
- return wrapCommand(Query)
157
- })
416
+ addHook({ name, file: 'lib/cmd/query.js', versions: ['>=3'], patchDefault: true }, wrapCommand)
417
+ addHook({ name, file: 'lib/cmd/execute.js', versions: ['>=3'], patchDefault: true }, wrapCommand)
158
418
 
159
- addHook({ name, file: 'lib/cmd/execute.js', versions: ['>=3'] }, (Execute) => {
160
- return wrapCommand(Execute)
161
- })
419
+ // mariadb 3.4.1 switched its internal getConnection from promises to callbacks. That exposes synchronous
420
+ // recent-idle reuse without a clock read while still letting us time validation and queueing.
421
+ // The same release renamed _createConnection to _createPoolConnection.
422
+ addHook({ name, file: 'lib/pool.js', versions: ['>=3.4.1'], patchDefault: true }, wrapInternalPool)
162
423
 
163
- // mariadb 3.4.1 refactored the pool: getConnection switched from promises to
164
- // callbacks and _createConnection was renamed to _createPoolConnection.
165
- addHook({ name, file: 'lib/pool.js', versions: ['>=3.4.1'] }, (Pool) => {
166
- shimmer.wrap(Pool.prototype, 'getConnection', wrapPoolGetConnectionMethod)
167
- shimmer.wrap(Pool.prototype, '_createPoolConnection', wrapPoolMethod)
168
-
169
- return Pool
170
- })
424
+ // Orchestrion completion follows the returned Promise, but operation classification must end as
425
+ // soon as the facade calls its private internal pool, so these methods need a synchronous bracket.
426
+ addHook({ name, file: 'lib/pool-promise.js', versions: ['>=3.4.1'], patchDefault: true }, wrapPublicPool)
427
+ addHook({ name, file: 'lib/pool-callback.js', versions: ['>=3.4.1'], patchDefault: true }, wrapPublicPool)
171
428
 
172
- addHook({ name, file: 'lib/pool.js', versions: ['>=3 <3.4.1'] }, (Pool) => {
429
+ addHook({ name, file: 'lib/pool.js', versions: ['>=3 <3.4.1'], patchDefault: true }, (Pool) => {
173
430
  shimmer.wrap(Pool.prototype, '_createConnection', wrapPoolMethod)
174
431
 
175
432
  return Pool