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.
Files changed (170) hide show
  1. package/README.md +21 -0
  2. package/dist/auth/secret-resolver.d.ts +16 -0
  3. package/dist/auth/secret-resolver.js +64 -0
  4. package/dist/auth/sql-profile-loader/env-source.d.ts +3 -0
  5. package/dist/auth/sql-profile-loader/env-source.js +94 -0
  6. package/dist/auth/sql-profile-loader/file-source.d.ts +6 -0
  7. package/dist/auth/sql-profile-loader/file-source.js +40 -0
  8. package/dist/auth/sql-profile-loader/loader.d.ts +16 -0
  9. package/dist/auth/sql-profile-loader/loader.js +81 -0
  10. package/dist/auth/sql-profile-loader/parsing.d.ts +14 -0
  11. package/dist/auth/sql-profile-loader/parsing.js +216 -0
  12. package/dist/auth/sql-profile-loader/runtime-override.d.ts +14 -0
  13. package/dist/auth/sql-profile-loader/runtime-override.js +52 -0
  14. package/dist/auth/sql-profile-loader/types.d.ts +64 -0
  15. package/dist/auth/sql-profile-loader/types.js +1 -0
  16. package/dist/auth/sql-profile-loader.d.ts +4 -0
  17. package/dist/auth/sql-profile-loader.js +3 -0
  18. package/dist/capability/feature-matrix.d.ts +5 -0
  19. package/dist/capability/feature-matrix.js +237 -0
  20. package/dist/capability/probe.d.ts +19 -0
  21. package/dist/capability/probe.js +139 -0
  22. package/dist/capability/types.d.ts +49 -0
  23. package/dist/capability/types.js +16 -0
  24. package/dist/capability/version.d.ts +3 -0
  25. package/dist/capability/version.js +47 -0
  26. package/dist/cloud/auth.d.ts +26 -0
  27. package/dist/cloud/auth.js +198 -0
  28. package/dist/cloud/instances.d.ts +46 -0
  29. package/dist/cloud/instances.js +224 -0
  30. package/dist/config/env.d.ts +1 -0
  31. package/dist/config/env.js +194 -0
  32. package/dist/config/index.d.ts +6 -0
  33. package/dist/config/index.js +21 -0
  34. package/dist/config/redaction.d.ts +2 -0
  35. package/dist/config/redaction.js +19 -0
  36. package/dist/config/schema.d.ts +417 -0
  37. package/dist/config/schema.js +100 -0
  38. package/dist/context/datasource-resolver.d.ts +19 -0
  39. package/dist/context/datasource-resolver.js +71 -0
  40. package/dist/context/session-context.d.ts +26 -0
  41. package/dist/context/session-context.js +1 -0
  42. package/dist/diagnostics/metrics-source.d.ts +65 -0
  43. package/dist/diagnostics/metrics-source.js +280 -0
  44. package/dist/diagnostics/slow-sql-source/das-source.d.ts +43 -0
  45. package/dist/diagnostics/slow-sql-source/das-source.js +170 -0
  46. package/dist/diagnostics/slow-sql-source/factory.d.ts +5 -0
  47. package/dist/diagnostics/slow-sql-source/factory.js +87 -0
  48. package/dist/diagnostics/slow-sql-source/parsers.d.ts +7 -0
  49. package/dist/diagnostics/slow-sql-source/parsers.js +125 -0
  50. package/dist/diagnostics/slow-sql-source/taurus-api-source.d.ts +42 -0
  51. package/dist/diagnostics/slow-sql-source/taurus-api-source.js +149 -0
  52. package/dist/diagnostics/slow-sql-source/types.d.ts +40 -0
  53. package/dist/diagnostics/slow-sql-source/types.js +1 -0
  54. package/dist/diagnostics/slow-sql-source/utils.d.ts +20 -0
  55. package/dist/diagnostics/slow-sql-source/utils.js +170 -0
  56. package/dist/diagnostics/slow-sql-source.d.ts +4 -0
  57. package/dist/diagnostics/slow-sql-source.js +3 -0
  58. package/dist/diagnostics/types.d.ts +189 -0
  59. package/dist/diagnostics/types.js +39 -0
  60. package/dist/engine/data-access/locks.d.ts +8 -0
  61. package/dist/engine/data-access/locks.js +146 -0
  62. package/dist/engine/data-access/processlist.d.ts +4 -0
  63. package/dist/engine/data-access/processlist.js +56 -0
  64. package/dist/engine/data-access/statements.d.ts +10 -0
  65. package/dist/engine/data-access/statements.js +203 -0
  66. package/dist/engine/data-access/storage.d.ts +6 -0
  67. package/dist/engine/data-access/storage.js +96 -0
  68. package/dist/engine/data-access.d.ts +4 -0
  69. package/dist/engine/data-access.js +4 -0
  70. package/dist/engine/diagnostics.d.ts +7 -0
  71. package/dist/engine/diagnostics.js +7 -0
  72. package/dist/engine/helper-modules/diagnostics.d.ts +57 -0
  73. package/dist/engine/helper-modules/diagnostics.js +322 -0
  74. package/dist/engine/helper-modules/parsers.d.ts +13 -0
  75. package/dist/engine/helper-modules/parsers.js +283 -0
  76. package/dist/engine/helper-modules/sql.d.ts +12 -0
  77. package/dist/engine/helper-modules/sql.js +119 -0
  78. package/dist/engine/helper-modules/types.d.ts +103 -0
  79. package/dist/engine/helper-modules/types.js +1 -0
  80. package/dist/engine/helpers.d.ts +4 -0
  81. package/dist/engine/helpers.js +4 -0
  82. package/dist/engine/runtime.d.ts +20 -0
  83. package/dist/engine/runtime.js +385 -0
  84. package/dist/engine/types.d.ts +125 -0
  85. package/dist/engine/types.js +1 -0
  86. package/dist/engine/workflows/connection-spike.d.ts +4 -0
  87. package/dist/engine/workflows/connection-spike.js +316 -0
  88. package/dist/engine/workflows/db-hotspot.d.ts +4 -0
  89. package/dist/engine/workflows/db-hotspot.js +182 -0
  90. package/dist/engine/workflows/lock-contention-helpers/entities.d.ts +9 -0
  91. package/dist/engine/workflows/lock-contention-helpers/entities.js +58 -0
  92. package/dist/engine/workflows/lock-contention-helpers/no-match.d.ts +3 -0
  93. package/dist/engine/workflows/lock-contention-helpers/no-match.js +65 -0
  94. package/dist/engine/workflows/lock-contention-helpers/report.d.ts +21 -0
  95. package/dist/engine/workflows/lock-contention-helpers/report.js +104 -0
  96. package/dist/engine/workflows/lock-contention-helpers/root-cause.d.ts +4 -0
  97. package/dist/engine/workflows/lock-contention-helpers/root-cause.js +79 -0
  98. package/dist/engine/workflows/lock-contention-helpers/signals.d.ts +22 -0
  99. package/dist/engine/workflows/lock-contention-helpers/signals.js +34 -0
  100. package/dist/engine/workflows/lock-contention-helpers.d.ts +5 -0
  101. package/dist/engine/workflows/lock-contention-helpers.js +5 -0
  102. package/dist/engine/workflows/lock-contention.d.ts +4 -0
  103. package/dist/engine/workflows/lock-contention.js +67 -0
  104. package/dist/engine/workflows/service-latency.d.ts +4 -0
  105. package/dist/engine/workflows/service-latency.js +262 -0
  106. package/dist/engine/workflows/slow-query-helpers.d.ts +41 -0
  107. package/dist/engine/workflows/slow-query-helpers.js +253 -0
  108. package/dist/engine/workflows/slow-query.d.ts +4 -0
  109. package/dist/engine/workflows/slow-query.js +156 -0
  110. package/dist/engine/workflows/storage-pressure-helpers.d.ts +12 -0
  111. package/dist/engine/workflows/storage-pressure-helpers.js +281 -0
  112. package/dist/engine/workflows/storage-pressure.d.ts +4 -0
  113. package/dist/engine/workflows/storage-pressure.js +27 -0
  114. package/dist/engine/workflows/top-slow-sql.d.ts +4 -0
  115. package/dist/engine/workflows/top-slow-sql.js +222 -0
  116. package/dist/engine.d.ts +77 -0
  117. package/dist/engine.js +240 -0
  118. package/dist/executor/adapters/mysql.d.ts +2 -0
  119. package/dist/executor/adapters/mysql.js +114 -0
  120. package/dist/executor/connection-pool.d.ts +105 -0
  121. package/dist/executor/connection-pool.js +236 -0
  122. package/dist/executor/explain.d.ts +5 -0
  123. package/dist/executor/explain.js +119 -0
  124. package/dist/executor/query-tracker.d.ts +45 -0
  125. package/dist/executor/query-tracker.js +83 -0
  126. package/dist/executor/result-normalizer.d.ts +6 -0
  127. package/dist/executor/result-normalizer.js +47 -0
  128. package/dist/executor/sql-executor.d.ts +32 -0
  129. package/dist/executor/sql-executor.js +250 -0
  130. package/dist/executor/types.d.ts +70 -0
  131. package/dist/executor/types.js +1 -0
  132. package/dist/index.d.ts +40 -0
  133. package/dist/index.js +21 -0
  134. package/dist/safety/confirmation-store.d.ts +44 -0
  135. package/dist/safety/confirmation-store.js +130 -0
  136. package/dist/safety/guardrail.d.ts +39 -0
  137. package/dist/safety/guardrail.js +99 -0
  138. package/dist/safety/parser/adapter.d.ts +10 -0
  139. package/dist/safety/parser/adapter.js +72 -0
  140. package/dist/safety/parser/ast-utils.d.ts +10 -0
  141. package/dist/safety/parser/ast-utils.js +167 -0
  142. package/dist/safety/parser/features.d.ts +12 -0
  143. package/dist/safety/parser/features.js +113 -0
  144. package/dist/safety/parser/index.d.ts +2 -0
  145. package/dist/safety/parser/index.js +1 -0
  146. package/dist/safety/parser/types.d.ts +76 -0
  147. package/dist/safety/parser/types.js +1 -0
  148. package/dist/safety/redaction.d.ts +34 -0
  149. package/dist/safety/redaction.js +186 -0
  150. package/dist/safety/sql-classifier.d.ts +19 -0
  151. package/dist/safety/sql-classifier.js +43 -0
  152. package/dist/safety/sql-validator.d.ts +19 -0
  153. package/dist/safety/sql-validator.js +143 -0
  154. package/dist/schema/adapters/mysql.d.ts +16 -0
  155. package/dist/schema/adapters/mysql.js +287 -0
  156. package/dist/schema/introspector.d.ts +70 -0
  157. package/dist/schema/introspector.js +40 -0
  158. package/dist/taurus/flashback.d.ts +36 -0
  159. package/dist/taurus/flashback.js +149 -0
  160. package/dist/taurus/recycle-bin.d.ts +14 -0
  161. package/dist/taurus/recycle-bin.js +61 -0
  162. package/dist/utils/formatter.d.ts +70 -0
  163. package/dist/utils/formatter.js +60 -0
  164. package/dist/utils/hash.d.ts +2 -0
  165. package/dist/utils/hash.js +247 -0
  166. package/dist/utils/id.d.ts +2 -0
  167. package/dist/utils/id.js +11 -0
  168. package/dist/utils/logger.d.ts +9 -0
  169. package/dist/utils/logger.js +39 -0
  170. package/package.json +46 -0
@@ -0,0 +1,65 @@
1
+ import type { Config } from "../config/index.js";
2
+ import type { SessionContext } from "../context/session-context.js";
3
+ import type { DiagnosisWindow } from "./types.js";
4
+ export type MetricAlias = "cpu_util" | "mem_util" | "connection_count" | "active_connection_count" | "connection_usage" | "qps" | "tps" | "slow_queries" | "storage_used_size" | "storage_write_delay" | "storage_read_delay" | "row_lock_time" | "row_lock_waits" | "long_trx_count" | "write_iops" | "read_iops" | "write_throughput" | "read_throughput" | "temp_tables_per_min";
5
+ export interface MetricPoint {
6
+ timestamp: number;
7
+ value: number;
8
+ }
9
+ export interface MetricSummary {
10
+ alias: MetricAlias;
11
+ metricName: string;
12
+ points: MetricPoint[];
13
+ latest?: number;
14
+ max?: number;
15
+ min?: number;
16
+ avg?: number;
17
+ }
18
+ export interface QueryMetricsInput {
19
+ aliases: MetricAlias[];
20
+ timeRange?: DiagnosisWindow;
21
+ }
22
+ export interface MetricsSource {
23
+ query(input: QueryMetricsInput, ctx: SessionContext): Promise<MetricSummary[]>;
24
+ }
25
+ type CesMetricsSourceOptions = {
26
+ endpoint: string;
27
+ projectId: string;
28
+ instanceId: string;
29
+ nodeId?: string;
30
+ authToken?: string;
31
+ accessKeyId?: string;
32
+ secretAccessKey?: string;
33
+ securityToken?: string;
34
+ namespace: string;
35
+ instanceDimension: string;
36
+ nodeDimension: string;
37
+ period: string;
38
+ filter: "average" | "max" | "min" | "sum" | "variance";
39
+ requestTimeoutMs: number;
40
+ defaultLookbackMinutes: number;
41
+ fetchImpl?: typeof fetch;
42
+ };
43
+ export declare class CesMetricsSource implements MetricsSource {
44
+ private readonly endpoint;
45
+ private readonly projectId;
46
+ private readonly instanceId;
47
+ private readonly nodeId?;
48
+ private readonly authToken?;
49
+ private readonly accessKeyId?;
50
+ private readonly secretAccessKey?;
51
+ private readonly securityToken?;
52
+ private readonly namespace;
53
+ private readonly instanceDimension;
54
+ private readonly nodeDimension;
55
+ private readonly period;
56
+ private readonly filter;
57
+ private readonly requestTimeoutMs;
58
+ private readonly defaultLookbackMinutes;
59
+ private readonly fetchImpl;
60
+ constructor(options: CesMetricsSourceOptions);
61
+ query(input: QueryMetricsInput): Promise<MetricSummary[]>;
62
+ private postJson;
63
+ }
64
+ export declare function createMetricsSource(config: Config): MetricsSource | undefined;
65
+ export {};
@@ -0,0 +1,280 @@
1
+ import { fetchHuaweiCloud } from "../cloud/auth.js";
2
+ const TAURUS_CES_METRICS = {
3
+ cpu_util: "gaussdb_mysql001_cpu_util",
4
+ mem_util: "gaussdb_mysql002_mem_util",
5
+ connection_count: "gaussdb_mysql006_conn_count",
6
+ active_connection_count: "gaussdb_mysql007_conn_active_count",
7
+ connection_usage: "gaussdb_mysql072_conn_usage",
8
+ qps: "gaussdb_mysql008_qps",
9
+ tps: "gaussdb_mysql009_tps",
10
+ slow_queries: "gaussdb_mysql074_slow_queries",
11
+ storage_used_size: "gaussdb_mysql048_disk_used_size",
12
+ storage_write_delay: "gaussdb_mysql104_dfv_write_delay",
13
+ storage_read_delay: "gaussdb_mysql105_dfv_read_delay",
14
+ row_lock_time: "gaussdb_mysql121_innodb_row_lock_time",
15
+ row_lock_waits: "gaussdb_mysql122_innodb_row_lock_waits",
16
+ long_trx_count: "gaussdb_mysql128_long_trx_count",
17
+ write_iops: "gaussdb_mysql342_iostat_iops_write",
18
+ read_iops: "gaussdb_mysql344_iostat_iops_read",
19
+ write_throughput: "gaussdb_mysql346_iostat_throughput_write",
20
+ read_throughput: "gaussdb_mysql348_iostat_throughput_read",
21
+ temp_tables_per_min: "gaussdb_mysql378_create_temp_tbl_per_min",
22
+ };
23
+ function readNumber(value) {
24
+ if (typeof value === "number" && Number.isFinite(value)) {
25
+ return value;
26
+ }
27
+ if (typeof value === "string" && value.trim().length > 0) {
28
+ const parsed = Number.parseFloat(value);
29
+ return Number.isFinite(parsed) ? parsed : undefined;
30
+ }
31
+ return undefined;
32
+ }
33
+ function parseRelativeLookback(value) {
34
+ if (!value) {
35
+ return undefined;
36
+ }
37
+ const match = value.trim().match(/^(\d+)\s*([mhd])$/i);
38
+ if (!match) {
39
+ return undefined;
40
+ }
41
+ const amount = Number.parseInt(match[1], 10);
42
+ switch (match[2].toLowerCase()) {
43
+ case "m":
44
+ return amount * 60_000;
45
+ case "h":
46
+ return amount * 3_600_000;
47
+ case "d":
48
+ return amount * 86_400_000;
49
+ default:
50
+ return undefined;
51
+ }
52
+ }
53
+ function normalizeTimeRange(input, defaultLookbackMinutes) {
54
+ const now = Date.now();
55
+ const to = input?.to ? Date.parse(input.to) : now;
56
+ const safeTo = Number.isFinite(to) ? to : now;
57
+ const relative = parseRelativeLookback(input?.relative);
58
+ const from = input?.from
59
+ ? Date.parse(input.from)
60
+ : relative
61
+ ? safeTo - relative
62
+ : safeTo - defaultLookbackMinutes * 60_000;
63
+ return {
64
+ from: Number.isFinite(from)
65
+ ? from
66
+ : safeTo - defaultLookbackMinutes * 60_000,
67
+ to: safeTo,
68
+ };
69
+ }
70
+ function summarize(alias, metricName, points) {
71
+ const sorted = [...points].sort((left, right) => left.timestamp - right.timestamp);
72
+ const values = sorted.map((point) => point.value);
73
+ return {
74
+ alias,
75
+ metricName,
76
+ points: sorted,
77
+ latest: values.at(-1),
78
+ max: values.length > 0 ? Math.max(...values) : undefined,
79
+ min: values.length > 0 ? Math.min(...values) : undefined,
80
+ avg: values.length > 0
81
+ ? values.reduce((sum, value) => sum + value, 0) / values.length
82
+ : undefined,
83
+ };
84
+ }
85
+ function parseMetricDataItem(item) {
86
+ const rawDatapoints = item.datapoints ?? item.data_points ?? item.values;
87
+ if (!Array.isArray(rawDatapoints)) {
88
+ return [];
89
+ }
90
+ return rawDatapoints
91
+ .map((entry) => {
92
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
93
+ return undefined;
94
+ }
95
+ const record = entry;
96
+ const timestamp = readNumber(record.timestamp ?? record.time ?? record.collect_time);
97
+ const value = readNumber(record.average) ??
98
+ readNumber(record.max) ??
99
+ readNumber(record.min) ??
100
+ readNumber(record.sum) ??
101
+ readNumber(record.value);
102
+ if (timestamp === undefined || value === undefined) {
103
+ return undefined;
104
+ }
105
+ return { timestamp, value };
106
+ })
107
+ .filter((point) => point !== undefined);
108
+ }
109
+ function pickMetricItems(payload) {
110
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
111
+ return [];
112
+ }
113
+ const record = payload;
114
+ const candidates = record.metrics ?? record.metric_data ?? record.items ?? record.datapoints;
115
+ if (!Array.isArray(candidates)) {
116
+ return [];
117
+ }
118
+ return candidates.filter((item) => item !== null && typeof item === "object" && !Array.isArray(item));
119
+ }
120
+ async function parseResponse(response) {
121
+ const payload = (await response.json());
122
+ return payload && typeof payload === "object" && !Array.isArray(payload)
123
+ ? payload
124
+ : {};
125
+ }
126
+ export class CesMetricsSource {
127
+ endpoint;
128
+ projectId;
129
+ instanceId;
130
+ nodeId;
131
+ authToken;
132
+ accessKeyId;
133
+ secretAccessKey;
134
+ securityToken;
135
+ namespace;
136
+ instanceDimension;
137
+ nodeDimension;
138
+ period;
139
+ filter;
140
+ requestTimeoutMs;
141
+ defaultLookbackMinutes;
142
+ fetchImpl;
143
+ constructor(options) {
144
+ this.endpoint = options.endpoint.replace(/\/+$/g, "");
145
+ this.projectId = options.projectId;
146
+ this.instanceId = options.instanceId;
147
+ this.nodeId = options.nodeId;
148
+ this.authToken = options.authToken;
149
+ this.accessKeyId = options.accessKeyId;
150
+ this.secretAccessKey = options.secretAccessKey;
151
+ this.securityToken = options.securityToken;
152
+ this.namespace = options.namespace;
153
+ this.instanceDimension = options.instanceDimension;
154
+ this.nodeDimension = options.nodeDimension;
155
+ this.period = options.period;
156
+ this.filter = options.filter;
157
+ this.requestTimeoutMs = options.requestTimeoutMs;
158
+ this.defaultLookbackMinutes = options.defaultLookbackMinutes;
159
+ this.fetchImpl = options.fetchImpl ?? fetch;
160
+ }
161
+ async query(input) {
162
+ const uniqueAliases = [...new Set(input.aliases)];
163
+ if (uniqueAliases.length === 0) {
164
+ return [];
165
+ }
166
+ const timeRange = normalizeTimeRange(input.timeRange, this.defaultLookbackMinutes);
167
+ const path = `/V1.0/${this.projectId}/batch-query-metric-data`;
168
+ const dimensions = [
169
+ {
170
+ name: this.instanceDimension,
171
+ value: this.instanceId,
172
+ },
173
+ ];
174
+ if (this.nodeId) {
175
+ dimensions.push({
176
+ name: this.nodeDimension,
177
+ value: this.nodeId,
178
+ });
179
+ }
180
+ const metricByName = new Map();
181
+ for (const alias of uniqueAliases) {
182
+ metricByName.set(TAURUS_CES_METRICS[alias], alias);
183
+ }
184
+ const payload = {
185
+ metrics: uniqueAliases.map((alias) => ({
186
+ namespace: this.namespace,
187
+ metric_name: TAURUS_CES_METRICS[alias],
188
+ dimensions,
189
+ })),
190
+ from: timeRange.from,
191
+ to: timeRange.to,
192
+ period: this.period,
193
+ filter: this.filter,
194
+ };
195
+ const body = await this.postJson(path, payload);
196
+ return pickMetricItems(body)
197
+ .map((item) => {
198
+ const metricName = typeof item.metric_name === "string"
199
+ ? item.metric_name
200
+ : typeof item.metricName === "string"
201
+ ? item.metricName
202
+ : undefined;
203
+ if (!metricName) {
204
+ return undefined;
205
+ }
206
+ const alias = metricByName.get(metricName);
207
+ if (!alias) {
208
+ return undefined;
209
+ }
210
+ return summarize(alias, metricName, parseMetricDataItem(item));
211
+ })
212
+ .filter((item) => item !== undefined);
213
+ }
214
+ async postJson(path, payload) {
215
+ const controller = new AbortController();
216
+ const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
217
+ try {
218
+ const body = JSON.stringify(payload);
219
+ const response = await fetchHuaweiCloud({
220
+ url: `${this.endpoint}${path}`,
221
+ method: "POST",
222
+ headers: {
223
+ "content-type": "application/json",
224
+ },
225
+ body,
226
+ auth: {
227
+ authToken: this.authToken,
228
+ accessKeyId: this.accessKeyId,
229
+ secretAccessKey: this.secretAccessKey,
230
+ securityToken: this.securityToken,
231
+ },
232
+ fetchImpl: (input, init) => this.fetchImpl(input, {
233
+ ...init,
234
+ signal: controller.signal,
235
+ }),
236
+ });
237
+ if (!response.ok) {
238
+ throw new Error(`CES batch-query-metric-data returned HTTP ${response.status}.`);
239
+ }
240
+ return parseResponse(response);
241
+ }
242
+ catch (error) {
243
+ if (error instanceof Error) {
244
+ throw error;
245
+ }
246
+ throw new Error("CES batch-query-metric-data request failed.");
247
+ }
248
+ finally {
249
+ clearTimeout(timer);
250
+ }
251
+ }
252
+ }
253
+ export function createMetricsSource(config) {
254
+ const ces = config.metricsSource?.ces;
255
+ if (!ces?.enabled ||
256
+ !ces.endpoint ||
257
+ !ces.projectId ||
258
+ !ces.instanceId ||
259
+ (!ces.authToken &&
260
+ !(config.cloud?.accessKeyId && config.cloud?.secretAccessKey))) {
261
+ return undefined;
262
+ }
263
+ return new CesMetricsSource({
264
+ endpoint: ces.endpoint,
265
+ projectId: ces.projectId,
266
+ instanceId: ces.instanceId,
267
+ nodeId: ces.nodeId,
268
+ authToken: ces.authToken,
269
+ accessKeyId: config.cloud?.accessKeyId,
270
+ secretAccessKey: config.cloud?.secretAccessKey,
271
+ securityToken: config.cloud?.securityToken,
272
+ namespace: ces.namespace,
273
+ instanceDimension: ces.instanceDimension,
274
+ nodeDimension: ces.nodeDimension,
275
+ period: ces.period,
276
+ filter: ces.filter,
277
+ requestTimeoutMs: ces.requestTimeoutMs,
278
+ defaultLookbackMinutes: ces.defaultLookbackMinutes,
279
+ });
280
+ }
@@ -0,0 +1,43 @@
1
+ import type { SessionContext } from "../../context/session-context.js";
2
+ import type { FindTopSlowSqlInput } from "../types.js";
3
+ import type { ExternalSlowSqlSample, ResolveSlowSqlInput, SlowSqlSource } from "./types.js";
4
+ type DasSlowSqlSourceOptions = {
5
+ endpoint: string;
6
+ projectId: string;
7
+ instanceId: string;
8
+ authToken?: string;
9
+ accessKeyId?: string;
10
+ secretAccessKey?: string;
11
+ securityToken?: string;
12
+ datastoreType: "MySQL" | "TaurusDB";
13
+ requestTimeoutMs: number;
14
+ defaultLookbackMinutes: number;
15
+ maxRecords: number;
16
+ maxPages: number;
17
+ fetchImpl?: typeof fetch;
18
+ };
19
+ export declare class DasSlowSqlSource implements SlowSqlSource {
20
+ private readonly endpoint;
21
+ private readonly projectId;
22
+ private readonly instanceId;
23
+ private readonly authToken?;
24
+ private readonly accessKeyId?;
25
+ private readonly secretAccessKey?;
26
+ private readonly securityToken?;
27
+ private readonly datastoreType;
28
+ private readonly requestTimeoutMs;
29
+ private readonly defaultLookbackMinutes;
30
+ private readonly maxRecords;
31
+ private readonly maxPages;
32
+ private readonly fetchImpl;
33
+ constructor(options: DasSlowSqlSourceOptions);
34
+ resolve(input: ResolveSlowSqlInput, ctx: SessionContext): Promise<ExternalSlowSqlSample | undefined>;
35
+ findTop(input: FindTopSlowSqlInput, _ctx: SessionContext): Promise<ExternalSlowSqlSample[]>;
36
+ private fetchSlowQueryLogs;
37
+ private fetchSqlStatements;
38
+ private collectPagedCandidates;
39
+ private pickBestMatch;
40
+ private toExternalSample;
41
+ private getJson;
42
+ }
43
+ export {};
@@ -0,0 +1,170 @@
1
+ import { fetchHuaweiCloud } from "../../cloud/auth.js";
2
+ import { buildQueryString, candidateToExternalSample, formatUnixSeconds, normalizeTimeRange, parseResponse, pickNextMarker, readNumber, readString, scoreCandidate, secondsToMs, sortExternalSamples } from "./utils.js";
3
+ import { parseDasSlowLogCandidate, parseDasSqlStatementCandidate, pickDasTopSlowArrays } from "./parsers.js";
4
+ export class DasSlowSqlSource {
5
+ endpoint;
6
+ projectId;
7
+ instanceId;
8
+ authToken;
9
+ accessKeyId;
10
+ secretAccessKey;
11
+ securityToken;
12
+ datastoreType;
13
+ requestTimeoutMs;
14
+ defaultLookbackMinutes;
15
+ maxRecords;
16
+ maxPages;
17
+ fetchImpl;
18
+ constructor(options) {
19
+ this.endpoint = options.endpoint.replace(/\/+$/g, "");
20
+ this.projectId = options.projectId;
21
+ this.instanceId = options.instanceId;
22
+ this.authToken = options.authToken;
23
+ this.accessKeyId = options.accessKeyId;
24
+ this.secretAccessKey = options.secretAccessKey;
25
+ this.securityToken = options.securityToken;
26
+ this.datastoreType = options.datastoreType;
27
+ this.requestTimeoutMs = options.requestTimeoutMs;
28
+ this.defaultLookbackMinutes = options.defaultLookbackMinutes;
29
+ this.maxRecords = options.maxRecords;
30
+ this.maxPages = options.maxPages;
31
+ this.fetchImpl = options.fetchImpl ?? fetch;
32
+ }
33
+ async resolve(input, ctx) {
34
+ const timeWindow = normalizeTimeRange(input.timeRange, this.defaultLookbackMinutes);
35
+ const slowLogCandidates = await this.fetchSlowQueryLogs(timeWindow, ctx);
36
+ const matchedSlowLog = this.pickBestMatch(slowLogCandidates, input);
37
+ if (matchedSlowLog) {
38
+ return this.toExternalSample(matchedSlowLog, input, "das_slow_query_logs");
39
+ }
40
+ const fullSqlCandidates = await this.fetchSqlStatements(timeWindow, ctx);
41
+ const matchedFullSql = this.pickBestMatch(fullSqlCandidates, input);
42
+ if (matchedFullSql) {
43
+ return this.toExternalSample(matchedFullSql, input, "das_sql_statements");
44
+ }
45
+ return undefined;
46
+ }
47
+ async findTop(input, _ctx) {
48
+ const timeWindow = normalizeTimeRange(input.timeRange, this.defaultLookbackMinutes);
49
+ const payload = await this.getJson(`/v3/${this.projectId}/instances/${this.instanceId}/top-slow-log`, {
50
+ datastore_type: this.datastoreType,
51
+ start_at: formatUnixSeconds(new Date(timeWindow.startTime)),
52
+ end_at: formatUnixSeconds(new Date(timeWindow.endTime)),
53
+ num: Math.min(input.topN ?? 5, this.maxRecords),
54
+ });
55
+ const samples = pickDasTopSlowArrays(payload)
56
+ .map((item) => candidateToExternalSample({
57
+ sql: readString(item.template) ??
58
+ readString(item.sql) ??
59
+ readString(item.sql_statement),
60
+ database: readString(item.databases) ?? readString(item.database),
61
+ user: readString(item.users) ?? readString(item.user),
62
+ execCount: readNumber(item.times) ?? readNumber(item.count),
63
+ avgLatencyMs: secondsToMs(item.avg_query_time) ??
64
+ secondsToMs(item.query_time),
65
+ avgLockTimeMs: secondsToMs(item.avg_lock_time),
66
+ avgRowsExamined: readNumber(item.rows_examined),
67
+ rowsSent: readNumber(item.rows_sent),
68
+ rawRef: `das:/v3/${this.projectId}/instances/${this.instanceId}/top-slow-log`,
69
+ }, "das_top_slow_log"))
70
+ .filter((value) => value !== undefined);
71
+ return sortExternalSamples(samples, input.sortBy).slice(0, Math.min(input.topN ?? 5, this.maxRecords));
72
+ }
73
+ async fetchSlowQueryLogs(timeWindow, ctx) {
74
+ return this.collectPagedCandidates("/slow-query-logs", timeWindow, ctx, "slow_logs", parseDasSlowLogCandidate);
75
+ }
76
+ async fetchSqlStatements(timeWindow, ctx) {
77
+ return this.collectPagedCandidates("/sql-statements", timeWindow, ctx, "sql_statements", parseDasSqlStatementCandidate);
78
+ }
79
+ async collectPagedCandidates(suffix, timeWindow, ctx, arrayKey, parser) {
80
+ const output = [];
81
+ let marker;
82
+ for (let page = 0; page < this.maxPages; page += 1) {
83
+ const path = `/v3/${this.projectId}/instances/${this.instanceId}${suffix}`;
84
+ const payload = await this.getJson(path, {
85
+ datastore_type: this.datastoreType,
86
+ start_at: formatUnixSeconds(new Date(timeWindow.startTime)),
87
+ end_at: formatUnixSeconds(new Date(timeWindow.endTime)),
88
+ limit: this.maxRecords,
89
+ marker,
90
+ });
91
+ const items = Array.isArray(payload[arrayKey])
92
+ ? payload[arrayKey].filter((item) => item !== null && typeof item === "object" && !Array.isArray(item))
93
+ : [];
94
+ for (const item of items) {
95
+ const candidate = parser(item, `das:${path}`);
96
+ if (!candidate) {
97
+ continue;
98
+ }
99
+ if (ctx.database &&
100
+ candidate.database &&
101
+ candidate.database !== ctx.database) {
102
+ continue;
103
+ }
104
+ output.push(candidate);
105
+ }
106
+ marker = pickNextMarker(payload);
107
+ if (!marker || items.length === 0 || output.length >= this.maxRecords) {
108
+ break;
109
+ }
110
+ }
111
+ return output.slice(0, this.maxRecords);
112
+ }
113
+ pickBestMatch(candidates, input) {
114
+ const scored = candidates
115
+ .map((candidate) => ({ candidate, score: scoreCandidate(candidate, input) }))
116
+ .sort((left, right) => right.score - left.score);
117
+ if (scored.length === 0) {
118
+ return undefined;
119
+ }
120
+ if (input.sqlHash || input.digestText) {
121
+ return scored[0].score > 0 ? scored[0].candidate : undefined;
122
+ }
123
+ return scored[0].candidate;
124
+ }
125
+ toExternalSample(candidate, input, source) {
126
+ const sample = candidateToExternalSample(candidate, source);
127
+ if (!sample) {
128
+ return undefined;
129
+ }
130
+ return {
131
+ ...sample,
132
+ sqlHash: input.sqlHash ?? sample.sqlHash,
133
+ digestText: input.digestText ?? sample.digestText,
134
+ };
135
+ }
136
+ async getJson(path, params) {
137
+ const queryString = buildQueryString(params);
138
+ const controller = new AbortController();
139
+ const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
140
+ try {
141
+ const response = await fetchHuaweiCloud({
142
+ url: `${this.endpoint}${path}${queryString ? `?${queryString}` : ""}`,
143
+ method: "GET",
144
+ headers: {
145
+ "content-type": "application/json",
146
+ },
147
+ auth: {
148
+ authToken: this.authToken,
149
+ accessKeyId: this.accessKeyId,
150
+ secretAccessKey: this.secretAccessKey,
151
+ securityToken: this.securityToken,
152
+ },
153
+ fetchImpl: (input, init) => this.fetchImpl(input, {
154
+ ...init,
155
+ signal: controller.signal,
156
+ }),
157
+ });
158
+ if (!response.ok) {
159
+ return {};
160
+ }
161
+ return parseResponse(response);
162
+ }
163
+ catch {
164
+ return {};
165
+ }
166
+ finally {
167
+ clearTimeout(timer);
168
+ }
169
+ }
170
+ }
@@ -0,0 +1,5 @@
1
+ import type { Config } from "../../config/index.js";
2
+ import type { DiagnoseSlowQueryInput } from "../types.js";
3
+ import type { ResolveSlowSqlInput, SlowSqlSource } from "./types.js";
4
+ export declare function createSlowSqlSource(config: Config): SlowSqlSource | undefined;
5
+ export declare function buildResolveSlowSqlInput(input: DiagnoseSlowQueryInput): ResolveSlowSqlInput;
@@ -0,0 +1,87 @@
1
+ import { DasSlowSqlSource } from "./das-source.js";
2
+ import { TaurusApiSlowSqlSource } from "./taurus-api-source.js";
3
+ import { sortExternalSamples } from "./utils.js";
4
+ class CompositeSlowSqlSource {
5
+ sources;
6
+ constructor(sources) {
7
+ this.sources = sources;
8
+ }
9
+ async resolve(input, ctx) {
10
+ for (const source of this.sources) {
11
+ const resolved = await source.resolve(input, ctx);
12
+ if (resolved) {
13
+ return resolved;
14
+ }
15
+ }
16
+ return undefined;
17
+ }
18
+ async findTop(input, ctx) {
19
+ const allSamples = (await Promise.all(this.sources.map((source) => source.findTop ? source.findTop(input, ctx) : []))).flat();
20
+ const deduped = allSamples.filter((sample, index, allItems) => {
21
+ const key = `${sample.source}:${sample.sqlHash}:${sample.digestText ?? ""}:${sample.sql}`;
22
+ return (allItems.findIndex((candidate) => {
23
+ const candidateKey = `${candidate.source}:${candidate.sqlHash}:${candidate.digestText ?? ""}:${candidate.sql}`;
24
+ return candidateKey === key;
25
+ }) === index);
26
+ });
27
+ return sortExternalSamples(deduped, input.sortBy).slice(0, Math.min(input.topN ?? 5, deduped.length));
28
+ }
29
+ }
30
+ export function createSlowSqlSource(config) {
31
+ const sources = [];
32
+ const taurusApi = config.slowSqlSource?.taurusApi;
33
+ if (taurusApi?.enabled &&
34
+ taurusApi.endpoint &&
35
+ taurusApi.projectId &&
36
+ taurusApi.instanceId &&
37
+ taurusApi.nodeId &&
38
+ (taurusApi.authToken ||
39
+ (config.cloud?.accessKeyId && config.cloud?.secretAccessKey))) {
40
+ sources.push(new TaurusApiSlowSqlSource({
41
+ endpoint: taurusApi.endpoint,
42
+ projectId: taurusApi.projectId,
43
+ instanceId: taurusApi.instanceId,
44
+ nodeId: taurusApi.nodeId,
45
+ authToken: taurusApi.authToken,
46
+ accessKeyId: config.cloud?.accessKeyId,
47
+ secretAccessKey: config.cloud?.secretAccessKey,
48
+ securityToken: config.cloud?.securityToken,
49
+ language: taurusApi.language,
50
+ requestTimeoutMs: taurusApi.requestTimeoutMs,
51
+ defaultLookbackMinutes: taurusApi.defaultLookbackMinutes,
52
+ maxRecords: taurusApi.maxRecords,
53
+ }));
54
+ }
55
+ const das = config.slowSqlSource?.das;
56
+ if (das?.enabled &&
57
+ das.endpoint &&
58
+ das.projectId &&
59
+ das.instanceId &&
60
+ (das.authToken || (config.cloud?.accessKeyId && config.cloud?.secretAccessKey))) {
61
+ sources.push(new DasSlowSqlSource({
62
+ endpoint: das.endpoint,
63
+ projectId: das.projectId,
64
+ instanceId: das.instanceId,
65
+ authToken: das.authToken,
66
+ accessKeyId: config.cloud?.accessKeyId,
67
+ secretAccessKey: config.cloud?.secretAccessKey,
68
+ securityToken: config.cloud?.securityToken,
69
+ datastoreType: das.datastoreType,
70
+ requestTimeoutMs: das.requestTimeoutMs,
71
+ defaultLookbackMinutes: das.defaultLookbackMinutes,
72
+ maxRecords: das.maxRecords,
73
+ maxPages: das.maxPages,
74
+ }));
75
+ }
76
+ if (sources.length === 0) {
77
+ return undefined;
78
+ }
79
+ return sources.length === 1 ? sources[0] : new CompositeSlowSqlSource(sources);
80
+ }
81
+ export function buildResolveSlowSqlInput(input) {
82
+ return {
83
+ sqlHash: input.sqlHash,
84
+ digestText: input.digestText,
85
+ timeRange: input.timeRange,
86
+ };
87
+ }
@@ -0,0 +1,7 @@
1
+ import type { TaurusApiCandidate } from "./types.js";
2
+ export declare function pickArrayCandidate(payload: unknown): Record<string, unknown>[];
3
+ export declare function parseStatisticsCandidate(item: Record<string, unknown>, rawRef: string): TaurusApiCandidate | undefined;
4
+ export declare function parseDetailCandidate(item: Record<string, unknown>, rawRef: string): TaurusApiCandidate | undefined;
5
+ export declare function parseDasSlowLogCandidate(item: Record<string, unknown>, rawRef: string): TaurusApiCandidate | undefined;
6
+ export declare function parseDasSqlStatementCandidate(item: Record<string, unknown>, rawRef: string): TaurusApiCandidate | undefined;
7
+ export declare function pickDasTopSlowArrays(payload: Record<string, unknown>): Record<string, unknown>[];