dd-trace 6.12.0 → 6.13.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/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/index.d.ts +127 -26
- package/package.json +10 -10
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +28 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/jest.js +140 -18
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright.js +34 -20
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -4
- package/packages/datadog-instrumentations/src/vitest-worker.js +11 -0
- package/packages/datadog-instrumentations/src/webdriverio.js +26 -0
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +9 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -15
- package/packages/dd-trace/src/aiguard/evaluation.js +94 -20
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +21 -9
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +51 -26
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
- package/packages/dd-trace/src/ci-visibility/final-flush.js +20 -0
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +196 -29
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -0
- package/packages/dd-trace/src/config/index.js +2 -1
- package/packages/dd-trace/src/config/supported-configurations.json +27 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -3
- package/packages/dd-trace/src/dogstatsd.js +45 -18
- package/packages/dd-trace/src/encode/agentless-json.js +5 -4
- package/packages/dd-trace/src/exporters/agent/index.js +22 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +4 -4
- package/packages/dd-trace/src/exporters/common/writer.js +24 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +102 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +51 -2
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
- package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
- package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
- package/packages/dd-trace/src/llmobs/index.js +80 -17
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/writers/base.js +60 -24
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +4 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +3 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +410 -342
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +74 -21
- package/packages/dd-trace/src/plugins/util/web.js +2 -65
- package/packages/dd-trace/src/profiling/oom.js +4 -3
- package/packages/dd-trace/src/profiling/profiler.js +7 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/proxy.js +36 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -6
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +55 -0
- package/packages/dd-trace/src/serverless/vercel.js +122 -0
- package/packages/dd-trace/src/serverless.js +37 -20
- package/packages/dd-trace/src/span_stats.js +40 -3
- package/packages/dd-trace/src/tracer.js +22 -0
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
|
@@ -0,0 +1,1597 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { errorMonitor } = require('node:events')
|
|
4
|
+
const { performance } = require('node:perf_hooks')
|
|
5
|
+
|
|
6
|
+
const shimmer = require('../../datadog-shimmer')
|
|
7
|
+
const { channel } = require('./helpers/instrument')
|
|
8
|
+
const { acquireWait, reportPoolAcquireError } = require('./helpers/pool-acquire')
|
|
9
|
+
|
|
10
|
+
const connectionStartCh = channel('apm:mariadb:connection:start')
|
|
11
|
+
const connectionFinishCh = channel('apm:mariadb:connection:finish')
|
|
12
|
+
const startCh = channel('apm:mariadb:query:start')
|
|
13
|
+
const finishCh = channel('apm:mariadb:query:finish')
|
|
14
|
+
const errorCh = channel('apm:mariadb:query:error')
|
|
15
|
+
const skipCh = channel('apm:mariadb:pool:skip')
|
|
16
|
+
const acquireStartCh = channel('apm:mariadb:pool:acquire:start')
|
|
17
|
+
const acquireFinishCh = channel('apm:mariadb:pool:acquire:finish')
|
|
18
|
+
const poolAcquireChannels = {
|
|
19
|
+
connectionFinishCh,
|
|
20
|
+
acquireStartCh,
|
|
21
|
+
acquireFinishCh,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const activeCommands = new WeakMap()
|
|
25
|
+
const commandMethods = ['query', 'execute', 'batch']
|
|
26
|
+
const trackedCommandMethods = ['changeUser', 'ping', 'prepare', 'reset']
|
|
27
|
+
const emptyConnectionContext = { currentStore: {} }
|
|
28
|
+
const emptyOptions = {}
|
|
29
|
+
const wrappedClients = new WeakSet()
|
|
30
|
+
const wrappedConnections = new WeakSet()
|
|
31
|
+
const IMPORT_FILE_RESOURCE = 'IMPORT FILE'
|
|
32
|
+
const noop = () => {}
|
|
33
|
+
const POOL_ACQUISITION_COMPACTION_THRESHOLD = 1024
|
|
34
|
+
const STATUS_IN_TRANSACTION = 1
|
|
35
|
+
const transactionMethods = [
|
|
36
|
+
['beginTransaction', 'START TRANSACTION'],
|
|
37
|
+
['commit', 'COMMIT'],
|
|
38
|
+
['rollback', 'ROLLBACK'],
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
/** @typedef {{ length: number, [index: number]: unknown } & Iterable<unknown>} ArgumentsLike */
|
|
42
|
+
/** @typedef {{ options: object, pendingRemovals: number }} ClusterNodeOptions */
|
|
43
|
+
/** @typedef {{ options?: object }} ClusterSelection */
|
|
44
|
+
/** @typedef {import('node:async_hooks').AsyncLocalStorage<ClusterSelection>} ClusterSelectionStorage */
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {object} PoolAcquisition
|
|
47
|
+
* @property {Record<string, unknown>} [acquireCtx]
|
|
48
|
+
* @property {boolean} [acquired]
|
|
49
|
+
* @property {object} connectionCtx
|
|
50
|
+
* @property {unknown} [error]
|
|
51
|
+
* @property {boolean} [errorReported]
|
|
52
|
+
* @property {boolean} [finished]
|
|
53
|
+
* @property {boolean} measure
|
|
54
|
+
* @property {object} options
|
|
55
|
+
* @property {object} pool
|
|
56
|
+
* @property {object} [queryCtx]
|
|
57
|
+
* @property {boolean} [queryStarted]
|
|
58
|
+
* @property {boolean} [ready]
|
|
59
|
+
* @property {number} [start]
|
|
60
|
+
*/
|
|
61
|
+
/** @typedef {import('node:async_hooks').AsyncLocalStorage<PoolAcquisition>} PoolAcquisitionStorage */
|
|
62
|
+
/** @typedef {{ acquisitions: Array<PoolAcquisition | undefined>, index: number }} PendingPoolAcquisitions */
|
|
63
|
+
|
|
64
|
+
/** @type {ClusterSelectionStorage | undefined} */
|
|
65
|
+
let clusterSelectionStorage
|
|
66
|
+
|
|
67
|
+
/** @type {PoolAcquisitionStorage | undefined} */
|
|
68
|
+
let poolAcquisitionStorage
|
|
69
|
+
|
|
70
|
+
/** @type {WeakMap<object, PendingPoolAcquisitions>} */
|
|
71
|
+
const pendingPoolAcquisitions = new WeakMap()
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Creates cluster selection storage only when an application uses pool clusters.
|
|
75
|
+
*
|
|
76
|
+
* @returns {ClusterSelectionStorage}
|
|
77
|
+
*/
|
|
78
|
+
function getClusterSelectionStorage () {
|
|
79
|
+
if (clusterSelectionStorage === undefined) {
|
|
80
|
+
const { AsyncLocalStorage } = require('node:async_hooks')
|
|
81
|
+
clusterSelectionStorage = new AsyncLocalStorage()
|
|
82
|
+
}
|
|
83
|
+
return clusterSelectionStorage
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Creates pool acquisition storage only when an application uses a bundled pool.
|
|
88
|
+
*
|
|
89
|
+
* @returns {PoolAcquisitionStorage}
|
|
90
|
+
*/
|
|
91
|
+
function getPoolAcquisitionStorage () {
|
|
92
|
+
if (poolAcquisitionStorage === undefined) {
|
|
93
|
+
const { AsyncLocalStorage } = require('node:async_hooks')
|
|
94
|
+
poolAcquisitionStorage = new AsyncLocalStorage()
|
|
95
|
+
}
|
|
96
|
+
return poolAcquisitionStorage
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Extracts SQL from MariaDB's string and object command forms.
|
|
101
|
+
*
|
|
102
|
+
* @param {unknown} command
|
|
103
|
+
* @returns {unknown}
|
|
104
|
+
*/
|
|
105
|
+
function normalizeSql (command) {
|
|
106
|
+
return command?.sql ?? command
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Parses connection options without allowing instrumentation to break the factory call.
|
|
111
|
+
*
|
|
112
|
+
* @param {Function} defaultOptions
|
|
113
|
+
* @param {unknown} options
|
|
114
|
+
* @returns {object}
|
|
115
|
+
*/
|
|
116
|
+
function normalizeOptions (defaultOptions, options) {
|
|
117
|
+
try {
|
|
118
|
+
return defaultOptions(options)
|
|
119
|
+
} catch {
|
|
120
|
+
return options !== null && typeof options === 'object' ? options : {}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Creates acquisition state while the caller context is still active.
|
|
126
|
+
*
|
|
127
|
+
* @param {object} pool
|
|
128
|
+
* @param {object} options
|
|
129
|
+
* @param {object} [queryCtx]
|
|
130
|
+
* @param {'explicit' | 'measure' | 'observe'} mode
|
|
131
|
+
* @returns {PoolAcquisition}
|
|
132
|
+
*/
|
|
133
|
+
function createPoolAcquisition (pool, options, queryCtx, mode) {
|
|
134
|
+
const explicit = mode === 'explicit'
|
|
135
|
+
const measure = explicit || mode === 'measure'
|
|
136
|
+
const connectionCtx = measure || queryCtx !== undefined ? {} : emptyConnectionContext
|
|
137
|
+
const acquisition = { connectionCtx, measure, options, pool, queryCtx }
|
|
138
|
+
|
|
139
|
+
if (connectionCtx !== emptyConnectionContext) connectionStartCh.publish(connectionCtx)
|
|
140
|
+
if (explicit) {
|
|
141
|
+
acquisition.acquireCtx = { conf: options }
|
|
142
|
+
acquireStartCh.publish(acquisition.acquireCtx)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return acquisition
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Queues a delayed acquisition for pool events that no longer carry its async-local context.
|
|
150
|
+
*
|
|
151
|
+
* @param {PoolAcquisition} acquisition
|
|
152
|
+
* @returns {void}
|
|
153
|
+
*/
|
|
154
|
+
function queuePoolAcquisition (acquisition) {
|
|
155
|
+
let pending = pendingPoolAcquisitions.get(acquisition.pool)
|
|
156
|
+
if (pending === undefined) {
|
|
157
|
+
pending = { acquisitions: [], index: 0 }
|
|
158
|
+
pendingPoolAcquisitions.set(acquisition.pool, pending)
|
|
159
|
+
}
|
|
160
|
+
pending.acquisitions.push(acquisition)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Reports whether an acquisition still needs a matching pool event.
|
|
165
|
+
*
|
|
166
|
+
* @param {PoolAcquisition} acquisition
|
|
167
|
+
* @returns {boolean}
|
|
168
|
+
*/
|
|
169
|
+
function isPoolAcquisitionPending (acquisition) {
|
|
170
|
+
return !acquisition.acquired && !acquisition.finished && !acquisition.errorReported
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Releases a consumed acquisition and periodically compacts its queue.
|
|
175
|
+
*
|
|
176
|
+
* @param {PendingPoolAcquisitions} pending
|
|
177
|
+
* @returns {void}
|
|
178
|
+
*/
|
|
179
|
+
function discardPoolAcquisition (pending) {
|
|
180
|
+
pending.acquisitions[pending.index++] = undefined
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
pending.index < POOL_ACQUISITION_COMPACTION_THRESHOLD ||
|
|
184
|
+
pending.index * 2 < pending.acquisitions.length ||
|
|
185
|
+
pending.index === pending.acquisitions.length
|
|
186
|
+
) {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
pending.acquisitions = pending.acquisitions.slice(pending.index)
|
|
191
|
+
pending.index = 0
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Removes completed entries from the front of a pool's acquisition queue.
|
|
196
|
+
*
|
|
197
|
+
* @param {object} pool
|
|
198
|
+
* @returns {void}
|
|
199
|
+
*/
|
|
200
|
+
function prunePoolAcquisitions (pool) {
|
|
201
|
+
const pending = pendingPoolAcquisitions.get(pool)
|
|
202
|
+
if (pending === undefined) return
|
|
203
|
+
|
|
204
|
+
while (pending.index < pending.acquisitions.length) {
|
|
205
|
+
const acquisition = /** @type {PoolAcquisition} */ (pending.acquisitions[pending.index])
|
|
206
|
+
if (isPoolAcquisitionPending(acquisition)) return
|
|
207
|
+
|
|
208
|
+
discardPoolAcquisition(pending)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
pendingPoolAcquisitions.delete(pool)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Takes the next unfinished acquisition from a pool's public-operation queue.
|
|
216
|
+
*
|
|
217
|
+
* @param {object} pool
|
|
218
|
+
* @returns {PoolAcquisition | undefined}
|
|
219
|
+
*/
|
|
220
|
+
function takePoolAcquisition (pool) {
|
|
221
|
+
const pending = pendingPoolAcquisitions.get(pool)
|
|
222
|
+
if (pending === undefined) return
|
|
223
|
+
|
|
224
|
+
while (pending.index < pending.acquisitions.length) {
|
|
225
|
+
const acquisition = /** @type {PoolAcquisition} */ (pending.acquisitions[pending.index])
|
|
226
|
+
discardPoolAcquisition(pending)
|
|
227
|
+
|
|
228
|
+
if (isPoolAcquisitionPending(acquisition)) return acquisition
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
pendingPoolAcquisitions.delete(pool)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Records the pool wait when MariaDB announces that the calling operation acquired a connection.
|
|
236
|
+
*
|
|
237
|
+
* @param {object} pool
|
|
238
|
+
* @returns {void}
|
|
239
|
+
*/
|
|
240
|
+
function recordPoolAcquisition (pool) {
|
|
241
|
+
let acquisition = poolAcquisitionStorage?.getStore()
|
|
242
|
+
if (acquisition?.pool !== pool || !isPoolAcquisitionPending(acquisition)) {
|
|
243
|
+
acquisition = takePoolAcquisition(pool)
|
|
244
|
+
}
|
|
245
|
+
if (acquisition === undefined) return
|
|
246
|
+
|
|
247
|
+
acquisition.acquired = true
|
|
248
|
+
prunePoolAcquisitions(pool)
|
|
249
|
+
if (!acquisition.measure) {
|
|
250
|
+
startPoolCommand(acquisition)
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const poolWaitTime = acquireWait(acquisition.start)
|
|
255
|
+
|
|
256
|
+
if (acquisition.queryCtx !== undefined) acquisition.queryCtx.poolWaitTime = poolWaitTime
|
|
257
|
+
if (acquisition.acquireCtx !== undefined) acquisition.acquireCtx.poolWaitTime = poolWaitTime
|
|
258
|
+
|
|
259
|
+
startPoolCommand(acquisition)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Starts a pooled command after MariaDB has acquired the connection that will execute it.
|
|
264
|
+
*
|
|
265
|
+
* @param {PoolAcquisition} acquisition
|
|
266
|
+
* @returns {void}
|
|
267
|
+
*/
|
|
268
|
+
function startPoolCommand (acquisition) {
|
|
269
|
+
const queryCtx = acquisition.queryCtx
|
|
270
|
+
if (queryCtx === undefined || acquisition.queryStarted) return
|
|
271
|
+
|
|
272
|
+
acquisition.queryStarted = true
|
|
273
|
+
connectionFinishCh.runStores(acquisition.connectionCtx, runCommandStart, undefined, queryCtx)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Starts a command lifecycle without running connector work inside its span store.
|
|
278
|
+
*
|
|
279
|
+
* @param {object} ctx
|
|
280
|
+
* @returns {void}
|
|
281
|
+
*/
|
|
282
|
+
function runCommandStart (ctx) {
|
|
283
|
+
startCh.runStores(ctx, noop)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Creates an acquire error span when a pooled query fails before receiving a connection.
|
|
288
|
+
*
|
|
289
|
+
* @param {PoolAcquisition | undefined} acquisition
|
|
290
|
+
* @param {unknown} error
|
|
291
|
+
* @returns {void}
|
|
292
|
+
*/
|
|
293
|
+
function reportPoolQueryAcquireError (acquisition, error) {
|
|
294
|
+
if (acquisition === undefined || acquisition.acquired || acquisition.errorReported) return
|
|
295
|
+
|
|
296
|
+
acquisition.error = error
|
|
297
|
+
if (!acquisition.ready) return
|
|
298
|
+
|
|
299
|
+
acquisition.errorReported = true
|
|
300
|
+
prunePoolAcquisitions(acquisition.pool)
|
|
301
|
+
connectionFinishCh.runStores(acquisition.connectionCtx, reportPoolAcquireError, undefined,
|
|
302
|
+
acquisition.start, error, { conf: acquisition.options }, poolAcquireChannels)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Calls a method with its original receiver and arguments.
|
|
307
|
+
*
|
|
308
|
+
* @param {Function} method
|
|
309
|
+
* @param {unknown} receiver
|
|
310
|
+
* @param {ArgumentsLike} args
|
|
311
|
+
* @returns {unknown}
|
|
312
|
+
*/
|
|
313
|
+
function callMethod (method, receiver, args) {
|
|
314
|
+
return method.apply(receiver, args)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Runs a public pool operation while associating its acquire event with that operation.
|
|
319
|
+
*
|
|
320
|
+
* @param {PoolAcquisition} acquisition
|
|
321
|
+
* @param {Function} method
|
|
322
|
+
* @param {object} receiver
|
|
323
|
+
* @param {ArgumentsLike} args
|
|
324
|
+
* @returns {unknown}
|
|
325
|
+
*/
|
|
326
|
+
function runPoolAcquisition (acquisition, method, receiver, args) {
|
|
327
|
+
const result = getPoolAcquisitionStorage().run(acquisition, callMethod, method, receiver, args)
|
|
328
|
+
|
|
329
|
+
acquisition.ready = true
|
|
330
|
+
if (!acquisition.acquired && !acquisition.finished) {
|
|
331
|
+
queuePoolAcquisition(acquisition)
|
|
332
|
+
if (!acquisition.measure) return result
|
|
333
|
+
|
|
334
|
+
if (acquisition.error === undefined) {
|
|
335
|
+
acquisition.start = performance.now()
|
|
336
|
+
} else {
|
|
337
|
+
reportPoolQueryAcquireError(acquisition, acquisition.error)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return result
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Completes tracking for a pool command that has returned to the caller.
|
|
346
|
+
*
|
|
347
|
+
* @param {PoolAcquisition | undefined} acquisition
|
|
348
|
+
* @param {unknown} [error]
|
|
349
|
+
* @returns {void}
|
|
350
|
+
*/
|
|
351
|
+
function finishPoolCommandAcquisition (acquisition, error) {
|
|
352
|
+
if (acquisition === undefined) return
|
|
353
|
+
if (error && acquisition.measure) {
|
|
354
|
+
reportPoolQueryAcquireError(acquisition, error)
|
|
355
|
+
if (!acquisition.ready) return
|
|
356
|
+
}
|
|
357
|
+
if (!acquisition.acquired) acquisition.finished = true
|
|
358
|
+
prunePoolAcquisitions(acquisition.pool)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Runs a bundled pool method in the instrumentation skip context.
|
|
363
|
+
*
|
|
364
|
+
* @param {Function} method
|
|
365
|
+
* @param {unknown} receiver
|
|
366
|
+
* @param {ArgumentsLike} args
|
|
367
|
+
* @returns {unknown}
|
|
368
|
+
*/
|
|
369
|
+
function runSkippedPoolMethod (method, receiver, args) {
|
|
370
|
+
return skipCh.runStores({}, method, receiver, ...args)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Finishes the acquire span created for a public getConnection call.
|
|
375
|
+
*
|
|
376
|
+
* @param {PoolAcquisition} acquisition
|
|
377
|
+
* @param {unknown} [error]
|
|
378
|
+
* @returns {void}
|
|
379
|
+
*/
|
|
380
|
+
function finishExplicitPoolAcquisition (acquisition, error) {
|
|
381
|
+
if (acquisition.finished) return
|
|
382
|
+
acquisition.finished = true
|
|
383
|
+
prunePoolAcquisitions(acquisition.pool)
|
|
384
|
+
|
|
385
|
+
const acquireCtx = acquisition.acquireCtx
|
|
386
|
+
if (acquireCtx === undefined) return
|
|
387
|
+
|
|
388
|
+
acquireCtx.poolWaitTime ??= acquireWait(acquisition.start)
|
|
389
|
+
if (error) acquireCtx.error = error
|
|
390
|
+
acquireFinishCh.publish(acquireCtx)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Restores the acquisition observer if application listener cleanup removed it.
|
|
395
|
+
*
|
|
396
|
+
* @param {object} pool
|
|
397
|
+
* @param {Function} observer
|
|
398
|
+
* @returns {void}
|
|
399
|
+
*/
|
|
400
|
+
function restorePoolAcquisitionObserver (pool, observer) {
|
|
401
|
+
const listeners = pool.listeners('acquire')
|
|
402
|
+
for (const listener of listeners) {
|
|
403
|
+
if (listener === observer) return
|
|
404
|
+
}
|
|
405
|
+
pool.prependListener('acquire', observer)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Observes public acquire events forwarded by a bundled pool.
|
|
410
|
+
*
|
|
411
|
+
* @param {object} pool
|
|
412
|
+
* @returns {void}
|
|
413
|
+
*/
|
|
414
|
+
function observePoolAcquisitions (pool) {
|
|
415
|
+
const observer = () => {
|
|
416
|
+
if (connectionStartCh.hasSubscribers) recordPoolAcquisition(pool)
|
|
417
|
+
}
|
|
418
|
+
pool.prependListener('acquire', observer)
|
|
419
|
+
|
|
420
|
+
shimmer.wrap(pool, 'removeAllListeners', removeAllListeners => function (event) {
|
|
421
|
+
const result = removeAllListeners.apply(this, arguments)
|
|
422
|
+
if (event === undefined || event === 'acquire') restorePoolAcquisitionObserver(pool, observer)
|
|
423
|
+
return result
|
|
424
|
+
})
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Marks a command as active on its owning public connection.
|
|
429
|
+
*
|
|
430
|
+
* @param {object | undefined} owner
|
|
431
|
+
* @returns {void}
|
|
432
|
+
*/
|
|
433
|
+
function startCommand (owner) {
|
|
434
|
+
if (owner === undefined) return
|
|
435
|
+
activeCommands.set(owner, (activeCommands.get(owner) ?? 0) + 1)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Releases one active command without clearing concurrent commands.
|
|
440
|
+
*
|
|
441
|
+
* @param {object | undefined} owner
|
|
442
|
+
* @returns {void}
|
|
443
|
+
*/
|
|
444
|
+
function endCommand (owner) {
|
|
445
|
+
if (owner === undefined) return
|
|
446
|
+
const active = activeCommands.get(owner)
|
|
447
|
+
if (active === 1) {
|
|
448
|
+
activeCommands.delete(owner)
|
|
449
|
+
} else if (active !== undefined) {
|
|
450
|
+
activeCommands.set(owner, active - 1)
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Publishes command completion state and releases its owner.
|
|
456
|
+
*
|
|
457
|
+
* @param {object} ctx
|
|
458
|
+
* @param {object | undefined} owner
|
|
459
|
+
* @param {Error} [error]
|
|
460
|
+
* @param {unknown} [result]
|
|
461
|
+
* @returns {void}
|
|
462
|
+
*/
|
|
463
|
+
function finishCommandState (ctx, owner, error, result) {
|
|
464
|
+
endCommand(owner)
|
|
465
|
+
if (error) {
|
|
466
|
+
ctx.error = error
|
|
467
|
+
errorCh.publish(ctx)
|
|
468
|
+
}
|
|
469
|
+
ctx.result = result
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Publishes a complete command lifecycle outside a callback continuation.
|
|
474
|
+
*
|
|
475
|
+
* @param {object} ctx
|
|
476
|
+
* @param {object | undefined} owner
|
|
477
|
+
* @param {Error} [error]
|
|
478
|
+
* @param {unknown} [result]
|
|
479
|
+
* @returns {void}
|
|
480
|
+
*/
|
|
481
|
+
function finishCommand (ctx, owner, error, result) {
|
|
482
|
+
finishCommandState(ctx, owner, error, result)
|
|
483
|
+
finishCh.publish(ctx)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Replaces an existing callback or inserts one in the method's declared callback slot.
|
|
488
|
+
*
|
|
489
|
+
* @param {ArgumentsLike} args
|
|
490
|
+
* @param {number} callbackIndex
|
|
491
|
+
* @param {unknown} callback
|
|
492
|
+
* @param {(callback?: Function) => Function} createCallback
|
|
493
|
+
* @returns {void}
|
|
494
|
+
*/
|
|
495
|
+
function setCommandCallback (args, callbackIndex, callback, createCallback) {
|
|
496
|
+
if (typeof callback === 'function') {
|
|
497
|
+
args[args.length - 1] = shimmer.wrapCallback(callback, createCallback)
|
|
498
|
+
return
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const wrappedCallback = createCallback()
|
|
502
|
+
// MariaDB reads the declared callback slot, so appending after an explicit null leaves the wrapper unused.
|
|
503
|
+
if (callbackIndex >= 0 && args.length > callbackIndex && args[callbackIndex] == null) {
|
|
504
|
+
args[callbackIndex] = wrappedCallback
|
|
505
|
+
return
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
args.length = Math.max(args.length + 1, callbackIndex + 1)
|
|
509
|
+
args[args.length - 1] = wrappedCallback
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Tracks an untraced promise command while MariaDB may have work queued for it.
|
|
514
|
+
*
|
|
515
|
+
* @param {Function} command
|
|
516
|
+
* @returns {Function}
|
|
517
|
+
*/
|
|
518
|
+
function createTrackPromiseCommand (command) {
|
|
519
|
+
return function () {
|
|
520
|
+
if (!startCh.hasSubscribers) return command.apply(this, arguments)
|
|
521
|
+
|
|
522
|
+
const owner = this
|
|
523
|
+
startCommand(owner)
|
|
524
|
+
|
|
525
|
+
let result
|
|
526
|
+
try {
|
|
527
|
+
result = command.apply(this, arguments)
|
|
528
|
+
} catch (error) {
|
|
529
|
+
endCommand(owner)
|
|
530
|
+
throw error
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return result.then(result => {
|
|
534
|
+
endCommand(owner)
|
|
535
|
+
return result
|
|
536
|
+
}, error => {
|
|
537
|
+
endCommand(owner)
|
|
538
|
+
throw error
|
|
539
|
+
})
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Tracks an untraced callback command while MariaDB may have work queued for it.
|
|
545
|
+
*
|
|
546
|
+
* @param {Function} command
|
|
547
|
+
* @returns {Function}
|
|
548
|
+
*/
|
|
549
|
+
function createTrackCallbackCommand (command) {
|
|
550
|
+
const callbackIndex = command.length - 1
|
|
551
|
+
|
|
552
|
+
return function () {
|
|
553
|
+
if (!startCh.hasSubscribers) return command.apply(this, arguments)
|
|
554
|
+
|
|
555
|
+
const owner = this
|
|
556
|
+
let finished = false
|
|
557
|
+
const finish = () => {
|
|
558
|
+
if (finished) return
|
|
559
|
+
finished = true
|
|
560
|
+
endCommand(owner)
|
|
561
|
+
}
|
|
562
|
+
const createCallback = callback => function () {
|
|
563
|
+
finish()
|
|
564
|
+
if (typeof callback === 'function') return callback.apply(this, arguments)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const callback = arguments[arguments.length - 1]
|
|
568
|
+
setCommandCallback(arguments, callbackIndex, callback, createCallback)
|
|
569
|
+
startCommand(owner)
|
|
570
|
+
|
|
571
|
+
try {
|
|
572
|
+
return command.apply(this, arguments)
|
|
573
|
+
} catch (error) {
|
|
574
|
+
finish()
|
|
575
|
+
throw error
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Creates a promise-returning command wrapper.
|
|
582
|
+
*
|
|
583
|
+
* @param {object} options
|
|
584
|
+
* @param {unknown} [preparedSql]
|
|
585
|
+
* @param {object} [commandOwner]
|
|
586
|
+
* @param {number} [_commandArity] Reserved for callback command wrappers.
|
|
587
|
+
* @param {boolean} [trackActiveCommands]
|
|
588
|
+
* @param {'measure' | 'observe'} [poolAcquisition]
|
|
589
|
+
* @returns {(command: Function) => Function}
|
|
590
|
+
*/
|
|
591
|
+
function createWrapPromiseCommand (
|
|
592
|
+
options,
|
|
593
|
+
preparedSql,
|
|
594
|
+
commandOwner,
|
|
595
|
+
_commandArity,
|
|
596
|
+
trackActiveCommands = true,
|
|
597
|
+
poolAcquisition
|
|
598
|
+
) {
|
|
599
|
+
return function wrapCommand (command) {
|
|
600
|
+
return function (sql) {
|
|
601
|
+
if (!startCh.hasSubscribers) return command.apply(this, arguments)
|
|
602
|
+
|
|
603
|
+
const owner = trackActiveCommands ? (commandOwner ?? this) : undefined
|
|
604
|
+
const ctx = { sql: preparedSql ?? normalizeSql(sql), conf: options }
|
|
605
|
+
const acquisition = poolAcquisition === undefined
|
|
606
|
+
? undefined
|
|
607
|
+
: createPoolAcquisition(this, options, ctx, poolAcquisition)
|
|
608
|
+
|
|
609
|
+
startCommand(owner)
|
|
610
|
+
|
|
611
|
+
let result
|
|
612
|
+
try {
|
|
613
|
+
result = acquisition === undefined
|
|
614
|
+
? startCh.runStores(ctx, command, this, ...arguments)
|
|
615
|
+
: runPoolAcquisition(acquisition, command, this, arguments)
|
|
616
|
+
} catch (error) {
|
|
617
|
+
finishPoolCommandAcquisition(acquisition, error)
|
|
618
|
+
if (acquisition === undefined || acquisition.queryStarted) finishCommand(ctx, owner, error)
|
|
619
|
+
throw error
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return result.then(result => {
|
|
623
|
+
finishPoolCommandAcquisition(acquisition)
|
|
624
|
+
if (acquisition === undefined || acquisition.queryStarted) finishCommand(ctx, owner, undefined, result)
|
|
625
|
+
return result
|
|
626
|
+
}, error => {
|
|
627
|
+
finishPoolCommandAcquisition(acquisition, error)
|
|
628
|
+
if (acquisition === undefined || acquisition.queryStarted) finishCommand(ctx, owner, error)
|
|
629
|
+
throw error
|
|
630
|
+
})
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Creates a callback command wrapper and supplies a completion callback when the caller omits one.
|
|
637
|
+
*
|
|
638
|
+
* @param {object} options
|
|
639
|
+
* @param {unknown} [preparedSql]
|
|
640
|
+
* @param {object} [commandOwner]
|
|
641
|
+
* @param {number} [commandArity] Original arity when command is wrapped by a variadic forwarding function.
|
|
642
|
+
* @param {boolean} [trackActiveCommands]
|
|
643
|
+
* @param {'measure' | 'observe'} [poolAcquisition]
|
|
644
|
+
* @returns {(command: Function) => Function}
|
|
645
|
+
*/
|
|
646
|
+
function createWrapCallbackCommand (
|
|
647
|
+
options,
|
|
648
|
+
preparedSql,
|
|
649
|
+
commandOwner,
|
|
650
|
+
commandArity,
|
|
651
|
+
trackActiveCommands = true,
|
|
652
|
+
poolAcquisition
|
|
653
|
+
) {
|
|
654
|
+
return function wrapCommand (command) {
|
|
655
|
+
const callbackIndex = (commandArity ?? command.length) - 1
|
|
656
|
+
|
|
657
|
+
return function (sql) {
|
|
658
|
+
if (!startCh.hasSubscribers) return command.apply(this, arguments)
|
|
659
|
+
|
|
660
|
+
const owner = trackActiveCommands ? (commandOwner ?? this) : undefined
|
|
661
|
+
const callback = arguments[arguments.length - 1]
|
|
662
|
+
const ctx = { sql: preparedSql ?? normalizeSql(sql), conf: options }
|
|
663
|
+
const acquisition = poolAcquisition === undefined
|
|
664
|
+
? undefined
|
|
665
|
+
: createPoolAcquisition(this, options, ctx, poolAcquisition)
|
|
666
|
+
const wrapper = callback => function (error) {
|
|
667
|
+
finishPoolCommandAcquisition(acquisition, error)
|
|
668
|
+
if (acquisition !== undefined && !acquisition.queryStarted) {
|
|
669
|
+
return typeof callback === 'function'
|
|
670
|
+
? connectionFinishCh.runStores(acquisition.connectionCtx, callback, this, ...arguments)
|
|
671
|
+
: undefined
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
finishCommandState(ctx, owner, error)
|
|
675
|
+
|
|
676
|
+
return typeof callback === 'function'
|
|
677
|
+
? finishCh.runStores(ctx, callback, this, ...arguments)
|
|
678
|
+
: finishCh.runStores(ctx, noop, this)
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
setCommandCallback(arguments, callbackIndex, callback, wrapper)
|
|
682
|
+
|
|
683
|
+
startCommand(owner)
|
|
684
|
+
|
|
685
|
+
try {
|
|
686
|
+
return acquisition === undefined
|
|
687
|
+
? startCh.runStores(ctx, command, this, ...arguments)
|
|
688
|
+
: runPoolAcquisition(acquisition, command, this, arguments)
|
|
689
|
+
} catch (error) {
|
|
690
|
+
finishPoolCommandAcquisition(acquisition, error)
|
|
691
|
+
if (acquisition === undefined || acquisition.queryStarted) finishCommand(ctx, owner, error)
|
|
692
|
+
throw error
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Traces a Readable-returning command without replacing the stream.
|
|
700
|
+
*
|
|
701
|
+
* @param {object} options
|
|
702
|
+
* @param {unknown} [preparedSql]
|
|
703
|
+
* @param {object} [commandOwner]
|
|
704
|
+
* @returns {(streamMethod: Function) => Function}
|
|
705
|
+
*/
|
|
706
|
+
function createWrapStream (options, preparedSql, commandOwner) {
|
|
707
|
+
return function wrapStream (streamMethod) {
|
|
708
|
+
return function (sql) {
|
|
709
|
+
if (!startCh.hasSubscribers) return streamMethod.apply(this, arguments)
|
|
710
|
+
|
|
711
|
+
const owner = commandOwner ?? this
|
|
712
|
+
const ctx = { sql: preparedSql ?? normalizeSql(sql), conf: options }
|
|
713
|
+
let stream
|
|
714
|
+
|
|
715
|
+
startCommand(owner)
|
|
716
|
+
try {
|
|
717
|
+
stream = startCh.runStores(ctx, streamMethod, this, ...arguments)
|
|
718
|
+
} catch (error) {
|
|
719
|
+
finishCommand(ctx, owner, error)
|
|
720
|
+
throw error
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
let finished = false
|
|
724
|
+
const cleanup = () => {
|
|
725
|
+
stream.removeListener('end', onEnd)
|
|
726
|
+
stream.removeListener('close', onClose)
|
|
727
|
+
stream.removeListener(errorMonitor, onError)
|
|
728
|
+
}
|
|
729
|
+
const complete = error => {
|
|
730
|
+
if (finished) return
|
|
731
|
+
finished = true
|
|
732
|
+
cleanup()
|
|
733
|
+
finishCommand(ctx, owner, error)
|
|
734
|
+
}
|
|
735
|
+
const onEnd = () => complete()
|
|
736
|
+
const onClose = () => complete()
|
|
737
|
+
const onError = error => complete(error)
|
|
738
|
+
|
|
739
|
+
stream.once('end', onEnd)
|
|
740
|
+
stream.once('close', onClose)
|
|
741
|
+
stream.once(errorMonitor, onError)
|
|
742
|
+
|
|
743
|
+
return stream
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Wraps command methods exposed by a bundled client.
|
|
750
|
+
*
|
|
751
|
+
* @param {object} client
|
|
752
|
+
* @param {(command: Function) => Function} wrapper
|
|
753
|
+
* @returns {void}
|
|
754
|
+
*/
|
|
755
|
+
function wrapClientCommands (client, wrapper) {
|
|
756
|
+
if (wrappedClients.has(client)) return
|
|
757
|
+
|
|
758
|
+
wrappedClients.add(client)
|
|
759
|
+
for (const method of commandMethods) {
|
|
760
|
+
if (typeof client[method] === 'function') shimmer.wrap(client, method, wrapper)
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Wraps commands exposed by a bundled pool and tracks acquisition for query and execute.
|
|
766
|
+
*
|
|
767
|
+
* @param {object} pool
|
|
768
|
+
* @param {object} options
|
|
769
|
+
* @param {(options: object, sql?: unknown, owner?: object, commandArity?: number,
|
|
770
|
+
* trackActiveCommands?: boolean, poolAcquisition?: 'measure' | 'observe') =>
|
|
771
|
+
* (command: Function) => Function} createWrapper
|
|
772
|
+
* @returns {void}
|
|
773
|
+
*/
|
|
774
|
+
function wrapPoolCommands (pool, options, createWrapper) {
|
|
775
|
+
if (wrappedClients.has(pool)) return
|
|
776
|
+
|
|
777
|
+
wrappedClients.add(pool)
|
|
778
|
+
for (const method of commandMethods) {
|
|
779
|
+
if (typeof pool[method] !== 'function') continue
|
|
780
|
+
const poolAcquisition = method === 'query' || method === 'execute' ? 'measure' : 'observe'
|
|
781
|
+
shimmer.wrap(pool, method, createWrapPoolCommand(options, createWrapper, undefined, poolAcquisition))
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Wraps transaction helpers whose bundled implementations bypass the public query methods.
|
|
787
|
+
*
|
|
788
|
+
* @param {object} client
|
|
789
|
+
* @param {object} options
|
|
790
|
+
* @param {(options: object, sql: string, owner?: object, commandArity?: number) =>
|
|
791
|
+
* (command: Function) => Function} createWrapper
|
|
792
|
+
* @returns {void}
|
|
793
|
+
*/
|
|
794
|
+
function wrapTransactionMethods (client, options, createWrapper) {
|
|
795
|
+
for (const [method, sql] of transactionMethods) {
|
|
796
|
+
shimmer.wrap(client, method, createWrapTransaction(options, sql, createWrapper))
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Traces a transaction helper only when MariaDB sends its command.
|
|
802
|
+
*
|
|
803
|
+
* @param {object} options
|
|
804
|
+
* @param {string} sql
|
|
805
|
+
* @param {(options: object, sql: string, owner?: object, commandArity?: number) =>
|
|
806
|
+
* (command: Function) => Function} createWrapper
|
|
807
|
+
* @returns {(transaction: Function) => Function}
|
|
808
|
+
*/
|
|
809
|
+
function createWrapTransaction (options, sql, createWrapper) {
|
|
810
|
+
return function wrapTransaction (transaction) {
|
|
811
|
+
const wrapCommand = createWrapper(options, sql, undefined, transaction.length)
|
|
812
|
+
const tracedTransaction = wrapCommand(function () {
|
|
813
|
+
return skipCh.runStores({}, transaction, this, ...arguments)
|
|
814
|
+
})
|
|
815
|
+
|
|
816
|
+
return function () {
|
|
817
|
+
if (!startCh.hasSubscribers) return transaction.apply(this, arguments)
|
|
818
|
+
|
|
819
|
+
if (sql !== 'START TRANSACTION' &&
|
|
820
|
+
!activeCommands.has(this) &&
|
|
821
|
+
!(this.info?.status & STATUS_IN_TRANSACTION)) {
|
|
822
|
+
return transaction.apply(this, arguments)
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
return tracedTransaction.apply(this, arguments)
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Wraps a promise connection and the prepared statements it creates.
|
|
832
|
+
*
|
|
833
|
+
* @param {object} connection
|
|
834
|
+
* @param {object} options
|
|
835
|
+
* @returns {object}
|
|
836
|
+
*/
|
|
837
|
+
function wrapPromiseConnection (connection, options) {
|
|
838
|
+
wrapClientCommands(connection, createWrapPromiseCommand(options))
|
|
839
|
+
if (wrappedConnections.has(connection)) return connection
|
|
840
|
+
|
|
841
|
+
wrappedConnections.add(connection)
|
|
842
|
+
for (const method of trackedCommandMethods) {
|
|
843
|
+
if (typeof connection[method] === 'function') shimmer.wrap(connection, method, createTrackPromiseCommand)
|
|
844
|
+
}
|
|
845
|
+
shimmer.wrap(connection, 'importFile', createWrapPromiseCommand(options, IMPORT_FILE_RESOURCE))
|
|
846
|
+
if (typeof connection.queryStream === 'function') {
|
|
847
|
+
shimmer.wrap(connection, 'queryStream', createWrapStream(options))
|
|
848
|
+
}
|
|
849
|
+
shimmer.wrap(connection, 'prepare', createWrapPromisePrepare(options))
|
|
850
|
+
wrapTransactionMethods(connection, options, createWrapPromiseCommand)
|
|
851
|
+
|
|
852
|
+
return connection
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Wraps a callback connection and the prepared statements it creates.
|
|
857
|
+
*
|
|
858
|
+
* @param {object} connection
|
|
859
|
+
* @param {object} options
|
|
860
|
+
* @returns {object}
|
|
861
|
+
*/
|
|
862
|
+
function wrapCallbackConnection (connection, options) {
|
|
863
|
+
wrapClientCommands(connection, createWrapCallbackCommand(options))
|
|
864
|
+
if (wrappedConnections.has(connection)) return connection
|
|
865
|
+
|
|
866
|
+
wrappedConnections.add(connection)
|
|
867
|
+
for (const method of trackedCommandMethods) {
|
|
868
|
+
if (typeof connection[method] === 'function') shimmer.wrap(connection, method, createTrackCallbackCommand)
|
|
869
|
+
}
|
|
870
|
+
shimmer.wrap(connection, 'importFile', createWrapCallbackCommand(options, IMPORT_FILE_RESOURCE))
|
|
871
|
+
if (typeof connection.queryStream === 'function') {
|
|
872
|
+
shimmer.wrap(connection, 'queryStream', createWrapStream(options))
|
|
873
|
+
}
|
|
874
|
+
shimmer.wrap(connection, 'prepare', createWrapCallbackPrepare(options))
|
|
875
|
+
wrapTransactionMethods(connection, options, createWrapCallbackCommand)
|
|
876
|
+
|
|
877
|
+
return connection
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Wraps prepared statements created by a promise connection.
|
|
882
|
+
*
|
|
883
|
+
* @param {object} options
|
|
884
|
+
* @returns {(prepare: Function) => Function}
|
|
885
|
+
*/
|
|
886
|
+
function createWrapPromisePrepare (options) {
|
|
887
|
+
return function wrapPrepare (prepare) {
|
|
888
|
+
return function (sql) {
|
|
889
|
+
const connection = this
|
|
890
|
+
const preparedSql = normalizeSql(sql)
|
|
891
|
+
return prepare.apply(this, arguments).then(statement => {
|
|
892
|
+
shimmer.wrap(statement, 'execute', createWrapPromiseCommand(options, preparedSql, connection))
|
|
893
|
+
if (typeof statement.executeStream === 'function') {
|
|
894
|
+
shimmer.wrap(statement, 'executeStream', createWrapStream(options, preparedSql, connection))
|
|
895
|
+
}
|
|
896
|
+
return statement
|
|
897
|
+
})
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Wraps prepared statements created by a callback connection.
|
|
904
|
+
*
|
|
905
|
+
* @param {object} options
|
|
906
|
+
* @returns {(prepare: Function) => Function}
|
|
907
|
+
*/
|
|
908
|
+
function createWrapCallbackPrepare (options) {
|
|
909
|
+
return function wrapPrepare (prepare) {
|
|
910
|
+
return function (sql) {
|
|
911
|
+
const connection = this
|
|
912
|
+
const preparedSql = normalizeSql(sql)
|
|
913
|
+
const callback = arguments[arguments.length - 1]
|
|
914
|
+
if (typeof callback !== 'function') return prepare.apply(this, arguments)
|
|
915
|
+
|
|
916
|
+
arguments[arguments.length - 1] = function () {
|
|
917
|
+
const statement = arguments[1]
|
|
918
|
+
if (statement) {
|
|
919
|
+
shimmer.wrap(
|
|
920
|
+
statement,
|
|
921
|
+
'execute',
|
|
922
|
+
createWrapCallbackPreparedExecute(options, preparedSql, connection)
|
|
923
|
+
)
|
|
924
|
+
if (typeof statement.executeStream === 'function') {
|
|
925
|
+
shimmer.wrap(statement, 'executeStream', createWrapStream(options, preparedSql, connection))
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return callback.apply(this, arguments)
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
return prepare.apply(this, arguments)
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Wraps callback prepared statements, which return a promise when no callback is provided.
|
|
938
|
+
*
|
|
939
|
+
* @param {object} options
|
|
940
|
+
* @param {unknown} sql
|
|
941
|
+
* @param {object} connection
|
|
942
|
+
* @returns {(execute: Function) => Function}
|
|
943
|
+
*/
|
|
944
|
+
function createWrapCallbackPreparedExecute (options, sql, connection) {
|
|
945
|
+
return function wrapExecute (execute) {
|
|
946
|
+
const wrapCallbackCommand = createWrapCallbackCommand(options, sql, connection)
|
|
947
|
+
const wrapPromiseCommand = createWrapPromiseCommand(options, sql, connection)
|
|
948
|
+
const executeWithCallback = wrapCallbackCommand(execute)
|
|
949
|
+
const executeWithPromise = wrapPromiseCommand(execute)
|
|
950
|
+
|
|
951
|
+
return function () {
|
|
952
|
+
const hasCallback = typeof arguments[1] === 'function' || typeof arguments[2] === 'function'
|
|
953
|
+
const wrappedExecute = hasCallback ? executeWithCallback : executeWithPromise
|
|
954
|
+
return wrappedExecute.apply(this, arguments)
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Runs bundled pool internals in the skip store while tracing the public command.
|
|
961
|
+
*
|
|
962
|
+
* @param {object} options
|
|
963
|
+
* @param {(options: object, sql?: unknown, owner?: object, commandArity?: number,
|
|
964
|
+
* trackActiveCommands?: boolean, poolAcquisition?: 'measure' | 'observe') =>
|
|
965
|
+
* (command: Function) => Function} createWrapper
|
|
966
|
+
* @param {unknown} [preparedSql]
|
|
967
|
+
* @param {'measure' | 'observe'} [poolAcquisition]
|
|
968
|
+
* @returns {(command: Function) => Function}
|
|
969
|
+
*/
|
|
970
|
+
function createWrapPoolCommand (options, createWrapper, preparedSql, poolAcquisition) {
|
|
971
|
+
return function wrapPoolCommand (command) {
|
|
972
|
+
const wrapCommand = createWrapper(options, preparedSql, undefined, command.length, false, poolAcquisition)
|
|
973
|
+
return wrapCommand(function () {
|
|
974
|
+
return skipCh.runStores({}, command, this, ...arguments)
|
|
975
|
+
})
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Restores the caller context and instruments a pooled promise connection.
|
|
981
|
+
*
|
|
982
|
+
* @param {object} ctx
|
|
983
|
+
* @param {object} connection
|
|
984
|
+
* @param {object} options
|
|
985
|
+
* @returns {object}
|
|
986
|
+
*/
|
|
987
|
+
function finishPromiseGetConnection (ctx, connection, options) {
|
|
988
|
+
return connectionFinishCh.runStores(ctx, wrapPromiseConnection, undefined, connection, options)
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Restores the caller context when a promise acquisition fails.
|
|
993
|
+
*
|
|
994
|
+
* @param {object} ctx
|
|
995
|
+
* @param {Error} error
|
|
996
|
+
* @throws {Error} The connection acquisition error.
|
|
997
|
+
*/
|
|
998
|
+
function finishPromiseGetConnectionError (ctx, error) {
|
|
999
|
+
return connectionFinishCh.runStores(ctx, () => { throw error })
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Finishes an explicit promise acquisition and instruments its connection in the caller context.
|
|
1004
|
+
*
|
|
1005
|
+
* @param {PoolAcquisition} acquisition
|
|
1006
|
+
* @param {object} connection
|
|
1007
|
+
* @param {object} options
|
|
1008
|
+
* @returns {object}
|
|
1009
|
+
*/
|
|
1010
|
+
function finishExplicitPromiseGetConnection (acquisition, connection, options) {
|
|
1011
|
+
return connectionFinishCh.runStores(acquisition.connectionCtx, () => {
|
|
1012
|
+
finishExplicitPoolAcquisition(acquisition)
|
|
1013
|
+
return wrapPromiseConnection(connection, options)
|
|
1014
|
+
})
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* Finishes a failed explicit promise acquisition in the caller context.
|
|
1019
|
+
*
|
|
1020
|
+
* @param {PoolAcquisition} acquisition
|
|
1021
|
+
* @param {Error} error
|
|
1022
|
+
* @throws {Error} The connection acquisition error.
|
|
1023
|
+
*/
|
|
1024
|
+
function finishExplicitPromiseGetConnectionError (acquisition, error) {
|
|
1025
|
+
return connectionFinishCh.runStores(acquisition.connectionCtx, () => {
|
|
1026
|
+
finishExplicitPoolAcquisition(acquisition, error)
|
|
1027
|
+
throw error
|
|
1028
|
+
})
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Wraps getConnection on a bundled promise pool.
|
|
1033
|
+
*
|
|
1034
|
+
* @param {object} options
|
|
1035
|
+
* @returns {(getConnection: Function) => Function}
|
|
1036
|
+
*/
|
|
1037
|
+
function createWrapPromiseGetConnection (options) {
|
|
1038
|
+
return function wrapGetConnection (getConnection) {
|
|
1039
|
+
return function () {
|
|
1040
|
+
if (!connectionStartCh.hasSubscribers) return getConnection.apply(this, arguments)
|
|
1041
|
+
|
|
1042
|
+
if (!acquireStartCh.hasSubscribers) {
|
|
1043
|
+
const ctx = {}
|
|
1044
|
+
connectionStartCh.publish(ctx)
|
|
1045
|
+
return skipCh.runStores({}, getConnection, this, ...arguments).then(
|
|
1046
|
+
connection => finishPromiseGetConnection(ctx, connection, options),
|
|
1047
|
+
error => finishPromiseGetConnectionError(ctx, error)
|
|
1048
|
+
)
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const acquisition = createPoolAcquisition(this, options, undefined, 'explicit')
|
|
1052
|
+
let result
|
|
1053
|
+
|
|
1054
|
+
try {
|
|
1055
|
+
result = runPoolAcquisition(acquisition, runSkippedPoolMethod, undefined, [getConnection, this, arguments])
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
return finishExplicitPromiseGetConnectionError(acquisition, error)
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
return result.then(
|
|
1061
|
+
connection => finishExplicitPromiseGetConnection(acquisition, connection, options),
|
|
1062
|
+
error => finishExplicitPromiseGetConnectionError(acquisition, error)
|
|
1063
|
+
)
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Wraps getConnection on a bundled callback pool.
|
|
1070
|
+
*
|
|
1071
|
+
* @param {object} options
|
|
1072
|
+
* @returns {(getConnection: Function) => Function}
|
|
1073
|
+
*/
|
|
1074
|
+
function createWrapCallbackGetConnection (options) {
|
|
1075
|
+
return function wrapGetConnection (getConnection) {
|
|
1076
|
+
return function () {
|
|
1077
|
+
const callback = arguments[arguments.length - 1]
|
|
1078
|
+
if (typeof callback !== 'function') return getConnection.apply(this, arguments)
|
|
1079
|
+
|
|
1080
|
+
if (!connectionStartCh.hasSubscribers) return getConnection.apply(this, arguments)
|
|
1081
|
+
|
|
1082
|
+
if (!acquireStartCh.hasSubscribers) {
|
|
1083
|
+
const ctx = {}
|
|
1084
|
+
arguments[arguments.length - 1] = function () {
|
|
1085
|
+
const connection = arguments[1]
|
|
1086
|
+
if (connection) wrapCallbackConnection(connection, options)
|
|
1087
|
+
return connectionFinishCh.runStores(ctx, callback, this, ...arguments)
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
connectionStartCh.publish(ctx)
|
|
1091
|
+
return skipCh.runStores({}, getConnection, this, ...arguments)
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
const acquisition = createPoolAcquisition(this, options, undefined, 'explicit')
|
|
1095
|
+
arguments[arguments.length - 1] = function (error, connection) {
|
|
1096
|
+
if (connection) wrapCallbackConnection(connection, options)
|
|
1097
|
+
return connectionFinishCh.runStores(acquisition.connectionCtx, () => {
|
|
1098
|
+
finishExplicitPoolAcquisition(acquisition, error)
|
|
1099
|
+
return callback.apply(this, arguments)
|
|
1100
|
+
})
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
try {
|
|
1104
|
+
return runPoolAcquisition(acquisition, runSkippedPoolMethod, undefined, [getConnection, this, arguments])
|
|
1105
|
+
} catch (error) {
|
|
1106
|
+
return finishExplicitPromiseGetConnectionError(acquisition, error)
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* Instruments connection wrappers emitted when a bundled pool creates a connection.
|
|
1114
|
+
*
|
|
1115
|
+
* @param {object} pool
|
|
1116
|
+
* @param {object} options
|
|
1117
|
+
* @param {(connection: object, options: object) => object} wrapConnection
|
|
1118
|
+
* @returns {void}
|
|
1119
|
+
*/
|
|
1120
|
+
function wrapPoolConnectionEvent (pool, options, wrapConnection) {
|
|
1121
|
+
shimmer.wrap(pool, 'emit', emit => function (event, connection) {
|
|
1122
|
+
if (event !== 'connection') return emit.apply(this, arguments)
|
|
1123
|
+
wrapConnection(connection, options)
|
|
1124
|
+
return connectionFinishCh.runStores(emptyConnectionContext, emit, this, ...arguments)
|
|
1125
|
+
})
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* Captures the connection options registered with a pool cluster.
|
|
1130
|
+
*
|
|
1131
|
+
* @param {object} cluster
|
|
1132
|
+
* @param {Function} defaultOptions
|
|
1133
|
+
* @param {ClusterSelectionStorage} selectionStorage
|
|
1134
|
+
* @returns {void}
|
|
1135
|
+
*/
|
|
1136
|
+
function captureClusterOptions (cluster, defaultOptions, selectionStorage) {
|
|
1137
|
+
/** @type {Map<string, ClusterNodeOptions>} */
|
|
1138
|
+
const optionsByIdentifier = new Map()
|
|
1139
|
+
let nodeCounter = 0
|
|
1140
|
+
|
|
1141
|
+
const removeOptions = identifier => {
|
|
1142
|
+
const nodeOptions = optionsByIdentifier.get(identifier)
|
|
1143
|
+
if (nodeOptions?.pendingRemovals) {
|
|
1144
|
+
nodeOptions.pendingRemovals--
|
|
1145
|
+
} else {
|
|
1146
|
+
optionsByIdentifier.delete(identifier)
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// ClusterCallback.on is bound to its private Cluster, so EventEmitter returns the internal
|
|
1151
|
+
// runtime object for both APIs. MariaDB does not expose the selected node on the returned
|
|
1152
|
+
// connection; capture _selectPool while the acquisition's async-local selection is active.
|
|
1153
|
+
const internalCluster = cluster.on('remove', removeOptions)
|
|
1154
|
+
if (typeof internalCluster._selectPool === 'function') {
|
|
1155
|
+
shimmer.wrap(internalCluster, '_selectPool', selectPool => function () {
|
|
1156
|
+
const identifier = selectPool.apply(this, arguments)
|
|
1157
|
+
const selection = selectionStorage.getStore()
|
|
1158
|
+
if (selection !== undefined) selection.options = optionsByIdentifier.get(identifier)?.options
|
|
1159
|
+
return identifier
|
|
1160
|
+
})
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
shimmer.wrap(cluster, 'add', add => function (identifier, options) {
|
|
1164
|
+
const hasIdentifier = typeof identifier === 'string' ||
|
|
1165
|
+
Object.prototype.toString.call(identifier) === '[object String]'
|
|
1166
|
+
const generatedIdentifier = hasIdentifier ? String(identifier) : `PoolNode-${nodeCounter++}`
|
|
1167
|
+
const connectionOptions = hasIdentifier ? options : identifier
|
|
1168
|
+
const result = skipCh.runStores({}, add, this, ...arguments)
|
|
1169
|
+
const previousNodeOptions = optionsByIdentifier.get(generatedIdentifier)
|
|
1170
|
+
|
|
1171
|
+
// MariaDB deletes a failed node before emitting its delayed remove event. A successful
|
|
1172
|
+
// same-identifier add while options remain therefore adds one stale event to ignore.
|
|
1173
|
+
const nodeOptions = {
|
|
1174
|
+
options: normalizeOptions(defaultOptions, connectionOptions),
|
|
1175
|
+
pendingRemovals: previousNodeOptions === undefined ? 0 : previousNodeOptions.pendingRemovals + 1,
|
|
1176
|
+
}
|
|
1177
|
+
optionsByIdentifier.set(generatedIdentifier, nodeOptions)
|
|
1178
|
+
|
|
1179
|
+
return result
|
|
1180
|
+
})
|
|
1181
|
+
|
|
1182
|
+
shimmer.wrap(cluster, 'remove', remove => function (pattern) {
|
|
1183
|
+
const result = remove.apply(this, arguments)
|
|
1184
|
+
removeClusterOptions(optionsByIdentifier, pattern)
|
|
1185
|
+
return result
|
|
1186
|
+
})
|
|
1187
|
+
|
|
1188
|
+
shimmer.wrap(cluster, 'end', end => function () {
|
|
1189
|
+
const result = end.apply(this, arguments)
|
|
1190
|
+
optionsByIdentifier.clear()
|
|
1191
|
+
internalCluster.removeListener('remove', removeOptions)
|
|
1192
|
+
return result
|
|
1193
|
+
})
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Removes options for every cluster node matching a selector.
|
|
1198
|
+
*
|
|
1199
|
+
* @param {Map<string, ClusterNodeOptions>} optionsByIdentifier
|
|
1200
|
+
* @param {string} pattern
|
|
1201
|
+
* @returns {void}
|
|
1202
|
+
*/
|
|
1203
|
+
function removeClusterOptions (optionsByIdentifier, pattern) {
|
|
1204
|
+
const regularExpression = new RegExp(pattern)
|
|
1205
|
+
|
|
1206
|
+
for (const identifier of optionsByIdentifier.keys()) {
|
|
1207
|
+
regularExpression.lastIndex = 0
|
|
1208
|
+
if (regularExpression.test(identifier)) optionsByIdentifier.delete(identifier)
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
* Calls a cluster acquisition inside the pool-skip context while selection storage is active.
|
|
1214
|
+
*
|
|
1215
|
+
* @param {Function} getConnection
|
|
1216
|
+
* @param {object} receiver
|
|
1217
|
+
* @param {ArgumentsLike} args
|
|
1218
|
+
* @returns {unknown}
|
|
1219
|
+
*/
|
|
1220
|
+
function runClusterGetConnection (getConnection, receiver, args) {
|
|
1221
|
+
return skipCh.runStores({}, getConnection, receiver, ...args)
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
/**
|
|
1225
|
+
* Reports a failed bundled cluster node acquisition.
|
|
1226
|
+
*
|
|
1227
|
+
* @param {ClusterSelection} selection
|
|
1228
|
+
* @param {number | undefined} start
|
|
1229
|
+
* @param {unknown} error
|
|
1230
|
+
* @returns {void}
|
|
1231
|
+
*/
|
|
1232
|
+
function reportBundledClusterAcquireError (selection, start, error) {
|
|
1233
|
+
if (!acquireStartCh.hasSubscribers) return
|
|
1234
|
+
reportPoolAcquireError(start, error, { conf: selection.options ?? emptyOptions }, poolAcquireChannels)
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/**
|
|
1238
|
+
* Restores promise caller context and reports a failed bundled cluster node acquisition.
|
|
1239
|
+
*
|
|
1240
|
+
* @param {object} ctx
|
|
1241
|
+
* @param {ClusterSelection} selection
|
|
1242
|
+
* @param {number | undefined} start
|
|
1243
|
+
* @param {unknown} error
|
|
1244
|
+
* @throws {unknown} The connection acquisition error.
|
|
1245
|
+
*/
|
|
1246
|
+
function finishPromiseClusterGetConnectionError (ctx, selection, start, error) {
|
|
1247
|
+
return connectionFinishCh.runStores(ctx, () => {
|
|
1248
|
+
reportBundledClusterAcquireError(selection, start, error)
|
|
1249
|
+
throw error
|
|
1250
|
+
})
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* Restores callback caller context and finishes a bundled cluster acquisition.
|
|
1255
|
+
*
|
|
1256
|
+
* @param {ClusterSelection} selection
|
|
1257
|
+
* @param {number | undefined} start
|
|
1258
|
+
* @param {Function} callback
|
|
1259
|
+
* @param {unknown} receiver
|
|
1260
|
+
* @param {ArgumentsLike} args
|
|
1261
|
+
* @returns {unknown}
|
|
1262
|
+
*/
|
|
1263
|
+
function finishCallbackClusterGetConnection (selection, start, callback, receiver, args) {
|
|
1264
|
+
const error = args[0]
|
|
1265
|
+
const connection = args[1]
|
|
1266
|
+
if (error) reportBundledClusterAcquireError(selection, start, error)
|
|
1267
|
+
if (connection) wrapCallbackConnection(connection, selection.options ?? emptyOptions)
|
|
1268
|
+
return callback.apply(receiver, args)
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* Wraps promise connections acquired from a bundled pool cluster.
|
|
1273
|
+
*
|
|
1274
|
+
* @param {ClusterSelectionStorage} selectionStorage
|
|
1275
|
+
* @returns {(getConnection: Function) => Function}
|
|
1276
|
+
*/
|
|
1277
|
+
function createWrapPromiseClusterGetConnection (selectionStorage) {
|
|
1278
|
+
return function wrapGetConnection (getConnection) {
|
|
1279
|
+
return function () {
|
|
1280
|
+
const ctx = {}
|
|
1281
|
+
/** @type {ClusterSelection} */
|
|
1282
|
+
const selection = {}
|
|
1283
|
+
const start = acquireStartCh.hasSubscribers ? performance.now() : undefined
|
|
1284
|
+
|
|
1285
|
+
connectionStartCh.publish(ctx)
|
|
1286
|
+
|
|
1287
|
+
const result = selectionStorage.run(
|
|
1288
|
+
selection,
|
|
1289
|
+
runClusterGetConnection,
|
|
1290
|
+
getConnection,
|
|
1291
|
+
this,
|
|
1292
|
+
arguments
|
|
1293
|
+
)
|
|
1294
|
+
|
|
1295
|
+
return result.then(
|
|
1296
|
+
connection => finishPromiseGetConnection(ctx, connection, selection.options ?? emptyOptions),
|
|
1297
|
+
error => finishPromiseClusterGetConnectionError(ctx, selection, start, error)
|
|
1298
|
+
)
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* Wraps callback connections acquired from a bundled pool cluster.
|
|
1305
|
+
*
|
|
1306
|
+
* @param {ClusterSelectionStorage} selectionStorage
|
|
1307
|
+
* @returns {(getConnection: Function) => Function}
|
|
1308
|
+
*/
|
|
1309
|
+
function createWrapCallbackClusterGetConnection (selectionStorage) {
|
|
1310
|
+
return function wrapGetConnection (getConnection) {
|
|
1311
|
+
return function () {
|
|
1312
|
+
const callback = arguments[arguments.length - 1]
|
|
1313
|
+
if (typeof callback !== 'function') return getConnection.apply(this, arguments)
|
|
1314
|
+
|
|
1315
|
+
const ctx = {}
|
|
1316
|
+
/** @type {ClusterSelection} */
|
|
1317
|
+
const selection = {}
|
|
1318
|
+
const start = acquireStartCh.hasSubscribers ? performance.now() : undefined
|
|
1319
|
+
arguments[arguments.length - 1] = function () {
|
|
1320
|
+
return connectionFinishCh.runStores(
|
|
1321
|
+
ctx,
|
|
1322
|
+
finishCallbackClusterGetConnection,
|
|
1323
|
+
undefined,
|
|
1324
|
+
selection,
|
|
1325
|
+
start,
|
|
1326
|
+
callback,
|
|
1327
|
+
this,
|
|
1328
|
+
arguments
|
|
1329
|
+
)
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
connectionStartCh.publish(ctx)
|
|
1333
|
+
|
|
1334
|
+
return selectionStorage.run(
|
|
1335
|
+
selection,
|
|
1336
|
+
runClusterGetConnection,
|
|
1337
|
+
getConnection,
|
|
1338
|
+
this,
|
|
1339
|
+
arguments
|
|
1340
|
+
)
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/**
|
|
1346
|
+
* Wraps promise connections acquired from a bundled pool cluster.
|
|
1347
|
+
*
|
|
1348
|
+
* @param {object} cluster
|
|
1349
|
+
* @param {Function} defaultOptions
|
|
1350
|
+
* @returns {object}
|
|
1351
|
+
*/
|
|
1352
|
+
function wrapPromiseCluster (cluster, defaultOptions) {
|
|
1353
|
+
const selectionStorage = getClusterSelectionStorage()
|
|
1354
|
+
captureClusterOptions(cluster, defaultOptions, selectionStorage)
|
|
1355
|
+
|
|
1356
|
+
shimmer.wrap(cluster, 'getConnection', createWrapPromiseClusterGetConnection(selectionStorage))
|
|
1357
|
+
|
|
1358
|
+
return cluster
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/**
|
|
1362
|
+
* Wraps callback connections acquired from a bundled pool cluster.
|
|
1363
|
+
*
|
|
1364
|
+
* @param {object} cluster
|
|
1365
|
+
* @param {Function} defaultOptions
|
|
1366
|
+
* @returns {object}
|
|
1367
|
+
*/
|
|
1368
|
+
function wrapCallbackCluster (cluster, defaultOptions) {
|
|
1369
|
+
const selectionStorage = getClusterSelectionStorage()
|
|
1370
|
+
captureClusterOptions(cluster, defaultOptions, selectionStorage)
|
|
1371
|
+
|
|
1372
|
+
shimmer.wrap(cluster, 'getConnection', createWrapCallbackClusterGetConnection(selectionStorage))
|
|
1373
|
+
// The filtered callback facade delegates to a private Cluster instance, bypassing the public method above.
|
|
1374
|
+
shimmer.wrap(cluster, 'of', of => function () {
|
|
1375
|
+
const filteredCluster = of.apply(this, arguments)
|
|
1376
|
+
shimmer.wrap(filteredCluster, 'getConnection', createWrapCallbackClusterGetConnection(selectionStorage))
|
|
1377
|
+
return filteredCluster
|
|
1378
|
+
})
|
|
1379
|
+
|
|
1380
|
+
return cluster
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
/**
|
|
1384
|
+
* Wraps the createConnection factory from a bundled promise entry.
|
|
1385
|
+
*
|
|
1386
|
+
* @param {Function} defaultOptions
|
|
1387
|
+
* @returns {(createConnection: Function) => Function}
|
|
1388
|
+
*/
|
|
1389
|
+
function createWrapPromiseConnectionFactory (defaultOptions) {
|
|
1390
|
+
return function wrapCreateConnection (createConnection) {
|
|
1391
|
+
return function (options) {
|
|
1392
|
+
return createConnection.apply(this, arguments).then(connection => {
|
|
1393
|
+
return wrapPromiseConnection(connection, normalizeOptions(defaultOptions, options))
|
|
1394
|
+
})
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/**
|
|
1400
|
+
* Wraps the createConnection factory from a bundled callback entry.
|
|
1401
|
+
*
|
|
1402
|
+
* @param {Function} defaultOptions
|
|
1403
|
+
* @returns {(createConnection: Function) => Function}
|
|
1404
|
+
*/
|
|
1405
|
+
function createWrapCallbackConnectionFactory (defaultOptions) {
|
|
1406
|
+
return function wrapCreateConnection (createConnection) {
|
|
1407
|
+
return function (options) {
|
|
1408
|
+
const connection = createConnection.apply(this, arguments)
|
|
1409
|
+
return wrapCallbackConnection(connection, normalizeOptions(defaultOptions, options))
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* Wraps the createPool factory from a bundled promise entry.
|
|
1416
|
+
*
|
|
1417
|
+
* @param {Function} defaultOptions
|
|
1418
|
+
* @returns {(createPool: Function) => Function}
|
|
1419
|
+
*/
|
|
1420
|
+
function createWrapPromisePoolFactory (defaultOptions) {
|
|
1421
|
+
return function wrapCreatePool (createPool) {
|
|
1422
|
+
return function (options) {
|
|
1423
|
+
const pool = skipCh.runStores({}, createPool, this, ...arguments)
|
|
1424
|
+
const normalizedOptions = normalizeOptions(defaultOptions, options)
|
|
1425
|
+
|
|
1426
|
+
observePoolAcquisitions(pool)
|
|
1427
|
+
wrapPoolConnectionEvent(pool, normalizedOptions, wrapPromiseConnection)
|
|
1428
|
+
wrapPoolCommands(pool, normalizedOptions, createWrapPromiseCommand)
|
|
1429
|
+
shimmer.wrap(
|
|
1430
|
+
pool,
|
|
1431
|
+
'importFile',
|
|
1432
|
+
createWrapPoolCommand(normalizedOptions, createWrapPromiseCommand, IMPORT_FILE_RESOURCE, 'observe')
|
|
1433
|
+
)
|
|
1434
|
+
shimmer.wrap(pool, 'getConnection', createWrapPromiseGetConnection(normalizedOptions))
|
|
1435
|
+
|
|
1436
|
+
return pool
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* Wraps the createPool factory from a bundled callback entry.
|
|
1443
|
+
*
|
|
1444
|
+
* @param {Function} defaultOptions
|
|
1445
|
+
* @returns {(createPool: Function) => Function}
|
|
1446
|
+
*/
|
|
1447
|
+
function createWrapCallbackPoolFactory (defaultOptions) {
|
|
1448
|
+
return function wrapCreatePool (createPool) {
|
|
1449
|
+
return function (options) {
|
|
1450
|
+
const pool = skipCh.runStores({}, createPool, this, ...arguments)
|
|
1451
|
+
const normalizedOptions = normalizeOptions(defaultOptions, options)
|
|
1452
|
+
|
|
1453
|
+
observePoolAcquisitions(pool)
|
|
1454
|
+
wrapPoolConnectionEvent(pool, normalizedOptions, wrapCallbackConnection)
|
|
1455
|
+
wrapPoolCommands(pool, normalizedOptions, createWrapCallbackCommand)
|
|
1456
|
+
shimmer.wrap(
|
|
1457
|
+
pool,
|
|
1458
|
+
'importFile',
|
|
1459
|
+
createWrapPoolCommand(normalizedOptions, createWrapCallbackCommand, IMPORT_FILE_RESOURCE, 'observe')
|
|
1460
|
+
)
|
|
1461
|
+
shimmer.wrap(pool, 'getConnection', createWrapCallbackGetConnection(normalizedOptions))
|
|
1462
|
+
|
|
1463
|
+
return pool
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/**
|
|
1469
|
+
* Wraps the createPoolCluster factory from a bundled promise entry.
|
|
1470
|
+
*
|
|
1471
|
+
* @param {Function} defaultOptions
|
|
1472
|
+
* @returns {(createPoolCluster: Function) => Function}
|
|
1473
|
+
*/
|
|
1474
|
+
function createWrapPromiseClusterFactory (defaultOptions) {
|
|
1475
|
+
return function wrapCreatePoolCluster (createPoolCluster) {
|
|
1476
|
+
return function () {
|
|
1477
|
+
return wrapPromiseCluster(createPoolCluster.apply(this, arguments), defaultOptions)
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
/**
|
|
1483
|
+
* Wraps the createPoolCluster factory from a bundled callback entry.
|
|
1484
|
+
*
|
|
1485
|
+
* @param {Function} defaultOptions
|
|
1486
|
+
* @returns {(createPoolCluster: Function) => Function}
|
|
1487
|
+
*/
|
|
1488
|
+
function createWrapCallbackClusterFactory (defaultOptions) {
|
|
1489
|
+
return function wrapCreatePoolCluster (createPoolCluster) {
|
|
1490
|
+
return function () {
|
|
1491
|
+
return wrapCallbackCluster(createPoolCluster.apply(this, arguments), defaultOptions)
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
/**
|
|
1497
|
+
* Wraps the top-level importFile helper from a bundled promise entry.
|
|
1498
|
+
*
|
|
1499
|
+
* @param {Function} defaultOptions
|
|
1500
|
+
* @returns {(importFile: Function) => Function}
|
|
1501
|
+
*/
|
|
1502
|
+
function createWrapPromiseImportFile (defaultOptions) {
|
|
1503
|
+
return function wrapImportFile (importFile) {
|
|
1504
|
+
return function (options) {
|
|
1505
|
+
const wrapCommand = createWrapPromiseCommand(
|
|
1506
|
+
normalizeOptions(defaultOptions, options),
|
|
1507
|
+
IMPORT_FILE_RESOURCE,
|
|
1508
|
+
undefined,
|
|
1509
|
+
undefined,
|
|
1510
|
+
false
|
|
1511
|
+
)
|
|
1512
|
+
const wrapper = wrapCommand(importFile)
|
|
1513
|
+
return wrapper.apply(this, arguments)
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/**
|
|
1519
|
+
* Wraps the top-level importFile helper from a bundled callback entry.
|
|
1520
|
+
*
|
|
1521
|
+
* @param {Function} defaultOptions
|
|
1522
|
+
* @returns {(importFile: Function) => Function}
|
|
1523
|
+
*/
|
|
1524
|
+
function createWrapCallbackImportFile (defaultOptions) {
|
|
1525
|
+
return function wrapImportFile (importFile) {
|
|
1526
|
+
return function (options) {
|
|
1527
|
+
const wrapCommand = createWrapCallbackCommand(
|
|
1528
|
+
normalizeOptions(defaultOptions, options),
|
|
1529
|
+
IMPORT_FILE_RESOURCE,
|
|
1530
|
+
undefined,
|
|
1531
|
+
undefined,
|
|
1532
|
+
false
|
|
1533
|
+
)
|
|
1534
|
+
const wrapper = wrapCommand(importFile)
|
|
1535
|
+
return wrapper.apply(this, arguments)
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* Wraps selected CommonJS factories in the mutable default export and its non-configurable namespace getters.
|
|
1542
|
+
*
|
|
1543
|
+
* @param {object} mariadb
|
|
1544
|
+
* @param {Array<[string, (factory: Function) => Function]>} factories
|
|
1545
|
+
* @returns {object}
|
|
1546
|
+
*/
|
|
1547
|
+
function wrapBundle (mariadb, factories) {
|
|
1548
|
+
const defaultExport = mariadb.default
|
|
1549
|
+
let wrappedBundle = mariadb
|
|
1550
|
+
|
|
1551
|
+
for (const [name, wrapper] of factories) {
|
|
1552
|
+
wrappedBundle = shimmer.wrap(wrappedBundle, name, wrapper, { replaceGetter: true })
|
|
1553
|
+
}
|
|
1554
|
+
for (const [name] of factories) {
|
|
1555
|
+
shimmer.wrap(defaultExport, name, () => wrappedBundle[name])
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
return wrappedBundle
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
/**
|
|
1562
|
+
* Instruments the promise API exported by MariaDB's 3.5.3+ CommonJS bundle.
|
|
1563
|
+
*
|
|
1564
|
+
* @param {object} mariadb
|
|
1565
|
+
* @param {string} _version
|
|
1566
|
+
* @param {boolean} isIitm
|
|
1567
|
+
* @returns {object}
|
|
1568
|
+
*/
|
|
1569
|
+
function wrapPromiseBundle (mariadb, _version, isIitm) {
|
|
1570
|
+
if (isIitm) return mariadb
|
|
1571
|
+
|
|
1572
|
+
const defaultOptions = mariadb.defaultOptions
|
|
1573
|
+
return wrapBundle(mariadb, [
|
|
1574
|
+
['createConnection', createWrapPromiseConnectionFactory(defaultOptions)],
|
|
1575
|
+
['createPool', createWrapPromisePoolFactory(defaultOptions)],
|
|
1576
|
+
['createPoolCluster', createWrapPromiseClusterFactory(defaultOptions)],
|
|
1577
|
+
['importFile', createWrapPromiseImportFile(defaultOptions)],
|
|
1578
|
+
])
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/**
|
|
1582
|
+
* Instruments the callback API exported by MariaDB's 3.5.3+ CommonJS bundle.
|
|
1583
|
+
*
|
|
1584
|
+
* @param {object} mariadb
|
|
1585
|
+
* @returns {object}
|
|
1586
|
+
*/
|
|
1587
|
+
function wrapCallbackBundle (mariadb) {
|
|
1588
|
+
const defaultOptions = mariadb.defaultOptions
|
|
1589
|
+
return wrapBundle(mariadb, [
|
|
1590
|
+
['createConnection', createWrapCallbackConnectionFactory(defaultOptions)],
|
|
1591
|
+
['createPool', createWrapCallbackPoolFactory(defaultOptions)],
|
|
1592
|
+
['createPoolCluster', createWrapCallbackClusterFactory(defaultOptions)],
|
|
1593
|
+
['importFile', createWrapCallbackImportFile(defaultOptions)],
|
|
1594
|
+
])
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
module.exports = { wrapCallbackBundle, wrapPromiseBundle }
|