taurusdb-core 0.1.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/README.md +21 -0
- package/dist/auth/secret-resolver.d.ts +16 -0
- package/dist/auth/secret-resolver.js +64 -0
- package/dist/auth/sql-profile-loader/env-source.d.ts +3 -0
- package/dist/auth/sql-profile-loader/env-source.js +94 -0
- package/dist/auth/sql-profile-loader/file-source.d.ts +6 -0
- package/dist/auth/sql-profile-loader/file-source.js +40 -0
- package/dist/auth/sql-profile-loader/loader.d.ts +16 -0
- package/dist/auth/sql-profile-loader/loader.js +81 -0
- package/dist/auth/sql-profile-loader/parsing.d.ts +14 -0
- package/dist/auth/sql-profile-loader/parsing.js +216 -0
- package/dist/auth/sql-profile-loader/runtime-override.d.ts +14 -0
- package/dist/auth/sql-profile-loader/runtime-override.js +52 -0
- package/dist/auth/sql-profile-loader/types.d.ts +64 -0
- package/dist/auth/sql-profile-loader/types.js +1 -0
- package/dist/auth/sql-profile-loader.d.ts +4 -0
- package/dist/auth/sql-profile-loader.js +3 -0
- package/dist/capability/feature-matrix.d.ts +5 -0
- package/dist/capability/feature-matrix.js +237 -0
- package/dist/capability/probe.d.ts +19 -0
- package/dist/capability/probe.js +139 -0
- package/dist/capability/types.d.ts +49 -0
- package/dist/capability/types.js +16 -0
- package/dist/capability/version.d.ts +3 -0
- package/dist/capability/version.js +47 -0
- package/dist/cloud/auth.d.ts +26 -0
- package/dist/cloud/auth.js +198 -0
- package/dist/cloud/instances.d.ts +46 -0
- package/dist/cloud/instances.js +224 -0
- package/dist/config/env.d.ts +1 -0
- package/dist/config/env.js +194 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.js +21 -0
- package/dist/config/redaction.d.ts +2 -0
- package/dist/config/redaction.js +19 -0
- package/dist/config/schema.d.ts +417 -0
- package/dist/config/schema.js +100 -0
- package/dist/context/datasource-resolver.d.ts +19 -0
- package/dist/context/datasource-resolver.js +71 -0
- package/dist/context/session-context.d.ts +26 -0
- package/dist/context/session-context.js +1 -0
- package/dist/diagnostics/metrics-source.d.ts +65 -0
- package/dist/diagnostics/metrics-source.js +280 -0
- package/dist/diagnostics/slow-sql-source/das-source.d.ts +43 -0
- package/dist/diagnostics/slow-sql-source/das-source.js +170 -0
- package/dist/diagnostics/slow-sql-source/factory.d.ts +5 -0
- package/dist/diagnostics/slow-sql-source/factory.js +87 -0
- package/dist/diagnostics/slow-sql-source/parsers.d.ts +7 -0
- package/dist/diagnostics/slow-sql-source/parsers.js +125 -0
- package/dist/diagnostics/slow-sql-source/taurus-api-source.d.ts +42 -0
- package/dist/diagnostics/slow-sql-source/taurus-api-source.js +149 -0
- package/dist/diagnostics/slow-sql-source/types.d.ts +40 -0
- package/dist/diagnostics/slow-sql-source/types.js +1 -0
- package/dist/diagnostics/slow-sql-source/utils.d.ts +20 -0
- package/dist/diagnostics/slow-sql-source/utils.js +170 -0
- package/dist/diagnostics/slow-sql-source.d.ts +4 -0
- package/dist/diagnostics/slow-sql-source.js +3 -0
- package/dist/diagnostics/types.d.ts +189 -0
- package/dist/diagnostics/types.js +39 -0
- package/dist/engine/data-access/locks.d.ts +8 -0
- package/dist/engine/data-access/locks.js +146 -0
- package/dist/engine/data-access/processlist.d.ts +4 -0
- package/dist/engine/data-access/processlist.js +56 -0
- package/dist/engine/data-access/statements.d.ts +10 -0
- package/dist/engine/data-access/statements.js +203 -0
- package/dist/engine/data-access/storage.d.ts +6 -0
- package/dist/engine/data-access/storage.js +96 -0
- package/dist/engine/data-access.d.ts +4 -0
- package/dist/engine/data-access.js +4 -0
- package/dist/engine/diagnostics.d.ts +7 -0
- package/dist/engine/diagnostics.js +7 -0
- package/dist/engine/helper-modules/diagnostics.d.ts +57 -0
- package/dist/engine/helper-modules/diagnostics.js +322 -0
- package/dist/engine/helper-modules/parsers.d.ts +13 -0
- package/dist/engine/helper-modules/parsers.js +283 -0
- package/dist/engine/helper-modules/sql.d.ts +12 -0
- package/dist/engine/helper-modules/sql.js +119 -0
- package/dist/engine/helper-modules/types.d.ts +103 -0
- package/dist/engine/helper-modules/types.js +1 -0
- package/dist/engine/helpers.d.ts +4 -0
- package/dist/engine/helpers.js +4 -0
- package/dist/engine/runtime.d.ts +20 -0
- package/dist/engine/runtime.js +385 -0
- package/dist/engine/types.d.ts +125 -0
- package/dist/engine/types.js +1 -0
- package/dist/engine/workflows/connection-spike.d.ts +4 -0
- package/dist/engine/workflows/connection-spike.js +316 -0
- package/dist/engine/workflows/db-hotspot.d.ts +4 -0
- package/dist/engine/workflows/db-hotspot.js +182 -0
- package/dist/engine/workflows/lock-contention-helpers/entities.d.ts +9 -0
- package/dist/engine/workflows/lock-contention-helpers/entities.js +58 -0
- package/dist/engine/workflows/lock-contention-helpers/no-match.d.ts +3 -0
- package/dist/engine/workflows/lock-contention-helpers/no-match.js +65 -0
- package/dist/engine/workflows/lock-contention-helpers/report.d.ts +21 -0
- package/dist/engine/workflows/lock-contention-helpers/report.js +104 -0
- package/dist/engine/workflows/lock-contention-helpers/root-cause.d.ts +4 -0
- package/dist/engine/workflows/lock-contention-helpers/root-cause.js +79 -0
- package/dist/engine/workflows/lock-contention-helpers/signals.d.ts +22 -0
- package/dist/engine/workflows/lock-contention-helpers/signals.js +34 -0
- package/dist/engine/workflows/lock-contention-helpers.d.ts +5 -0
- package/dist/engine/workflows/lock-contention-helpers.js +5 -0
- package/dist/engine/workflows/lock-contention.d.ts +4 -0
- package/dist/engine/workflows/lock-contention.js +67 -0
- package/dist/engine/workflows/service-latency.d.ts +4 -0
- package/dist/engine/workflows/service-latency.js +262 -0
- package/dist/engine/workflows/slow-query-helpers.d.ts +41 -0
- package/dist/engine/workflows/slow-query-helpers.js +253 -0
- package/dist/engine/workflows/slow-query.d.ts +4 -0
- package/dist/engine/workflows/slow-query.js +156 -0
- package/dist/engine/workflows/storage-pressure-helpers.d.ts +12 -0
- package/dist/engine/workflows/storage-pressure-helpers.js +281 -0
- package/dist/engine/workflows/storage-pressure.d.ts +4 -0
- package/dist/engine/workflows/storage-pressure.js +27 -0
- package/dist/engine/workflows/top-slow-sql.d.ts +4 -0
- package/dist/engine/workflows/top-slow-sql.js +222 -0
- package/dist/engine.d.ts +77 -0
- package/dist/engine.js +240 -0
- package/dist/executor/adapters/mysql.d.ts +2 -0
- package/dist/executor/adapters/mysql.js +114 -0
- package/dist/executor/connection-pool.d.ts +105 -0
- package/dist/executor/connection-pool.js +236 -0
- package/dist/executor/explain.d.ts +5 -0
- package/dist/executor/explain.js +119 -0
- package/dist/executor/query-tracker.d.ts +45 -0
- package/dist/executor/query-tracker.js +83 -0
- package/dist/executor/result-normalizer.d.ts +6 -0
- package/dist/executor/result-normalizer.js +47 -0
- package/dist/executor/sql-executor.d.ts +32 -0
- package/dist/executor/sql-executor.js +250 -0
- package/dist/executor/types.d.ts +70 -0
- package/dist/executor/types.js +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +21 -0
- package/dist/safety/confirmation-store.d.ts +44 -0
- package/dist/safety/confirmation-store.js +130 -0
- package/dist/safety/guardrail.d.ts +39 -0
- package/dist/safety/guardrail.js +99 -0
- package/dist/safety/parser/adapter.d.ts +10 -0
- package/dist/safety/parser/adapter.js +72 -0
- package/dist/safety/parser/ast-utils.d.ts +10 -0
- package/dist/safety/parser/ast-utils.js +167 -0
- package/dist/safety/parser/features.d.ts +12 -0
- package/dist/safety/parser/features.js +113 -0
- package/dist/safety/parser/index.d.ts +2 -0
- package/dist/safety/parser/index.js +1 -0
- package/dist/safety/parser/types.d.ts +76 -0
- package/dist/safety/parser/types.js +1 -0
- package/dist/safety/redaction.d.ts +34 -0
- package/dist/safety/redaction.js +186 -0
- package/dist/safety/sql-classifier.d.ts +19 -0
- package/dist/safety/sql-classifier.js +43 -0
- package/dist/safety/sql-validator.d.ts +19 -0
- package/dist/safety/sql-validator.js +143 -0
- package/dist/schema/adapters/mysql.d.ts +16 -0
- package/dist/schema/adapters/mysql.js +287 -0
- package/dist/schema/introspector.d.ts +70 -0
- package/dist/schema/introspector.js +40 -0
- package/dist/taurus/flashback.d.ts +36 -0
- package/dist/taurus/flashback.js +149 -0
- package/dist/taurus/recycle-bin.d.ts +14 -0
- package/dist/taurus/recycle-bin.js +61 -0
- package/dist/utils/formatter.d.ts +70 -0
- package/dist/utils/formatter.js +60 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +247 -0
- package/dist/utils/id.d.ts +2 -0
- package/dist/utils/id.js +11 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.js +39 -0
- package/package.json +46 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { clampInteger, countBy, evidenceRowLimit, metricSummaryText, metricsSourceLimitation, parseProcesslistRows, pickMetric, queryMetricsSafely, roundMetric, withDatasourceSummary, } from "../helpers.js";
|
|
2
|
+
export async function diagnoseConnectionSpike(engine, input, ctx) {
|
|
3
|
+
const [processlist, metrics] = await Promise.all([
|
|
4
|
+
engine.showProcesslist({
|
|
5
|
+
user: input.user,
|
|
6
|
+
host: input.clientHost,
|
|
7
|
+
includeIdle: true,
|
|
8
|
+
includeSystem: false,
|
|
9
|
+
includeInfo: false,
|
|
10
|
+
maxRows: evidenceRowLimit(input.evidenceLevel),
|
|
11
|
+
}, ctx),
|
|
12
|
+
queryMetricsSafely(engine.metricsSource, [
|
|
13
|
+
"connection_count",
|
|
14
|
+
"active_connection_count",
|
|
15
|
+
"connection_usage",
|
|
16
|
+
"qps",
|
|
17
|
+
], input, ctx),
|
|
18
|
+
]);
|
|
19
|
+
const rows = parseProcesslistRows(processlist);
|
|
20
|
+
const connectionCountMetric = pickMetric(metrics, "connection_count");
|
|
21
|
+
const activeConnectionMetric = pickMetric(metrics, "active_connection_count");
|
|
22
|
+
const connectionUsageMetric = pickMetric(metrics, "connection_usage");
|
|
23
|
+
const qpsMetric = pickMetric(metrics, "qps");
|
|
24
|
+
const metricEvidence = metrics.map((metric) => ({
|
|
25
|
+
source: "ces_metrics",
|
|
26
|
+
title: `CES ${metric.alias}`,
|
|
27
|
+
summary: metricSummaryText(metric),
|
|
28
|
+
}));
|
|
29
|
+
if (rows.length === 0) {
|
|
30
|
+
const metricOnlySpike = (connectionUsageMetric?.max ?? 0) >= 80 ||
|
|
31
|
+
(connectionCountMetric?.max ?? 0) >= 100 ||
|
|
32
|
+
(activeConnectionMetric?.max ?? 0) >= 50;
|
|
33
|
+
if (metricOnlySpike) {
|
|
34
|
+
return {
|
|
35
|
+
tool: "diagnose_connection_spike",
|
|
36
|
+
status: "ok",
|
|
37
|
+
severity: (connectionUsageMetric?.max ?? 0) >= 90 ? "high" : "warning",
|
|
38
|
+
summary: withDatasourceSummary("Connection-spike diagnosis found CES connection pressure but no matching live sessions", ctx.datasource),
|
|
39
|
+
diagnosisWindow: {
|
|
40
|
+
from: input.timeRange?.from,
|
|
41
|
+
to: input.timeRange?.to,
|
|
42
|
+
relative: input.timeRange?.relative,
|
|
43
|
+
},
|
|
44
|
+
rootCauseCandidates: [
|
|
45
|
+
{
|
|
46
|
+
code: "connection_spike_ces_connection_pressure",
|
|
47
|
+
title: "Control-plane metrics show connection pressure",
|
|
48
|
+
confidence: (connectionUsageMetric?.max ?? 0) >= 90 ? "high" : "medium",
|
|
49
|
+
rationale: `CES connection metrics crossed the current thresholds${connectionUsageMetric?.max !== undefined ? `; max_connection_usage=${roundMetric(connectionUsageMetric.max)}` : ""}${connectionCountMetric?.max !== undefined ? `, max_connection_count=${roundMetric(connectionCountMetric.max)}` : ""}${activeConnectionMetric?.max !== undefined ? `, max_active_connections=${roundMetric(activeConnectionMetric.max)}` : ""}.`,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
keyFindings: [
|
|
53
|
+
"No matching processlist rows were available in the current snapshot.",
|
|
54
|
+
connectionUsageMetric
|
|
55
|
+
? `CES connection usage: ${metricSummaryText(connectionUsageMetric)}.`
|
|
56
|
+
: "CES connection usage metric was not returned.",
|
|
57
|
+
connectionCountMetric
|
|
58
|
+
? `CES connection count: ${metricSummaryText(connectionCountMetric)}.`
|
|
59
|
+
: "CES connection count metric was not returned.",
|
|
60
|
+
],
|
|
61
|
+
suspiciousEntities: input.user
|
|
62
|
+
? {
|
|
63
|
+
users: [
|
|
64
|
+
{
|
|
65
|
+
user: input.user,
|
|
66
|
+
clientHost: input.clientHost,
|
|
67
|
+
reason: "Provided as the diagnosis focus; CES showed connection pressure but the live snapshot did not contain matching sessions.",
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
}
|
|
71
|
+
: undefined,
|
|
72
|
+
evidence: [
|
|
73
|
+
{
|
|
74
|
+
source: "processlist",
|
|
75
|
+
title: "Current processlist snapshot",
|
|
76
|
+
summary: "No matching sessions were returned from information_schema.PROCESSLIST.",
|
|
77
|
+
},
|
|
78
|
+
...metricEvidence,
|
|
79
|
+
],
|
|
80
|
+
recommendedActions: [
|
|
81
|
+
"Re-run show_processlist during the spike window with include_idle=true and include_info=true.",
|
|
82
|
+
"Correlate CES connection pressure with application deploys, retry storms, and pool-size settings.",
|
|
83
|
+
],
|
|
84
|
+
limitations: [
|
|
85
|
+
"Live processlist evidence was not available for the observed CES spike window.",
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
tool: "diagnose_connection_spike",
|
|
91
|
+
status: "inconclusive",
|
|
92
|
+
severity: "info",
|
|
93
|
+
summary: withDatasourceSummary("No matching processlist sessions were observed for connection-spike diagnosis", ctx.datasource),
|
|
94
|
+
diagnosisWindow: {
|
|
95
|
+
from: input.timeRange?.from,
|
|
96
|
+
to: input.timeRange?.to,
|
|
97
|
+
relative: input.timeRange?.relative,
|
|
98
|
+
},
|
|
99
|
+
rootCauseCandidates: [
|
|
100
|
+
{
|
|
101
|
+
code: "connection_spike_no_matching_sessions",
|
|
102
|
+
title: "No matching live sessions observed",
|
|
103
|
+
confidence: "low",
|
|
104
|
+
rationale: "The current processlist snapshot did not contain sessions matching the requested user or client-host filters.",
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
keyFindings: [
|
|
108
|
+
input.user
|
|
109
|
+
? `No current processlist rows matched user ${input.user}.`
|
|
110
|
+
: "No current processlist rows were returned.",
|
|
111
|
+
input.clientHost
|
|
112
|
+
? `No current processlist rows matched host prefix ${input.clientHost}.`
|
|
113
|
+
: "No host filter was applied.",
|
|
114
|
+
],
|
|
115
|
+
suspiciousEntities: input.user
|
|
116
|
+
? {
|
|
117
|
+
users: [
|
|
118
|
+
{
|
|
119
|
+
user: input.user,
|
|
120
|
+
clientHost: input.clientHost,
|
|
121
|
+
reason: "Provided as the diagnosis focus, but no matching live sessions were observed in the current snapshot.",
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
}
|
|
125
|
+
: undefined,
|
|
126
|
+
evidence: [
|
|
127
|
+
{
|
|
128
|
+
source: "processlist",
|
|
129
|
+
title: "Current processlist snapshot",
|
|
130
|
+
summary: "No matching sessions were returned from information_schema.PROCESSLIST.",
|
|
131
|
+
},
|
|
132
|
+
...metricEvidence,
|
|
133
|
+
],
|
|
134
|
+
recommendedActions: [
|
|
135
|
+
"Re-run the diagnostic during the spike window to capture live sessions.",
|
|
136
|
+
"Use show_processlist with broader filters or include_idle=true to inspect connection buildup.",
|
|
137
|
+
],
|
|
138
|
+
limitations: [
|
|
139
|
+
"This diagnostic currently uses a point-in-time processlist snapshot only.",
|
|
140
|
+
...metricsSourceLimitation(engine.metricsSource),
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const sleepSessions = rows.filter((row) => row.command === "Sleep");
|
|
145
|
+
const activeSessions = rows.filter((row) => row.command !== "Sleep");
|
|
146
|
+
const longRunningSessions = rows.filter((row) => (row.timeSeconds ?? 0) >= 60);
|
|
147
|
+
const userCounts = countBy(rows, (row) => row.user);
|
|
148
|
+
const hostCounts = countBy(rows, (row) => row.host);
|
|
149
|
+
const topUser = userCounts[0];
|
|
150
|
+
const topHost = hostCounts[0];
|
|
151
|
+
const longestSessions = [...rows]
|
|
152
|
+
.sort((left, right) => (right.timeSeconds ?? 0) - (left.timeSeconds ?? 0))
|
|
153
|
+
.slice(0, 3);
|
|
154
|
+
const rootCauseCandidates = [];
|
|
155
|
+
if (sleepSessions.length >= Math.max(5, Math.ceil(rows.length * 0.6))) {
|
|
156
|
+
rootCauseCandidates.push({
|
|
157
|
+
code: "connection_spike_idle_session_accumulation",
|
|
158
|
+
title: "Idle session accumulation",
|
|
159
|
+
confidence: rows.length >= 10 ? "high" : "medium",
|
|
160
|
+
rationale: `${sleepSessions.length} of ${rows.length} matching sessions are idle (Sleep), which usually points to pooling saturation or clients holding connections open.`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (activeSessions.length >= Math.max(3, Math.ceil(rows.length * 0.4))) {
|
|
164
|
+
rootCauseCandidates.push({
|
|
165
|
+
code: "connection_spike_active_query_backlog",
|
|
166
|
+
title: "Active query backlog",
|
|
167
|
+
confidence: activeSessions.length >= 8 ? "high" : "medium",
|
|
168
|
+
rationale: `${activeSessions.length} matching sessions are active, suggesting requests may be piling up behind slow or blocked work.`,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (topUser && topUser.count >= Math.max(3, Math.ceil(rows.length * 0.5))) {
|
|
172
|
+
rootCauseCandidates.push({
|
|
173
|
+
code: "connection_spike_single_user_hotspot",
|
|
174
|
+
title: "Single-user hotspot",
|
|
175
|
+
confidence: topUser.count >= 8 ? "high" : "medium",
|
|
176
|
+
rationale: `User ${topUser.key} accounts for ${topUser.count} of ${rows.length} matching sessions, which suggests a concentrated source of connection growth.`,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (longRunningSessions.length > 0) {
|
|
180
|
+
rootCauseCandidates.push({
|
|
181
|
+
code: "connection_spike_long_running_sessions",
|
|
182
|
+
title: "Long-running sessions are holding connections",
|
|
183
|
+
confidence: longRunningSessions.length >= 3 ? "high" : "medium",
|
|
184
|
+
rationale: `${longRunningSessions.length} matching sessions have been active for at least 60 seconds, which can reduce pool turnover and amplify connection growth.`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if ((connectionUsageMetric?.max ?? 0) >= 80 ||
|
|
188
|
+
(connectionCountMetric?.max ?? 0) >= 100) {
|
|
189
|
+
rootCauseCandidates.push({
|
|
190
|
+
code: "connection_spike_ces_connection_pressure",
|
|
191
|
+
title: "CES metrics confirm connection pressure",
|
|
192
|
+
confidence: (connectionUsageMetric?.max ?? 0) >= 90 ? "high" : "medium",
|
|
193
|
+
rationale: `Cloud Eye connection metrics crossed the current pressure thresholds${connectionUsageMetric?.max !== undefined ? `; max_connection_usage=${roundMetric(connectionUsageMetric.max)}` : ""}${connectionCountMetric?.max !== undefined ? `, max_connection_count=${roundMetric(connectionCountMetric.max)}` : ""}.`,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (rootCauseCandidates.length === 0) {
|
|
197
|
+
rootCauseCandidates.push({
|
|
198
|
+
code: "connection_spike_snapshot_collected",
|
|
199
|
+
title: "Connection spike snapshot collected",
|
|
200
|
+
confidence: "low",
|
|
201
|
+
rationale: "A live processlist snapshot was collected, but no single dominant pattern crossed the current heuristic thresholds.",
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const maxCandidates = clampInteger(input.maxCandidates, 3, 1, 10);
|
|
205
|
+
const severity = rows.length >= 40 ||
|
|
206
|
+
activeSessions.length >= 15 ||
|
|
207
|
+
longRunningSessions.length >= 5
|
|
208
|
+
? "high"
|
|
209
|
+
: rows.length >= 15 ||
|
|
210
|
+
activeSessions.length >= 5 ||
|
|
211
|
+
longRunningSessions.length >= 2
|
|
212
|
+
? "warning"
|
|
213
|
+
: "info";
|
|
214
|
+
const suspiciousUsers = [];
|
|
215
|
+
if (input.user) {
|
|
216
|
+
suspiciousUsers.push({
|
|
217
|
+
user: input.user,
|
|
218
|
+
clientHost: input.clientHost,
|
|
219
|
+
reason: "Provided as the connection-spike focus.",
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
else if (topUser) {
|
|
223
|
+
suspiciousUsers.push({
|
|
224
|
+
user: topUser.key,
|
|
225
|
+
clientHost: topHost?.key,
|
|
226
|
+
reason: `Top user in current processlist snapshot with ${topUser.count} sessions.`,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const suspiciousSessions = longestSessions.map((row) => ({
|
|
230
|
+
sessionId: row.sessionId,
|
|
231
|
+
user: row.user,
|
|
232
|
+
state: row.state ?? row.command,
|
|
233
|
+
reason: row.timeSeconds !== undefined
|
|
234
|
+
? `Observed in the longest-running processlist sessions (${row.timeSeconds}s).`
|
|
235
|
+
: "Observed in the current processlist snapshot.",
|
|
236
|
+
}));
|
|
237
|
+
const keyFindings = [
|
|
238
|
+
`Collected ${rows.length} matching processlist sessions (${activeSessions.length} active, ${sleepSessions.length} idle).`,
|
|
239
|
+
topUser
|
|
240
|
+
? `Top user ${topUser.key} accounts for ${topUser.count} sessions.`
|
|
241
|
+
: "No dominant user was identified in the current snapshot.",
|
|
242
|
+
longRunningSessions.length > 0
|
|
243
|
+
? `${longRunningSessions.length} sessions have been active for at least 60 seconds.`
|
|
244
|
+
: "No long-running sessions (>=60s) were observed in the current snapshot.",
|
|
245
|
+
];
|
|
246
|
+
if (input.compareBaseline) {
|
|
247
|
+
keyFindings.push(engine.metricsSource
|
|
248
|
+
? "Baseline comparison was requested; CES connection metrics were collected for the requested time window."
|
|
249
|
+
: "Baseline comparison was requested, but only a live processlist snapshot is currently available.");
|
|
250
|
+
}
|
|
251
|
+
if (connectionUsageMetric) {
|
|
252
|
+
keyFindings.push(`CES connection usage: ${metricSummaryText(connectionUsageMetric)}.`);
|
|
253
|
+
}
|
|
254
|
+
if (connectionCountMetric) {
|
|
255
|
+
keyFindings.push(`CES connection count: ${metricSummaryText(connectionCountMetric)}.`);
|
|
256
|
+
}
|
|
257
|
+
if (activeConnectionMetric) {
|
|
258
|
+
keyFindings.push(`CES active connections: ${metricSummaryText(activeConnectionMetric)}.`);
|
|
259
|
+
}
|
|
260
|
+
if (qpsMetric) {
|
|
261
|
+
keyFindings.push(`CES QPS: ${metricSummaryText(qpsMetric)}.`);
|
|
262
|
+
}
|
|
263
|
+
const recommendedActions = [
|
|
264
|
+
"Use show_processlist with include_info=true to inspect the longest-running sessions in more detail.",
|
|
265
|
+
"Correlate the snapshot with application deploys, retry storms, and pool-size settings.",
|
|
266
|
+
];
|
|
267
|
+
if (sleepSessions.length >= Math.max(5, Math.ceil(rows.length * 0.6))) {
|
|
268
|
+
recommendedActions.push("Inspect client pooling, idle timeout, and connection reuse behavior for excessive Sleep sessions.");
|
|
269
|
+
}
|
|
270
|
+
if (activeSessions.length >= Math.max(3, Math.ceil(rows.length * 0.4))) {
|
|
271
|
+
recommendedActions.push("Review slow or blocked active sessions with explain_sql / explain_sql_enhanced and lock diagnostics.");
|
|
272
|
+
}
|
|
273
|
+
if ((connectionUsageMetric?.max ?? 0) >= 80) {
|
|
274
|
+
recommendedActions.push("Check configured max connections and application pool concurrency because CES connection usage crossed 80%.");
|
|
275
|
+
}
|
|
276
|
+
const evidence = [
|
|
277
|
+
{
|
|
278
|
+
source: "processlist",
|
|
279
|
+
title: "Current processlist snapshot",
|
|
280
|
+
summary: `Snapshot captured ${rows.length} matching sessions, including ${activeSessions.length} active and ${sleepSessions.length} idle sessions.`,
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
source: "processlist",
|
|
284
|
+
title: "Dominant user and host distribution",
|
|
285
|
+
summary: topUser || topHost
|
|
286
|
+
? `Top user: ${topUser?.key ?? "n/a"} (${topUser?.count ?? 0}); top host: ${topHost?.key ?? "n/a"} (${topHost?.count ?? 0}).`
|
|
287
|
+
: "No dominant user or host distribution was identified.",
|
|
288
|
+
},
|
|
289
|
+
...metricEvidence,
|
|
290
|
+
];
|
|
291
|
+
return {
|
|
292
|
+
tool: "diagnose_connection_spike",
|
|
293
|
+
status: "ok",
|
|
294
|
+
severity,
|
|
295
|
+
summary: withDatasourceSummary(`Connection-spike diagnosis collected a live processlist snapshot with ${rows.length} matching sessions`, ctx.datasource),
|
|
296
|
+
diagnosisWindow: {
|
|
297
|
+
from: input.timeRange?.from,
|
|
298
|
+
to: input.timeRange?.to,
|
|
299
|
+
relative: input.timeRange?.relative,
|
|
300
|
+
},
|
|
301
|
+
rootCauseCandidates: rootCauseCandidates.slice(0, maxCandidates),
|
|
302
|
+
keyFindings,
|
|
303
|
+
suspiciousEntities: suspiciousUsers.length > 0 || suspiciousSessions.length > 0
|
|
304
|
+
? {
|
|
305
|
+
users: suspiciousUsers.length > 0 ? suspiciousUsers : undefined,
|
|
306
|
+
sessions: suspiciousSessions.length > 0 ? suspiciousSessions : undefined,
|
|
307
|
+
}
|
|
308
|
+
: undefined,
|
|
309
|
+
evidence,
|
|
310
|
+
recommendedActions: [...new Set(recommendedActions)],
|
|
311
|
+
limitations: [
|
|
312
|
+
"This diagnostic currently relies on a point-in-time processlist snapshot only.",
|
|
313
|
+
...metricsSourceLimitation(engine.metricsSource),
|
|
314
|
+
],
|
|
315
|
+
};
|
|
316
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SessionContext } from "../../context/session-context.js";
|
|
2
|
+
import { type DbHotspotResult, type DiagnoseDbHotspotInput } from "../../diagnostics/types.js";
|
|
3
|
+
import type { TaurusDBEngine } from "../../engine.js";
|
|
4
|
+
export declare function diagnoseDbHotspot(engine: TaurusDBEngine, input: DiagnoseDbHotspotInput, ctx: SessionContext): Promise<DbHotspotResult>;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { buildConnectionSpikeNextToolInput, buildLockContentionNextToolInput, buildShowProcesslistNextToolInput, buildSlowQueryNextToolInput, clampInteger, confidenceWeight, dedupeNextToolInputs, hotspotTypePriority, withDatasourceSummary, } from "../helpers.js";
|
|
2
|
+
export async function diagnoseDbHotspot(engine, input, ctx) {
|
|
3
|
+
const maxCandidates = clampInteger(input.maxCandidates, 5, 1, 10);
|
|
4
|
+
const scope = input.scope ?? "all";
|
|
5
|
+
const hotspots = [];
|
|
6
|
+
const evidence = [];
|
|
7
|
+
const recommendedNextTools = new Set();
|
|
8
|
+
const nextToolInputs = [];
|
|
9
|
+
const limitations = new Set();
|
|
10
|
+
if (scope === "all" || scope === "sql") {
|
|
11
|
+
const topSlowSql = await engine.findTopSlowSql({
|
|
12
|
+
...input,
|
|
13
|
+
topN: Math.min(maxCandidates, 5),
|
|
14
|
+
sortBy: "total_latency",
|
|
15
|
+
}, ctx);
|
|
16
|
+
for (const limitation of topSlowSql.limitations ?? []) {
|
|
17
|
+
limitations.add(limitation);
|
|
18
|
+
}
|
|
19
|
+
if (topSlowSql.status === "ok") {
|
|
20
|
+
for (const sql of topSlowSql.topSqls.slice(0, Math.min(maxCandidates, 3))) {
|
|
21
|
+
hotspots.push({
|
|
22
|
+
type: "sql",
|
|
23
|
+
title: sql.digestText
|
|
24
|
+
? `Top SQL hotspot: ${sql.digestText}`
|
|
25
|
+
: "Top SQL hotspot",
|
|
26
|
+
confidence: (sql.totalLatencyMs ?? 0) >= 1000 ||
|
|
27
|
+
(sql.avgLatencyMs ?? 0) >= 100
|
|
28
|
+
? "high"
|
|
29
|
+
: (sql.totalLatencyMs ?? 0) > 0 || (sql.avgLatencyMs ?? 0) > 0
|
|
30
|
+
? "medium"
|
|
31
|
+
: "low",
|
|
32
|
+
sqlHash: sql.sqlHash,
|
|
33
|
+
digestText: sql.digestText,
|
|
34
|
+
sampleSql: sql.sampleSql,
|
|
35
|
+
rationale: `Ranked in digest summaries${sql.totalLatencyMs !== undefined ? `; total_latency_ms=${sql.totalLatencyMs}` : ""}${sql.avgLatencyMs !== undefined ? `, avg_latency_ms=${sql.avgLatencyMs}` : ""}${sql.execCount !== undefined ? `, exec_count=${sql.execCount}` : ""}${sql.avgRowsExamined !== undefined ? `, avg_rows_examined=${sql.avgRowsExamined}` : ""}.`,
|
|
36
|
+
evidenceSources: sql.evidenceSources,
|
|
37
|
+
recommendation: sql.recommendation ??
|
|
38
|
+
"Use diagnose_slow_query to inspect the SQL hotspot in more detail.",
|
|
39
|
+
});
|
|
40
|
+
const slowQueryInput = buildSlowQueryNextToolInput(sql, input, "Analyze this SQL hotspot from database-hotspot aggregation.");
|
|
41
|
+
if (slowQueryInput) {
|
|
42
|
+
nextToolInputs.push(slowQueryInput);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
evidence.push(...topSlowSql.evidence.slice(0, 1));
|
|
46
|
+
recommendedNextTools.add("find_top_slow_sql");
|
|
47
|
+
recommendedNextTools.add("diagnose_slow_query");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (scope === "all" || scope === "table" || scope === "session") {
|
|
51
|
+
const lockContention = await engine.diagnoseLockContention({
|
|
52
|
+
...input,
|
|
53
|
+
maxCandidates: Math.min(maxCandidates, 3),
|
|
54
|
+
}, ctx);
|
|
55
|
+
for (const limitation of lockContention.limitations ?? []) {
|
|
56
|
+
limitations.add(limitation);
|
|
57
|
+
}
|
|
58
|
+
if (lockContention.status === "ok") {
|
|
59
|
+
if (scope === "all" || scope === "session") {
|
|
60
|
+
for (const session of lockContention.suspiciousEntities?.sessions?.slice(0, 2) ?? []) {
|
|
61
|
+
hotspots.push({
|
|
62
|
+
type: "session",
|
|
63
|
+
title: session.sessionId
|
|
64
|
+
? `Blocking session hotspot ${session.sessionId}`
|
|
65
|
+
: "Blocking session hotspot",
|
|
66
|
+
confidence: lockContention.rootCauseCandidates.some((candidate) => candidate.code === "lock_contention_single_blocker_hotspot")
|
|
67
|
+
? "high"
|
|
68
|
+
: "medium",
|
|
69
|
+
sessionId: session.sessionId,
|
|
70
|
+
rationale: session.reason,
|
|
71
|
+
evidenceSources: ["lock_waits"],
|
|
72
|
+
recommendation: "Use diagnose_lock_contention and show_processlist to inspect blocker SQL and transaction age.",
|
|
73
|
+
});
|
|
74
|
+
nextToolInputs.push(buildLockContentionNextToolInput({ blockerSessionId: session.sessionId }, input, "Inspect this blocking-session hotspot with lock-wait context."), buildShowProcesslistNextToolInput({
|
|
75
|
+
command: "Query",
|
|
76
|
+
includeIdle: false,
|
|
77
|
+
includeInfo: true,
|
|
78
|
+
}, input, "Review live running sessions around this blocking-session hotspot."));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (scope === "all" || scope === "table") {
|
|
82
|
+
for (const table of lockContention.suspiciousEntities?.tables?.slice(0, 2) ?? []) {
|
|
83
|
+
hotspots.push({
|
|
84
|
+
type: "table",
|
|
85
|
+
title: `Locked table hotspot ${table.table}`,
|
|
86
|
+
confidence: lockContention.rootCauseCandidates.some((candidate) => candidate.code === "lock_contention_hot_table")
|
|
87
|
+
? "high"
|
|
88
|
+
: "medium",
|
|
89
|
+
table: table.table,
|
|
90
|
+
rationale: table.reason,
|
|
91
|
+
evidenceSources: ["lock_waits"],
|
|
92
|
+
recommendation: "Use diagnose_lock_contention to inspect wait chains and reduce lock hold time on this table.",
|
|
93
|
+
});
|
|
94
|
+
nextToolInputs.push(buildLockContentionNextToolInput({ table: table.table }, input, "Inspect this locked-table hotspot with lock-wait context."));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
evidence.push(...lockContention.evidence.slice(0, 2));
|
|
98
|
+
recommendedNextTools.add("diagnose_lock_contention");
|
|
99
|
+
recommendedNextTools.add("show_processlist");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (scope === "all" || scope === "session") {
|
|
103
|
+
const connectionSpike = await engine.diagnoseConnectionSpike({
|
|
104
|
+
...input,
|
|
105
|
+
compareBaseline: false,
|
|
106
|
+
maxCandidates: Math.min(maxCandidates, 3),
|
|
107
|
+
}, ctx);
|
|
108
|
+
for (const limitation of connectionSpike.limitations ?? []) {
|
|
109
|
+
limitations.add(limitation);
|
|
110
|
+
}
|
|
111
|
+
if (connectionSpike.status === "ok") {
|
|
112
|
+
const focusUser = connectionSpike.suspiciousEntities?.users?.[0];
|
|
113
|
+
const focusSession = connectionSpike.suspiciousEntities?.sessions?.[0];
|
|
114
|
+
hotspots.push({
|
|
115
|
+
type: "session",
|
|
116
|
+
title: focusUser?.user
|
|
117
|
+
? `Connection hotspot around user ${focusUser.user}`
|
|
118
|
+
: focusSession?.sessionId
|
|
119
|
+
? `Connection hotspot around session ${focusSession.sessionId}`
|
|
120
|
+
: "Connection hotspot",
|
|
121
|
+
confidence: connectionSpike.rootCauseCandidates.some((candidate) => candidate.code === "connection_spike_idle_session_accumulation")
|
|
122
|
+
? "high"
|
|
123
|
+
: "medium",
|
|
124
|
+
sessionId: focusSession?.sessionId,
|
|
125
|
+
rationale: focusUser?.reason ??
|
|
126
|
+
focusSession?.reason ??
|
|
127
|
+
"A live processlist snapshot suggests a session-level hotspot around connection growth.",
|
|
128
|
+
evidenceSources: ["processlist"],
|
|
129
|
+
recommendation: "Use diagnose_connection_spike and show_processlist to inspect idle buildup and long-running sessions.",
|
|
130
|
+
});
|
|
131
|
+
nextToolInputs.push(buildConnectionSpikeNextToolInput({
|
|
132
|
+
user: focusUser?.user,
|
|
133
|
+
clientHost: focusUser?.clientHost,
|
|
134
|
+
}, input, "Inspect this connection hotspot with connection-growth diagnostics."), buildShowProcesslistNextToolInput({
|
|
135
|
+
user: focusUser?.user,
|
|
136
|
+
host: focusUser?.clientHost,
|
|
137
|
+
includeIdle: true,
|
|
138
|
+
includeInfo: true,
|
|
139
|
+
}, input, "Review live sessions for this connection hotspot."));
|
|
140
|
+
evidence.push(...connectionSpike.evidence.slice(0, 2));
|
|
141
|
+
recommendedNextTools.add("diagnose_connection_spike");
|
|
142
|
+
recommendedNextTools.add("show_processlist");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const dedupedHotspots = hotspots
|
|
146
|
+
.filter((item, index, allItems) => {
|
|
147
|
+
const key = `${item.type}:${item.sqlHash ?? ""}:${item.digestText ?? ""}:${item.sessionId ?? ""}:${item.table ?? ""}:${item.title}`;
|
|
148
|
+
return (allItems.findIndex((candidate) => {
|
|
149
|
+
const candidateKey = `${candidate.type}:${candidate.sqlHash ?? ""}:${candidate.digestText ?? ""}:${candidate.sessionId ?? ""}:${candidate.table ?? ""}:${candidate.title}`;
|
|
150
|
+
return candidateKey === key;
|
|
151
|
+
}) === index);
|
|
152
|
+
})
|
|
153
|
+
.sort((left, right) => confidenceWeight(right.confidence) -
|
|
154
|
+
confidenceWeight(left.confidence) ||
|
|
155
|
+
hotspotTypePriority(right.type) - hotspotTypePriority(left.type) ||
|
|
156
|
+
left.title.localeCompare(right.title))
|
|
157
|
+
.slice(0, maxCandidates);
|
|
158
|
+
if (scope === "table") {
|
|
159
|
+
limitations.add("Table hotspots currently rely on lock-wait evidence only; no table-level IO, scan, or storage metric collector is connected yet.");
|
|
160
|
+
}
|
|
161
|
+
if (scope === "session") {
|
|
162
|
+
limitations.add("Session hotspots currently rely on processlist and lock-wait snapshots only; no CPU or per-session resource metric collector is connected yet.");
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
tool: "diagnose_db_hotspot",
|
|
166
|
+
status: dedupedHotspots.length > 0 ? "ok" : "inconclusive",
|
|
167
|
+
summary: withDatasourceSummary(dedupedHotspots.length > 0
|
|
168
|
+
? `Database hotspot diagnosis collected ${dedupedHotspots.length} hotspot candidates`
|
|
169
|
+
: "Database hotspot diagnosis did not isolate a hotspot from current SQL, lock, or processlist evidence", ctx.datasource),
|
|
170
|
+
diagnosisWindow: {
|
|
171
|
+
from: input.timeRange?.from,
|
|
172
|
+
to: input.timeRange?.to,
|
|
173
|
+
relative: input.timeRange?.relative,
|
|
174
|
+
},
|
|
175
|
+
scope,
|
|
176
|
+
hotspots: dedupedHotspots,
|
|
177
|
+
evidence: evidence.slice(0, 5),
|
|
178
|
+
recommendedNextTools: [...recommendedNextTools],
|
|
179
|
+
nextToolInputs: dedupeNextToolInputs(nextToolInputs).slice(0, maxCandidates),
|
|
180
|
+
limitations: [...limitations].slice(0, 5),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DiagnosticResult } from "../../../diagnostics/types.js";
|
|
2
|
+
import { type DeadlockSummary, type LockWaitRow, type MetadataLockRow } from "../../helpers.js";
|
|
3
|
+
import type { LockContentionSignals } from "./signals.js";
|
|
4
|
+
export declare function buildLockContentionSuspiciousEntities(input: {
|
|
5
|
+
rows: LockWaitRow[];
|
|
6
|
+
metadataLockRows: MetadataLockRow[];
|
|
7
|
+
latestDeadlock?: DeadlockSummary;
|
|
8
|
+
signals: LockContentionSignals;
|
|
9
|
+
}): DiagnosticResult["suspiciousEntities"];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { isIdleTransactionBlocker, } from "../../helpers.js";
|
|
2
|
+
export function buildLockContentionSuspiciousEntities(input) {
|
|
3
|
+
const { rows, metadataLockRows, latestDeadlock, signals } = input;
|
|
4
|
+
const { tableCounts, metadataTables, topBlocker } = signals;
|
|
5
|
+
const blockerDetails = rows
|
|
6
|
+
.filter((row, index, allRows) => row.blockingSessionId !== undefined &&
|
|
7
|
+
allRows.findIndex((candidate) => candidate.blockingSessionId === row.blockingSessionId) === index)
|
|
8
|
+
.slice(0, 3);
|
|
9
|
+
const suspiciousSessions = blockerDetails.map((row) => ({
|
|
10
|
+
sessionId: row.blockingSessionId,
|
|
11
|
+
user: row.blockingUser,
|
|
12
|
+
state: row.blockingState ?? row.blockingTrxState,
|
|
13
|
+
reason: isIdleTransactionBlocker(row)
|
|
14
|
+
? `Observed as a blocker with no active processlist state; the transaction is still ${row.blockingTrxState}${row.blockingTrxAgeSeconds !== undefined ? ` and has been open for ${row.blockingTrxAgeSeconds}s` : ""}, so it can keep row locks until COMMIT or ROLLBACK.`
|
|
15
|
+
: topBlocker && row.blockingSessionId === topBlocker.key
|
|
16
|
+
? `Top blocker in the current snapshot with ${topBlocker.count} waiting sessions.`
|
|
17
|
+
: `Observed as a blocker in the current lock-wait snapshot${row.blockingTrxAgeSeconds !== undefined ? `; transaction age ${row.blockingTrxAgeSeconds}s` : ""}.`,
|
|
18
|
+
}));
|
|
19
|
+
const suspiciousTables = tableCounts
|
|
20
|
+
.slice(0, 3)
|
|
21
|
+
.map((entry) => ({
|
|
22
|
+
table: entry.key,
|
|
23
|
+
reason: `Observed in ${entry.count} current lock waits.`,
|
|
24
|
+
}));
|
|
25
|
+
for (const row of metadataLockRows.slice(0, 2)) {
|
|
26
|
+
if (row.blockingSessionId &&
|
|
27
|
+
!suspiciousSessions.some((item) => item.sessionId === row.blockingSessionId)) {
|
|
28
|
+
suspiciousSessions.push({
|
|
29
|
+
sessionId: row.blockingSessionId,
|
|
30
|
+
user: row.blockingUser,
|
|
31
|
+
state: row.blockingState,
|
|
32
|
+
reason: `Observed as a metadata-lock blocker${row.objectSchema || row.objectName ? ` on ${(row.objectSchema ? `${row.objectSchema}.` : "") + (row.objectName ?? "unknown")}` : ""}.`,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const entry of metadataTables.slice(0, 2)) {
|
|
37
|
+
if (!suspiciousTables.some((item) => item.table === entry.key)) {
|
|
38
|
+
suspiciousTables.push({
|
|
39
|
+
table: entry.key,
|
|
40
|
+
reason: `Observed in ${entry.count} current metadata-lock waits.`,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const table of latestDeadlock?.waitingTables.slice(0, 2) ?? []) {
|
|
45
|
+
if (!suspiciousTables.some((item) => item.table === table)) {
|
|
46
|
+
suspiciousTables.push({
|
|
47
|
+
table,
|
|
48
|
+
reason: "Referenced in the latest deadlock section from SHOW ENGINE INNODB STATUS.",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return suspiciousSessions.length > 0 || suspiciousTables.length > 0
|
|
53
|
+
? {
|
|
54
|
+
sessions: suspiciousSessions.length > 0 ? suspiciousSessions : undefined,
|
|
55
|
+
tables: suspiciousTables.length > 0 ? suspiciousTables : undefined,
|
|
56
|
+
}
|
|
57
|
+
: undefined;
|
|
58
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SessionContext } from "../../../context/session-context.js";
|
|
2
|
+
import type { DiagnoseLockContentionInput, DiagnosticResult } from "../../../diagnostics/types.js";
|
|
3
|
+
export declare function buildNoMatchingLockContentionResult(input: DiagnoseLockContentionInput, ctx: SessionContext): DiagnosticResult;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { withDatasourceSummary } from "../../helpers.js";
|
|
2
|
+
export function buildNoMatchingLockContentionResult(input, ctx) {
|
|
3
|
+
return {
|
|
4
|
+
tool: "diagnose_lock_contention",
|
|
5
|
+
status: "inconclusive",
|
|
6
|
+
severity: "info",
|
|
7
|
+
summary: withDatasourceSummary("No matching InnoDB lock waits were observed for lock-contention diagnosis", ctx.datasource),
|
|
8
|
+
diagnosisWindow: {
|
|
9
|
+
from: input.timeRange?.from,
|
|
10
|
+
to: input.timeRange?.to,
|
|
11
|
+
relative: input.timeRange?.relative,
|
|
12
|
+
},
|
|
13
|
+
rootCauseCandidates: [
|
|
14
|
+
{
|
|
15
|
+
code: "lock_contention_no_matching_waits",
|
|
16
|
+
title: "No matching live lock waits observed",
|
|
17
|
+
confidence: "low",
|
|
18
|
+
rationale: "The current InnoDB lock-wait snapshot did not contain rows matching the requested table or blocker-session filters.",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
keyFindings: [
|
|
22
|
+
input.table
|
|
23
|
+
? `No current lock waits matched table ${ctx.database ? `${ctx.database}.` : ""}${input.table}.`
|
|
24
|
+
: "No current InnoDB lock waits were returned.",
|
|
25
|
+
input.blockerSessionId
|
|
26
|
+
? `No current lock waits matched blocker session ${input.blockerSessionId}.`
|
|
27
|
+
: "No blocker session filter was applied.",
|
|
28
|
+
],
|
|
29
|
+
suspiciousEntities: input.table || input.blockerSessionId
|
|
30
|
+
? {
|
|
31
|
+
sessions: input.blockerSessionId
|
|
32
|
+
? [
|
|
33
|
+
{
|
|
34
|
+
sessionId: input.blockerSessionId,
|
|
35
|
+
reason: "Provided as the diagnosis focus, but no matching live lock waits were observed in the current snapshot.",
|
|
36
|
+
},
|
|
37
|
+
]
|
|
38
|
+
: undefined,
|
|
39
|
+
tables: input.table
|
|
40
|
+
? [
|
|
41
|
+
{
|
|
42
|
+
table: input.table,
|
|
43
|
+
reason: "Provided as the diagnosis focus, but no matching live lock waits were observed in the current snapshot.",
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
: undefined,
|
|
47
|
+
}
|
|
48
|
+
: undefined,
|
|
49
|
+
evidence: [
|
|
50
|
+
{
|
|
51
|
+
source: "lock_waits",
|
|
52
|
+
title: "Current InnoDB lock-wait snapshot",
|
|
53
|
+
summary: "No matching rows were returned from performance_schema.data_lock_waits for the current snapshot.",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
recommendedActions: [
|
|
57
|
+
"Rerun the diagnosis while the lock wait is active so the blocker and waiter chain is still visible.",
|
|
58
|
+
"Inspect blocker sessions with show_processlist and widen filters if the contention spans multiple tables or users.",
|
|
59
|
+
],
|
|
60
|
+
limitations: [
|
|
61
|
+
"This diagnostic currently uses a point-in-time InnoDB lock-wait snapshot only.",
|
|
62
|
+
"No metadata-lock or deadlock-history evidence was available for this run.",
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DiagnoseLockContentionInput, DiagnosticEvidenceItem } from "../../../diagnostics/types.js";
|
|
2
|
+
import { type DeadlockSummary, type LockWaitRow, type MetadataLockRow } from "../../helpers.js";
|
|
3
|
+
import type { LockContentionSignals } from "./signals.js";
|
|
4
|
+
export declare function buildLockContentionKeyFindings(input: {
|
|
5
|
+
rows: LockWaitRow[];
|
|
6
|
+
metadataLockRows: MetadataLockRow[];
|
|
7
|
+
latestDeadlock?: DeadlockSummary;
|
|
8
|
+
diagnosisInput: DiagnoseLockContentionInput;
|
|
9
|
+
signals: LockContentionSignals;
|
|
10
|
+
}): string[];
|
|
11
|
+
export declare function buildLockContentionRecommendedActions(input: {
|
|
12
|
+
latestDeadlock?: DeadlockSummary;
|
|
13
|
+
metadataLockRows: MetadataLockRow[];
|
|
14
|
+
signals: LockContentionSignals;
|
|
15
|
+
}): string[];
|
|
16
|
+
export declare function buildLockContentionEvidence(input: {
|
|
17
|
+
rows: LockWaitRow[];
|
|
18
|
+
metadataLockRows: MetadataLockRow[];
|
|
19
|
+
latestDeadlock?: DeadlockSummary;
|
|
20
|
+
signals: LockContentionSignals;
|
|
21
|
+
}): DiagnosticEvidenceItem[];
|