velocious 1.0.710 → 1.0.712
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/build/configuration-types.js +1 -1
- package/build/configuration.js +14 -0
- package/build/database/advisory-lock-runner.js +3 -7
- package/build/database/advisory-lock-telemetry.js +59 -22
- package/build/src/configuration-types.d.ts +2 -2
- package/build/src/configuration-types.js +2 -2
- package/build/src/configuration.d.ts +11 -0
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +14 -1
- package/build/src/database/advisory-lock-runner.d.ts.map +1 -1
- package/build/src/database/advisory-lock-runner.js +4 -7
- package/build/src/database/advisory-lock-telemetry.d.ts +33 -14
- package/build/src/database/advisory-lock-telemetry.d.ts.map +1 -1
- package/build/src/database/advisory-lock-telemetry.js +57 -23
- package/package.json +1 -1
- package/src/configuration-types.js +1 -1
- package/src/configuration.js +14 -0
- package/src/database/advisory-lock-runner.js +3 -7
- package/src/database/advisory-lock-telemetry.js +59 -22
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
/**
|
|
86
86
|
* @typedef {object} DatabaseConfigurationType
|
|
87
87
|
* @property {number | null} [advisoryLockAcquireTimeoutMs] - Bound (ms) a blocking advisory-lock acquisition waits for a contended lock before reporting failure. Default: `900000` (15 minutes). `null` opts out and restores the driver's indefinite wait. A per-call `timeoutMs` argument always overrides this.
|
|
88
|
-
* @property {{slowAcquireMs?: number | null, slowHoldMs?: number | null}} [advisoryLockTelemetry] - Event-driven advisory-lock duration telemetry thresholds. `slowAcquireMs` (default `5000`) reports an acquire wait that completed slower than the threshold, including the eventual outcome; `advisory-lock-acquire-timeout` always fires when a wait exceeds its bound and carries the holder identity available on the tier at that moment. `slowHoldMs` (default `60000`) reports a hold that outlives the threshold at release. `null` disables that report. Reports fire on the events themselves — no periodic scanner — on `
|
|
88
|
+
* @property {{slowAcquireMs?: number | null, slowHoldMs?: number | null}} [advisoryLockTelemetry] - Event-driven advisory-lock duration telemetry thresholds. `slowAcquireMs` (default `5000`) reports an acquire wait that completed slower than the threshold, including the eventual outcome; `advisory-lock-acquire-timeout` always fires when a wait exceeds its bound and carries the holder identity available on the tier at that moment. `slowHoldMs` (default `60000`) reports a hold that outlives the threshold at release. `null` disables that report. Reports fire on the events themselves — no periodic scanner — on `getTelemetryEvents()` as `telemetry` with `context: "advisory-lock-telemetry"`. See [docs/advisory-locks.md](../docs/advisory-locks.md).
|
|
89
89
|
* @property {string} [databaseCharset] - Default character set applied by `db:create` via mysql/mariadb `CREATE DATABASE ... CHARACTER SET`. Distinct from `charset`, which is the client connection charset forwarded to the mysql2 driver.
|
|
90
90
|
* @property {string} [databaseCollation] - Default collation applied by `db:create` via mysql/mariadb `CREATE DATABASE ... COLLATE`.
|
|
91
91
|
* @property {string} [database] - Database name for this connection.
|
package/src/configuration.js
CHANGED
|
@@ -525,6 +525,7 @@ export default class VelociousConfiguration {
|
|
|
525
525
|
* @type {WeakSet<object>} */
|
|
526
526
|
this._appliedRouteMounts = new WeakSet()
|
|
527
527
|
this._errorEvents = new EventEmitter()
|
|
528
|
+
this._telemetryEvents = new EventEmitter()
|
|
528
529
|
|
|
529
530
|
/**
|
|
530
531
|
* Stores the database pools value.
|
|
@@ -3865,6 +3866,19 @@ export default class VelociousConfiguration {
|
|
|
3865
3866
|
return this._errorEvents
|
|
3866
3867
|
}
|
|
3867
3868
|
|
|
3869
|
+
/**
|
|
3870
|
+
* Framework telemetry emitter. Telemetry reports measurements — lock contention,
|
|
3871
|
+
* slow queries, queue depth — that are worth observing but are not failures. It is
|
|
3872
|
+
* deliberately separate from `getErrorEvents()`: a bug reporter subscribes to the
|
|
3873
|
+
* error channel on the promise that every payload there carries a thrown value, and
|
|
3874
|
+
* a contention measurement emitted as `framework-error` turns into a bug report on
|
|
3875
|
+
* every occurrence instead of a metric.
|
|
3876
|
+
* @returns {import("eventemitter3").EventEmitter} - Framework telemetry events emitter.
|
|
3877
|
+
*/
|
|
3878
|
+
getTelemetryEvents() {
|
|
3879
|
+
return this._telemetryEvents
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3868
3882
|
/**
|
|
3869
3883
|
* Registers a reporter that can add client-safe metadata to frontend-model error payloads.
|
|
3870
3884
|
* @param {import("./configuration-types.js").ClientErrorPayloadReporterType} reporter - Reporter callback.
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
AdvisoryLockServiceUnavailableError,
|
|
13
13
|
advisoryLockBackendUnavailable
|
|
14
14
|
} from "./advisory-lock-errors.js"
|
|
15
|
+
import {emitAdvisoryLockFallbackReport} from "./advisory-lock-telemetry.js"
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Provenance of one advisory-lock acquisition (STC-210 design §3): every
|
|
@@ -407,17 +408,12 @@ export default class AdvisoryLockRunner {
|
|
|
407
408
|
*/
|
|
408
409
|
reportAdvisoryLockFallback({lockName, reason, servedBy}) {
|
|
409
410
|
const policy = resolveAdvisoryLockBackendPolicy(lockName, this.configuration.getAdvisoryLockPolicy())
|
|
410
|
-
|
|
411
|
-
context: "advisory-lock-fallback",
|
|
411
|
+
emitAdvisoryLockFallbackReport(this.configuration, {
|
|
412
412
|
lockName,
|
|
413
413
|
preferredTier: policy.preferredTier,
|
|
414
414
|
reason,
|
|
415
415
|
servedBy
|
|
416
|
-
}
|
|
417
|
-
const errorEvents = this.configuration.getErrorEvents()
|
|
418
|
-
|
|
419
|
-
errorEvents.emit("framework-error", payload)
|
|
420
|
-
errorEvents.emit("all-error", {...payload, errorType: "framework-error"})
|
|
416
|
+
})
|
|
421
417
|
}
|
|
422
418
|
|
|
423
419
|
/**
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* A lock convoy is only fixable when it is *attributable*: which lock name was
|
|
7
7
|
* contended, on which backend tier, for how long, and who held it. These reports
|
|
8
8
|
* fire on the acquisition/release events themselves — never from a periodic
|
|
9
|
-
* scanner — so a contended lock in production surfaces on the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* scanner — so a contended lock in production surfaces on the telemetry channel
|
|
10
|
+
* with the holder identity available on that tier at that moment, without SSH
|
|
11
|
+
* forensics.
|
|
12
12
|
*
|
|
13
13
|
* Three events, each with its own threshold (all configurable per database via
|
|
14
14
|
* `advisoryLockTelemetry`, see `docs/advisory-locks.md`):
|
|
@@ -26,10 +26,15 @@
|
|
|
26
26
|
* - `advisory-lock-slow-hold` — a hold lasted longer than `slowHoldMs`
|
|
27
27
|
* (default 60000), emitted at release.
|
|
28
28
|
*
|
|
29
|
-
* Reports are emitted on `configuration.
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* Reports are emitted on `configuration.getTelemetryEvents()` as `telemetry`
|
|
30
|
+
* with `{context: "advisory-lock-telemetry", event, lockName, backendTier, ...}`
|
|
31
|
+
* payloads.
|
|
32
|
+
*
|
|
33
|
+
* These are measurements, not failures, so they are deliberately kept off the
|
|
34
|
+
* framework-error channel: a bug reporter subscribed to `framework-error` /
|
|
35
|
+
* `all-error` must be able to assume every payload carries a thrown value, and a
|
|
36
|
+
* contention report delivered there became one bug report (plus its symbolication
|
|
37
|
+
* and notification work) per event.
|
|
33
38
|
*
|
|
34
39
|
* Holder lookups are strictly best-effort *after* the acquisition already
|
|
35
40
|
* settled: a failed lookup never changes the acquisition outcome and never
|
|
@@ -89,41 +94,73 @@ export function advisoryLockTelemetryThresholds(args) {
|
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
/**
|
|
92
|
-
* Emit one telemetry report on the framework-
|
|
97
|
+
* Emit one telemetry report on the framework-telemetry channel.
|
|
93
98
|
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* emissions are guarded.
|
|
99
|
-
* @param {import("../configuration.js").default} configuration - Configuration instance owning the error channel.
|
|
99
|
+
* When no `telemetry` listener is attached, a one-line summary goes to `stderr`
|
|
100
|
+
* so a convoy is never completely silent. A listener failure can never break
|
|
101
|
+
* lock handling, so emissions are guarded.
|
|
102
|
+
* @param {import("../configuration.js").default} configuration - Configuration instance owning the telemetry channel.
|
|
100
103
|
* @param {AdvisoryLockTelemetryReport} report - The report payload.
|
|
101
104
|
* @returns {void} - No return value.
|
|
102
105
|
*/
|
|
103
106
|
export function emitAdvisoryLockTelemetryReport(configuration, report) {
|
|
104
107
|
/** @type {import("eventemitter3").EventEmitter | undefined} */
|
|
105
|
-
let
|
|
108
|
+
let telemetryEvents
|
|
106
109
|
|
|
107
110
|
try {
|
|
108
|
-
|
|
111
|
+
telemetryEvents = configuration?.getTelemetryEvents()
|
|
109
112
|
} catch {
|
|
110
113
|
return // No configuration to report through — never break lock handling.
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
if (!
|
|
116
|
+
if (!telemetryEvents) return
|
|
114
117
|
|
|
115
|
-
const hasListener =
|
|
116
|
-
|| errorEvents.listenerCount("all-error") > 0
|
|
118
|
+
const hasListener = telemetryEvents.listenerCount("telemetry") > 0
|
|
117
119
|
const payload = {context: "advisory-lock-telemetry", ...report}
|
|
118
120
|
|
|
119
121
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
telemetryEvents.emit("telemetry", payload)
|
|
123
|
+
} catch {
|
|
124
|
+
// A broken listener must never surface as a lock failure.
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!hasListener) {
|
|
128
|
+
console.error(`[velocious advisory-lock-telemetry ${report.event}] lock=${JSON.stringify(report.lockName)} tier=${report.backendTier} durationMs=${report.durationMs}${report.outcome ? ` outcome=${report.outcome}` : ""}${report.holderConnectionId ? ` holderConnectionId=${report.holderConnectionId}` : ""}${report.holderId ? ` holderId=${report.holderId}` : ""} — subscribe via configuration.getTelemetryEvents().on("telemetry", …) to route these measurements to your metrics`)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Reports one acquisition served below the lock name's preferred tier
|
|
134
|
+
* (design §3): the caller wanted the lock server and got the database, so the
|
|
135
|
+
* downgrade must be visible without being mistaken for a failure. Fallbacks are
|
|
136
|
+
* measurements, not errors, so they stay off `framework-error` and its bug
|
|
137
|
+
* reporters (same contract as the duration telemetry).
|
|
138
|
+
* @param {import("../configuration.js").default} configuration - Owning configuration.
|
|
139
|
+
* @param {{lockName: string, preferredTier: string, reason: string, servedBy: string}} report - Fallback details.
|
|
140
|
+
* @returns {void}
|
|
141
|
+
*/
|
|
142
|
+
export function emitAdvisoryLockFallbackReport(configuration, report) {
|
|
143
|
+
/** @type {import("eventemitter3").EventEmitter | undefined} */
|
|
144
|
+
let telemetryEvents
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
telemetryEvents = configuration?.getTelemetryEvents()
|
|
148
|
+
} catch {
|
|
149
|
+
return // No configuration to report through — never break lock handling.
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!telemetryEvents) return
|
|
153
|
+
|
|
154
|
+
const payload = {context: "advisory-lock-fallback", ...report}
|
|
155
|
+
const hasListener = telemetryEvents.listenerCount("telemetry") > 0
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
telemetryEvents.emit("telemetry", payload)
|
|
122
159
|
} catch {
|
|
123
160
|
// A broken listener must never surface as a lock failure.
|
|
124
161
|
}
|
|
125
162
|
|
|
126
163
|
if (!hasListener) {
|
|
127
|
-
console.error(`[velocious advisory-lock-
|
|
164
|
+
console.error(`[velocious advisory-lock-fallback] lock=${JSON.stringify(report.lockName)} preferredTier=${report.preferredTier} servedBy=${report.servedBy} reason=${report.reason}`)
|
|
128
165
|
}
|
|
129
166
|
}
|