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,253 @@
|
|
|
1
|
+
import { extractPlanTableNames, } from "../helpers.js";
|
|
2
|
+
export async function collectPlanTableStats(engine, ctx, plan) {
|
|
3
|
+
if (!ctx.database) {
|
|
4
|
+
return [];
|
|
5
|
+
}
|
|
6
|
+
const planTables = await Promise.all(extractPlanTableNames(plan)
|
|
7
|
+
.slice(0, 3)
|
|
8
|
+
.map(async (table) => {
|
|
9
|
+
try {
|
|
10
|
+
const schema = await engine.describeTable(ctx, ctx.database, table);
|
|
11
|
+
return {
|
|
12
|
+
table: `${ctx.database}.${table}`,
|
|
13
|
+
rowCountEstimate: schema.rowCountEstimate,
|
|
14
|
+
indexCount: schema.indexes.length,
|
|
15
|
+
primaryKey: schema.primaryKey,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
22
|
+
return planTables.filter((value) => value !== undefined);
|
|
23
|
+
}
|
|
24
|
+
export function buildSlowQueryRootCauseCandidates(input) {
|
|
25
|
+
const { riskSummary, explain, digestSample, runtimeLockTimeMs, waitEventRows } = input;
|
|
26
|
+
const candidates = [];
|
|
27
|
+
if (riskSummary.fullTableScanLikely) {
|
|
28
|
+
candidates.push({
|
|
29
|
+
code: "slow_query_full_table_scan",
|
|
30
|
+
title: "Full table scan is the dominant slowdown signal",
|
|
31
|
+
confidence: (riskSummary.estimatedRows ?? 0) >= 100_000 ? "high" : "medium",
|
|
32
|
+
rationale: `EXPLAIN indicates a likely full table scan${riskSummary.estimatedRows !== undefined ? ` across about ${riskSummary.estimatedRows} rows` : ""}.`,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (riskSummary.usesFilesort) {
|
|
36
|
+
candidates.push({
|
|
37
|
+
code: "slow_query_filesort",
|
|
38
|
+
title: "Filesort overhead is contributing to latency",
|
|
39
|
+
confidence: "medium",
|
|
40
|
+
rationale: "EXPLAIN shows filesort usage, which usually means extra sort work and potential disk spill under pressure.",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (riskSummary.usesTempStructure) {
|
|
44
|
+
candidates.push({
|
|
45
|
+
code: "slow_query_temp_structure",
|
|
46
|
+
title: "Temporary structures are increasing execution cost",
|
|
47
|
+
confidence: "medium",
|
|
48
|
+
rationale: "EXPLAIN shows temporary structures, which often indicates expensive grouping, sorting, or join reshaping.",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (!riskSummary.indexHitLikely) {
|
|
52
|
+
candidates.push({
|
|
53
|
+
code: "slow_query_poor_index_usage",
|
|
54
|
+
title: "Index usage looks weak or absent",
|
|
55
|
+
confidence: riskSummary.fullTableScanLikely ? "high" : "medium",
|
|
56
|
+
rationale: "The plan does not look index-friendly, which increases scanned rows and slows execution.",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (explain.taurusHints.parallelQuery.blockedReason ||
|
|
60
|
+
explain.taurusHints.ndpPushdown.blockedReason) {
|
|
61
|
+
candidates.push({
|
|
62
|
+
code: "slow_query_taurus_feature_gap",
|
|
63
|
+
title: "TaurusDB acceleration features may not be fully available",
|
|
64
|
+
confidence: "low",
|
|
65
|
+
rationale: [
|
|
66
|
+
explain.taurusHints.parallelQuery.blockedReason,
|
|
67
|
+
explain.taurusHints.ndpPushdown.blockedReason,
|
|
68
|
+
]
|
|
69
|
+
.filter((value) => typeof value === "string")
|
|
70
|
+
.join(" "),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if ((digestSample?.avgTmpDiskTables ?? 0) > 0) {
|
|
74
|
+
candidates.push({
|
|
75
|
+
code: "slow_query_tmp_disk_spill",
|
|
76
|
+
title: "Temporary tables are spilling to disk",
|
|
77
|
+
confidence: (digestSample?.avgTmpDiskTables ?? 0) >= 1 ? "medium" : "low",
|
|
78
|
+
rationale: `Digest summaries show about ${digestSample?.avgTmpDiskTables} temporary disk tables per execution, which suggests spill-heavy grouping or sorting.`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if ((runtimeLockTimeMs ?? 0) >= 10) {
|
|
82
|
+
candidates.push({
|
|
83
|
+
code: "slow_query_lock_wait_pressure",
|
|
84
|
+
title: "Lock wait time is a material part of the statement latency",
|
|
85
|
+
confidence: (runtimeLockTimeMs ?? 0) >= 100 ? "high" : "medium",
|
|
86
|
+
rationale: `${digestSample?.avgLockTimeMs !== undefined ? "Digest summaries" : "External slow-log samples"} show about ${runtimeLockTimeMs} ms of lock time per execution, which suggests blocking or lock-wait pressure on the statement path.`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const topWaitEvent = waitEventRows[0];
|
|
90
|
+
if (topWaitEvent?.eventName?.startsWith("wait/lock/")) {
|
|
91
|
+
candidates.push({
|
|
92
|
+
code: "slow_query_wait_event_lock_contention",
|
|
93
|
+
title: "Runtime wait events point to lock contention",
|
|
94
|
+
confidence: (topWaitEvent.totalWaitMs ?? 0) >= 100 ? "high" : "medium",
|
|
95
|
+
rationale: `Statement history shows ${topWaitEvent.eventName} as the dominant nested wait event${topWaitEvent.totalWaitMs !== undefined ? ` with about ${topWaitEvent.totalWaitMs} ms total wait time` : ""}.`,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else if (topWaitEvent?.eventName?.startsWith("wait/io/")) {
|
|
99
|
+
candidates.push({
|
|
100
|
+
code: "slow_query_wait_event_io_pressure",
|
|
101
|
+
title: "Runtime wait events point to I/O-bound execution",
|
|
102
|
+
confidence: (topWaitEvent.totalWaitMs ?? 0) >= 100 ? "medium" : "low",
|
|
103
|
+
rationale: `Statement history shows ${topWaitEvent.eventName} as the dominant nested wait event, which usually indicates file or handler I/O pressure.`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else if (topWaitEvent?.eventName?.startsWith("wait/synch/")) {
|
|
107
|
+
candidates.push({
|
|
108
|
+
code: "slow_query_wait_event_sync_contention",
|
|
109
|
+
title: "Runtime wait events point to synchronization contention",
|
|
110
|
+
confidence: (topWaitEvent.totalWaitMs ?? 0) >= 100 ? "medium" : "low",
|
|
111
|
+
rationale: `Statement history shows ${topWaitEvent.eventName} as the dominant nested wait event, which suggests mutex or rwlock contention in the execution path.`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if ((digestSample?.noIndexUsedCount ?? 0) > 0 ||
|
|
115
|
+
(digestSample?.selectScanCount ?? 0) > 0) {
|
|
116
|
+
candidates.push({
|
|
117
|
+
code: "slow_query_runtime_scan_pressure",
|
|
118
|
+
title: "Runtime summaries show scan-heavy executions",
|
|
119
|
+
confidence: (digestSample?.noIndexUsedCount ?? 0) > 0 ? "medium" : "low",
|
|
120
|
+
rationale: `Digest summaries recorded${(digestSample?.selectScanCount ?? 0) > 0 ? ` ${digestSample?.selectScanCount} scan-driven executions` : ""}${(digestSample?.selectScanCount ?? 0) > 0 && (digestSample?.noIndexUsedCount ?? 0) > 0 ? " and" : ""}${(digestSample?.noIndexUsedCount ?? 0) > 0 ? ` ${digestSample?.noIndexUsedCount} executions without index usage` : ""}.`,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (candidates.length === 0) {
|
|
124
|
+
candidates.push({
|
|
125
|
+
code: "slow_query_plan_collected",
|
|
126
|
+
title: "Plan evidence was collected but no single dominant cause stood out",
|
|
127
|
+
confidence: "low",
|
|
128
|
+
rationale: "Live EXPLAIN evidence was collected, but the current heuristics did not isolate a single dominant full-scan, sort, or temporary-structure bottleneck.",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return candidates;
|
|
132
|
+
}
|
|
133
|
+
export function buildSlowQueryKeyFindings(input) {
|
|
134
|
+
const { riskSummary, resolvedPlanTables, digestSample, externalSlowSqlSample, runtimeRowsExamined, runtimeLockTimeMs, waitEventRows, } = input;
|
|
135
|
+
const keyFindings = [
|
|
136
|
+
riskSummary.estimatedRows !== undefined
|
|
137
|
+
? `EXPLAIN estimated about ${riskSummary.estimatedRows} rows for the analyzed statement.`
|
|
138
|
+
: "EXPLAIN row estimate was not available.",
|
|
139
|
+
riskSummary.fullTableScanLikely
|
|
140
|
+
? "The current plan is likely scanning the full table."
|
|
141
|
+
: "The current plan does not strongly indicate a full table scan.",
|
|
142
|
+
riskSummary.usesFilesort || riskSummary.usesTempStructure
|
|
143
|
+
? `The plan uses${riskSummary.usesFilesort ? " filesort" : ""}${riskSummary.usesFilesort && riskSummary.usesTempStructure ? " and" : ""}${riskSummary.usesTempStructure ? " temporary structures" : ""}.`
|
|
144
|
+
: "The plan does not show filesort or temporary-structure overhead.",
|
|
145
|
+
];
|
|
146
|
+
if (resolvedPlanTables.length > 0) {
|
|
147
|
+
keyFindings.push(...resolvedPlanTables.map((tableStats) => `${tableStats.table} has${tableStats.rowCountEstimate !== undefined ? ` row estimate ${tableStats.rowCountEstimate}` : " unknown row estimate"} and ${tableStats.indexCount} indexes.`));
|
|
148
|
+
}
|
|
149
|
+
if (runtimeRowsExamined !== undefined) {
|
|
150
|
+
keyFindings.push(`${digestSample?.avgRowsExamined !== undefined ? "Digest summaries" : "External slow-log samples"} show about ${runtimeRowsExamined} rows examined per execution.`);
|
|
151
|
+
}
|
|
152
|
+
if (runtimeLockTimeMs !== undefined) {
|
|
153
|
+
keyFindings.push(`${digestSample?.avgLockTimeMs !== undefined ? "Digest summaries" : "External slow-log samples"} show about ${runtimeLockTimeMs} ms of lock time per execution.`);
|
|
154
|
+
}
|
|
155
|
+
if (digestSample &&
|
|
156
|
+
((digestSample.avgTmpTables ?? 0) > 0 ||
|
|
157
|
+
(digestSample.avgTmpDiskTables ?? 0) > 0)) {
|
|
158
|
+
keyFindings.push(`Digest summaries show temporary table usage${digestSample.avgTmpTables !== undefined ? ` (avg_tmp_tables=${digestSample.avgTmpTables}` : ""}${digestSample.avgTmpDiskTables !== undefined ? `${digestSample.avgTmpTables !== undefined ? ", " : " ("}avg_tmp_disk_tables=${digestSample.avgTmpDiskTables}` : ""}${digestSample.avgTmpTables !== undefined || digestSample.avgTmpDiskTables !== undefined ? ")" : ""}.`);
|
|
159
|
+
}
|
|
160
|
+
if (externalSlowSqlSample && !digestSample) {
|
|
161
|
+
keyFindings.push(`External TaurusDB slow-log samples resolved SQL text${externalSlowSqlSample.startTime ? ` from ${externalSlowSqlSample.startTime}` : ""}${externalSlowSqlSample.database ? ` for database ${externalSlowSqlSample.database}` : ""}.`);
|
|
162
|
+
}
|
|
163
|
+
if (waitEventRows.length > 0) {
|
|
164
|
+
keyFindings.push(`Statement history shows ${waitEventRows[0].eventName}${waitEventRows[0].totalWaitMs !== undefined ? ` as the top nested wait event (${waitEventRows[0].totalWaitMs} ms total)` : " as the top nested wait event"}.`);
|
|
165
|
+
}
|
|
166
|
+
return keyFindings;
|
|
167
|
+
}
|
|
168
|
+
export function buildSlowQueryRecommendedActions(input) {
|
|
169
|
+
const { standardPlan, explain, riskSummary, resolvedPlanTables, digestSample, runtimeLockTimeMs, topWaitEvent, } = input;
|
|
170
|
+
const recommendedActions = [
|
|
171
|
+
...standardPlan.recommendations,
|
|
172
|
+
...explain.optimizationSuggestions,
|
|
173
|
+
];
|
|
174
|
+
if (riskSummary.fullTableScanLikely) {
|
|
175
|
+
recommendedActions.push("Review predicates and indexes so the query can avoid scanning the full table.");
|
|
176
|
+
}
|
|
177
|
+
if (riskSummary.usesFilesort || riskSummary.usesTempStructure) {
|
|
178
|
+
recommendedActions.push("Review ORDER BY / GROUP BY / JOIN shape to reduce filesort and temporary-structure work.");
|
|
179
|
+
}
|
|
180
|
+
if (explain.taurusHints.parallelQuery.blockedReason ||
|
|
181
|
+
explain.taurusHints.ndpPushdown.blockedReason) {
|
|
182
|
+
recommendedActions.push("Verify whether TaurusDB acceleration features are available and enabled for this workload.");
|
|
183
|
+
}
|
|
184
|
+
if (!riskSummary.indexHitLikely &&
|
|
185
|
+
resolvedPlanTables.some((tableStats) => tableStats.indexCount > 0)) {
|
|
186
|
+
recommendedActions.push("The referenced tables already have indexes; compare the current predicates and sort columns against existing index definitions.");
|
|
187
|
+
}
|
|
188
|
+
if ((digestSample?.avgTmpDiskTables ?? 0) > 0) {
|
|
189
|
+
recommendedActions.push("Check whether ORDER BY / GROUP BY can be supported by indexes to reduce temporary disk tables.");
|
|
190
|
+
}
|
|
191
|
+
if ((digestSample?.noIndexUsedCount ?? 0) > 0) {
|
|
192
|
+
recommendedActions.push("Runtime digest summaries show no-index executions; compare the query shape with existing indexes and predicate selectivity.");
|
|
193
|
+
}
|
|
194
|
+
if ((runtimeLockTimeMs ?? 0) >= 10) {
|
|
195
|
+
recommendedActions.push("Investigate blocker sessions or transaction scope because digest summaries show non-trivial lock time.");
|
|
196
|
+
}
|
|
197
|
+
if (topWaitEvent?.eventName?.startsWith("wait/lock/")) {
|
|
198
|
+
recommendedActions.push("Correlate the dominant lock wait event with blocker sessions, transaction scope, and hot rows before changing the SQL shape.");
|
|
199
|
+
}
|
|
200
|
+
if (topWaitEvent?.eventName?.startsWith("wait/io/")) {
|
|
201
|
+
recommendedActions.push("Check whether the dominant I/O wait aligns with table scans, filesort spill, or storage pressure on the accessed objects.");
|
|
202
|
+
}
|
|
203
|
+
if (topWaitEvent?.eventName?.startsWith("wait/synch/")) {
|
|
204
|
+
recommendedActions.push("Inspect concurrency hotspots because synchronization waits suggest contention beyond the SQL text itself.");
|
|
205
|
+
}
|
|
206
|
+
return [...new Set(recommendedActions)];
|
|
207
|
+
}
|
|
208
|
+
export function buildSlowQueryEvidence(input) {
|
|
209
|
+
const { standardPlan, riskSummary, resolvedPlanTables, digestSample, externalSlowSqlSample, waitEventRows, } = input;
|
|
210
|
+
return [
|
|
211
|
+
...(externalSlowSqlSample
|
|
212
|
+
? [
|
|
213
|
+
{
|
|
214
|
+
source: externalSlowSqlSample.source,
|
|
215
|
+
title: "External slow-log sample",
|
|
216
|
+
summary: `A SQL sample was resolved from TaurusDB slow-log APIs${externalSlowSqlSample.avgLatencyMs !== undefined ? `; avg_latency_ms=${externalSlowSqlSample.avgLatencyMs}` : ""}${externalSlowSqlSample.avgLockTimeMs !== undefined ? `, avg_lock_time_ms=${externalSlowSqlSample.avgLockTimeMs}` : ""}${externalSlowSqlSample.avgRowsExamined !== undefined ? `, avg_rows_examined=${externalSlowSqlSample.avgRowsExamined}` : ""}${externalSlowSqlSample.execCount !== undefined ? `, exec_count=${externalSlowSqlSample.execCount}` : ""}${externalSlowSqlSample.database ? `, database=${externalSlowSqlSample.database}` : ""}.`,
|
|
217
|
+
rawRef: externalSlowSqlSample.rawRef,
|
|
218
|
+
},
|
|
219
|
+
]
|
|
220
|
+
: []),
|
|
221
|
+
...(digestSample
|
|
222
|
+
? [
|
|
223
|
+
{
|
|
224
|
+
source: "statement_digest",
|
|
225
|
+
title: "Digest summary sample",
|
|
226
|
+
summary: `A query sample was resolved from performance_schema.events_statements_summary_by_digest${digestSample.execCount !== undefined ? `; exec_count=${digestSample.execCount}` : ""}${digestSample.avgLatencyMs !== undefined ? `, avg_latency_ms=${digestSample.avgLatencyMs}` : ""}${digestSample.maxLatencyMs !== undefined ? `, max_latency_ms=${digestSample.maxLatencyMs}` : ""}${digestSample.avgLockTimeMs !== undefined ? `, avg_lock_time_ms=${digestSample.avgLockTimeMs}` : ""}${digestSample.avgRowsExamined !== undefined ? `, avg_rows_examined=${digestSample.avgRowsExamined}` : ""}${digestSample.avgTmpDiskTables !== undefined ? `, avg_tmp_disk_tables=${digestSample.avgTmpDiskTables}` : ""}${digestSample.noIndexUsedCount !== undefined ? `, no_index_used_count=${digestSample.noIndexUsedCount}` : ""}.`,
|
|
227
|
+
},
|
|
228
|
+
]
|
|
229
|
+
: []),
|
|
230
|
+
...waitEventRows.map((row, index) => ({
|
|
231
|
+
source: "statement_wait_history",
|
|
232
|
+
title: index === 0
|
|
233
|
+
? "Dominant nested wait event"
|
|
234
|
+
: `Nested wait event ${index + 1}`,
|
|
235
|
+
summary: `${row.eventName ?? "unknown_event"}${row.totalWaitMs !== undefined ? ` total_wait_ms=${row.totalWaitMs}` : ""}${row.avgWaitMs !== undefined ? `, avg_wait_ms=${row.avgWaitMs}` : ""}${row.sampleCount !== undefined ? `, sample_count=${row.sampleCount}` : ""}${row.statementCount !== undefined ? `, statement_count=${row.statementCount}` : ""}.`,
|
|
236
|
+
})),
|
|
237
|
+
{
|
|
238
|
+
source: "explain",
|
|
239
|
+
title: "Live EXPLAIN plan",
|
|
240
|
+
summary: `A live EXPLAIN plan was collected for the provided SQL${standardPlan.queryId ? ` (query id ${standardPlan.queryId})` : ""}.`,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
source: "explain",
|
|
244
|
+
title: "Plan risk summary",
|
|
245
|
+
summary: `full_scan=${riskSummary.fullTableScanLikely}, index_hit=${riskSummary.indexHitLikely}, filesort=${riskSummary.usesFilesort}, temp_structure=${riskSummary.usesTempStructure}${riskSummary.estimatedRows !== undefined ? `, estimated_rows=${riskSummary.estimatedRows}` : ""}.`,
|
|
246
|
+
},
|
|
247
|
+
...resolvedPlanTables.map((tableStats) => ({
|
|
248
|
+
source: "table_schema",
|
|
249
|
+
title: `Referenced table ${tableStats.table}`,
|
|
250
|
+
summary: `${tableStats.table} has${tableStats.rowCountEstimate !== undefined ? ` row_count_estimate=${tableStats.rowCountEstimate}` : " unknown row count"}${tableStats.primaryKey && tableStats.primaryKey.length > 0 ? `, primary_key=${tableStats.primaryKey.join(",")}` : ""}, index_count=${tableStats.indexCount}.`,
|
|
251
|
+
})),
|
|
252
|
+
];
|
|
253
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SessionContext } from "../../context/session-context.js";
|
|
2
|
+
import { type DiagnoseSlowQueryInput, type DiagnosticResult } from "../../diagnostics/types.js";
|
|
3
|
+
import type { TaurusDBEngine } from "../../engine.js";
|
|
4
|
+
export declare function diagnoseSlowQuery(engine: TaurusDBEngine, input: DiagnoseSlowQueryInput, ctx: SessionContext): Promise<DiagnosticResult>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { buildResolveSlowSqlInput } from "../../diagnostics/slow-sql-source.js";
|
|
2
|
+
import { normalizeSql, sqlHash } from "../../utils/hash.js";
|
|
3
|
+
import { clampInteger, severityFromSlowQueryEvidence, sortRootCauseCandidates, withDatasourceSummary, } from "../helpers.js";
|
|
4
|
+
import { buildSlowQueryEvidence, buildSlowQueryKeyFindings, buildSlowQueryRecommendedActions, buildSlowQueryRootCauseCandidates, collectPlanTableStats, } from "./slow-query-helpers.js";
|
|
5
|
+
export async function diagnoseSlowQuery(engine, input, ctx) {
|
|
6
|
+
const sqlMatchedDigestSample = input.sql
|
|
7
|
+
? await engine.findStatementDigestSampleForSql(input.sql, ctx)
|
|
8
|
+
: undefined;
|
|
9
|
+
const externalSlowSqlSample = !input.sql && engine.slowSqlSource
|
|
10
|
+
? await engine.slowSqlSource.resolve(buildResolveSlowSqlInput(input), ctx)
|
|
11
|
+
: undefined;
|
|
12
|
+
const digestSample = sqlMatchedDigestSample ??
|
|
13
|
+
(!input.sql && input.digestText
|
|
14
|
+
? await engine.findStatementDigestSample(input.digestText, ctx)
|
|
15
|
+
: undefined);
|
|
16
|
+
const waitEventRows = input.digestText || digestSample?.digestText
|
|
17
|
+
? await engine.findStatementWaitEvents(input.digestText ?? digestSample?.digestText ?? "", ctx)
|
|
18
|
+
: [];
|
|
19
|
+
const effectiveSql = input.sql ?? externalSlowSqlSample?.sql ?? digestSample?.querySampleText;
|
|
20
|
+
const derivedSqlHash = effectiveSql
|
|
21
|
+
? sqlHash(normalizeSql(effectiveSql))
|
|
22
|
+
: undefined;
|
|
23
|
+
const runtimeLockTimeMs = digestSample?.avgLockTimeMs ?? externalSlowSqlSample?.avgLockTimeMs;
|
|
24
|
+
const runtimeRowsExamined = digestSample?.avgRowsExamined ?? externalSlowSqlSample?.avgRowsExamined;
|
|
25
|
+
const suspiciousSql = effectiveSql || input.sqlHash || input.digestText
|
|
26
|
+
? [
|
|
27
|
+
{
|
|
28
|
+
sqlHash: input.sqlHash ?? derivedSqlHash,
|
|
29
|
+
digestText: input.digestText ?? digestSample?.digestText,
|
|
30
|
+
reason: input.sql
|
|
31
|
+
? digestSample?.digestText
|
|
32
|
+
? "SQL text was provided, matched to performance_schema digest summaries, and analyzed with EXPLAIN plus runtime evidence."
|
|
33
|
+
: "SQL text was provided and analyzed with EXPLAIN evidence."
|
|
34
|
+
: externalSlowSqlSample?.sql
|
|
35
|
+
? "A SQL sample was resolved from the TaurusDB slow-log source and analyzed with EXPLAIN evidence."
|
|
36
|
+
: digestSample?.querySampleText
|
|
37
|
+
? "A statement sample was resolved from performance_schema digest summaries and analyzed with EXPLAIN evidence."
|
|
38
|
+
: "Only an SQL identifier was provided, so live EXPLAIN evidence could not be collected yet.",
|
|
39
|
+
},
|
|
40
|
+
]
|
|
41
|
+
: undefined;
|
|
42
|
+
if (!effectiveSql) {
|
|
43
|
+
return {
|
|
44
|
+
tool: "diagnose_slow_query",
|
|
45
|
+
status: "inconclusive",
|
|
46
|
+
severity: "info",
|
|
47
|
+
summary: withDatasourceSummary("Slow-query diagnosis needs SQL text for EXPLAIN-backed analysis", ctx.datasource),
|
|
48
|
+
diagnosisWindow: {
|
|
49
|
+
from: input.timeRange?.from,
|
|
50
|
+
to: input.timeRange?.to,
|
|
51
|
+
relative: input.timeRange?.relative,
|
|
52
|
+
},
|
|
53
|
+
rootCauseCandidates: [
|
|
54
|
+
{
|
|
55
|
+
code: "slow_query_missing_sql_text",
|
|
56
|
+
title: "SQL text is required for explain-backed diagnosis",
|
|
57
|
+
confidence: "low",
|
|
58
|
+
rationale: "The current implementation can analyze a slow query only when the SQL text is available for live EXPLAIN correlation.",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
keyFindings: [
|
|
62
|
+
input.sqlHash
|
|
63
|
+
? `SQL hash ${input.sqlHash} was provided without the original SQL text.`
|
|
64
|
+
: "No SQL text was provided.",
|
|
65
|
+
externalSlowSqlSample
|
|
66
|
+
? "An external TaurusDB slow-log source was queried, but no usable SQL sample was returned."
|
|
67
|
+
: undefined,
|
|
68
|
+
input.digestText
|
|
69
|
+
? "Digest text was provided, but no matching statement sample was available in the configured sources."
|
|
70
|
+
: "No digest text was provided.",
|
|
71
|
+
].filter((value) => typeof value === "string"),
|
|
72
|
+
suspiciousEntities: suspiciousSql ? { sqls: suspiciousSql } : undefined,
|
|
73
|
+
evidence: [
|
|
74
|
+
{
|
|
75
|
+
source: "sql_identifier",
|
|
76
|
+
title: "SQL identifier only",
|
|
77
|
+
summary: "The diagnosis request contained an SQL identifier, but no live EXPLAIN was run because the SQL text could not be resolved.",
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
recommendedActions: [
|
|
81
|
+
"Provide the full SQL text so the tool can run explain-based diagnosis.",
|
|
82
|
+
"If TaurusDB slow-log API is configured, verify its project, instance, node, and token settings.",
|
|
83
|
+
"If you are using digest_text, verify that performance_schema statement digest summaries are enabled and retaining QUERY_SAMPLE_TEXT.",
|
|
84
|
+
],
|
|
85
|
+
limitations: [
|
|
86
|
+
engine.slowSqlSource
|
|
87
|
+
? "Identifier-only diagnosis currently depends on TaurusDB slow-log samples and performance_schema digest samples."
|
|
88
|
+
: "No external slow-SQL source is connected yet.",
|
|
89
|
+
engine.slowSqlSource
|
|
90
|
+
? "The TaurusDB slow-log source currently resolves SQL samples but does not yet provide full Top SQL or all-query history coverage."
|
|
91
|
+
: "Identifier-only diagnosis is limited to performance_schema digest samples in the current version.",
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const explain = await engine.explainEnhanced(effectiveSql, ctx);
|
|
96
|
+
const standardPlan = explain.standardPlan;
|
|
97
|
+
const riskSummary = standardPlan.riskSummary;
|
|
98
|
+
const resolvedPlanTables = await collectPlanTableStats(engine, ctx, standardPlan.plan);
|
|
99
|
+
const rootCauseCandidates = buildSlowQueryRootCauseCandidates({
|
|
100
|
+
riskSummary,
|
|
101
|
+
explain,
|
|
102
|
+
digestSample,
|
|
103
|
+
runtimeLockTimeMs,
|
|
104
|
+
waitEventRows,
|
|
105
|
+
});
|
|
106
|
+
const maxCandidates = clampInteger(input.maxCandidates, 3, 1, 10);
|
|
107
|
+
const sortedRootCauseCandidates = sortRootCauseCandidates(rootCauseCandidates);
|
|
108
|
+
const severity = severityFromSlowQueryEvidence(riskSummary, sortedRootCauseCandidates);
|
|
109
|
+
const keyFindings = buildSlowQueryKeyFindings({
|
|
110
|
+
riskSummary,
|
|
111
|
+
resolvedPlanTables,
|
|
112
|
+
digestSample,
|
|
113
|
+
externalSlowSqlSample,
|
|
114
|
+
runtimeRowsExamined,
|
|
115
|
+
runtimeLockTimeMs,
|
|
116
|
+
waitEventRows,
|
|
117
|
+
});
|
|
118
|
+
const recommendedActions = buildSlowQueryRecommendedActions({
|
|
119
|
+
standardPlan,
|
|
120
|
+
explain,
|
|
121
|
+
riskSummary,
|
|
122
|
+
resolvedPlanTables,
|
|
123
|
+
digestSample,
|
|
124
|
+
runtimeLockTimeMs,
|
|
125
|
+
topWaitEvent: waitEventRows[0],
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
tool: "diagnose_slow_query",
|
|
129
|
+
status: "ok",
|
|
130
|
+
severity,
|
|
131
|
+
summary: withDatasourceSummary("Slow-query diagnosis collected live EXPLAIN evidence for the provided SQL", ctx.datasource),
|
|
132
|
+
diagnosisWindow: {
|
|
133
|
+
from: input.timeRange?.from,
|
|
134
|
+
to: input.timeRange?.to,
|
|
135
|
+
relative: input.timeRange?.relative,
|
|
136
|
+
},
|
|
137
|
+
rootCauseCandidates: sortedRootCauseCandidates.slice(0, maxCandidates),
|
|
138
|
+
keyFindings,
|
|
139
|
+
suspiciousEntities: suspiciousSql ? { sqls: suspiciousSql } : undefined,
|
|
140
|
+
evidence: buildSlowQueryEvidence({
|
|
141
|
+
standardPlan,
|
|
142
|
+
riskSummary,
|
|
143
|
+
resolvedPlanTables,
|
|
144
|
+
digestSample,
|
|
145
|
+
externalSlowSqlSample,
|
|
146
|
+
waitEventRows,
|
|
147
|
+
}),
|
|
148
|
+
recommendedActions,
|
|
149
|
+
limitations: [
|
|
150
|
+
engine.slowSqlSource
|
|
151
|
+
? "Identifier-only diagnosis can use TaurusDB slow-log samples, but still depends on available retention and query_sample coverage."
|
|
152
|
+
: "No external slow-SQL source is connected yet, so identifier-only diagnosis currently depends on performance_schema digest samples.",
|
|
153
|
+
"Runtime wait-event correlation currently depends on performance_schema statement and wait history being enabled and retaining matching samples.",
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SessionContext } from "../../context/session-context.js";
|
|
2
|
+
import type { MetricSummary, MetricsSource } from "../../diagnostics/metrics-source.js";
|
|
3
|
+
import type { DiagnoseStoragePressureInput, DiagnosticResult } from "../../diagnostics/types.js";
|
|
4
|
+
import { type StatementDigestRow, type TableStorageRow } from "../helpers.js";
|
|
5
|
+
export declare function buildStoragePressureDiagnosis(input: {
|
|
6
|
+
diagnosisInput: DiagnoseStoragePressureInput;
|
|
7
|
+
ctx: SessionContext;
|
|
8
|
+
digestRows: StatementDigestRow[];
|
|
9
|
+
tableRows: TableStorageRow[];
|
|
10
|
+
metrics: MetricSummary[];
|
|
11
|
+
metricsSource?: MetricsSource;
|
|
12
|
+
}): DiagnosticResult;
|