@mastra/clickhouse 1.16.1 → 1.17.0-alpha.1
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/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/integrations-databases-clickhouse.md +6 -0
- package/dist/index.cjs +492 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +469 -36
- package/dist/index.js.map +1 -1
- package/dist/storage/db/replication.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts +3 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/deletion-requests.d.ts +28 -0
- package/dist/storage/domains/observability/v-next/deletion-requests.d.ts.map +1 -0
- package/dist/storage/domains/observability/v-next/index.d.ts +8 -2
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts +12 -0
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -0
- package/dist/storage/domains/observability/v-next/tracing.d.ts +22 -7
- package/dist/storage/domains/observability/v-next/tracing.d.ts.map +1 -1
- package/dist/storage/index.d.ts +4 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { createClient } from "@clickhouse/client";
|
|
2
2
|
import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
|
|
3
|
+
import * as coreStorage from "@mastra/core/storage";
|
|
3
4
|
import { BRANCH_SPAN_TYPES, EntityType, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, ObservabilityStorage, SCORERS_SCHEMA, SPAN_SCHEMA, ScoresStorage, TABLE_AGENT_VERSIONS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_HARNESS_SESSIONS, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_CURSORS, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_TRACES, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, TraceStatus, WorkflowsStorage, buildInputPreview, calculatePagination, computeTraceStatus, createStorageErrorId, getDefaultValue, getSqlType, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, normalizePerPage, safelyParseJSON, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
|
|
4
5
|
import { MessageList } from "@mastra/core/agent";
|
|
5
6
|
import { MastraBase } from "@mastra/core/base";
|
|
6
7
|
import { parseFieldKey } from "@mastra/core/utils";
|
|
7
8
|
import { coreFeatures } from "@mastra/core/features";
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
8
10
|
import { saveScorePayloadSchema } from "@mastra/core/evals";
|
|
9
11
|
//#region src/storage/db/replication.ts
|
|
10
12
|
const DEFAULT_ZOOKEEPER_PATH = "/clickhouse/tables/{shard}/{database}/{table}";
|
|
@@ -70,12 +72,22 @@ function getEngineNameAndArgs(engine) {
|
|
|
70
72
|
}
|
|
71
73
|
function hasBalancedParens(s) {
|
|
72
74
|
let depth = 0;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
75
|
+
let quote;
|
|
76
|
+
for (let i = 0; i < s.length; i++) {
|
|
77
|
+
const c = s[i];
|
|
78
|
+
if (quote) {
|
|
79
|
+
if (c === "\\") i++;
|
|
80
|
+
else if (c === quote) quote = void 0;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (c === "`" || c === "\"" || c === "'") quote = c;
|
|
84
|
+
else if (c === "(") depth++;
|
|
85
|
+
else if (c === ")") {
|
|
86
|
+
depth--;
|
|
87
|
+
if (depth < 0) return false;
|
|
88
|
+
}
|
|
77
89
|
}
|
|
78
|
-
return depth === 0;
|
|
90
|
+
return depth === 0 && quote === void 0;
|
|
79
91
|
}
|
|
80
92
|
function buildReplicatedTableEngine(engine, replication) {
|
|
81
93
|
if (!replication) return engine;
|
|
@@ -147,20 +159,51 @@ function addOnClusterToDDL(sql, replication) {
|
|
|
147
159
|
return out;
|
|
148
160
|
}
|
|
149
161
|
function rewriteEngineClauses(sql, replication) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
162
|
+
const enginePattern = /ENGINE\s*=\s*(\w+)/gi;
|
|
163
|
+
let result = "";
|
|
164
|
+
let consumedUntil = 0;
|
|
165
|
+
for (let match = enginePattern.exec(sql); match; match = enginePattern.exec(sql)) {
|
|
166
|
+
const engineName = match[1];
|
|
167
|
+
if (!engineName) continue;
|
|
168
|
+
let engineEnd = enginePattern.lastIndex;
|
|
169
|
+
let argsStart = engineEnd;
|
|
170
|
+
while (/\s/.test(sql[argsStart] ?? "")) argsStart++;
|
|
171
|
+
let engine = engineName;
|
|
172
|
+
if (sql[argsStart] === "(") {
|
|
173
|
+
let depth = 0;
|
|
174
|
+
let closingParenEnd;
|
|
175
|
+
let quote;
|
|
176
|
+
for (let i = argsStart; i < sql.length; i++) {
|
|
177
|
+
const char = sql[i];
|
|
178
|
+
if (quote) {
|
|
179
|
+
if (char === "\\") i++;
|
|
180
|
+
else if (char === quote) quote = void 0;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (char === "`" || char === "\"" || char === "'") quote = char;
|
|
184
|
+
else if (char === "(") depth++;
|
|
185
|
+
else if (char === ")") {
|
|
186
|
+
depth--;
|
|
187
|
+
if (depth === 0) {
|
|
188
|
+
closingParenEnd = i + 1;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (closingParenEnd === void 0) {
|
|
194
|
+
result += sql.slice(consumedUntil, enginePattern.lastIndex);
|
|
195
|
+
consumedUntil = enginePattern.lastIndex;
|
|
196
|
+
continue;
|
|
160
197
|
}
|
|
198
|
+
engineEnd = closingParenEnd;
|
|
199
|
+
engine = `${engineName}${sql.slice(argsStart, engineEnd)}`;
|
|
161
200
|
}
|
|
162
|
-
|
|
163
|
-
|
|
201
|
+
result += sql.slice(consumedUntil, match.index);
|
|
202
|
+
result += `ENGINE = ${buildReplicatedTableEngine(engine, replication)}`;
|
|
203
|
+
consumedUntil = engineEnd;
|
|
204
|
+
enginePattern.lastIndex = engineEnd;
|
|
205
|
+
}
|
|
206
|
+
return result + sql.slice(consumedUntil);
|
|
164
207
|
}
|
|
165
208
|
function applyReplicationToDDL(sql, replication) {
|
|
166
209
|
return addOnClusterToDDL(replication ? rewriteEngineClauses(sql, replication) : sql, replication);
|
|
@@ -2477,6 +2520,7 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
|
|
|
2477
2520
|
}
|
|
2478
2521
|
}
|
|
2479
2522
|
async batchDeleteTraces(args) {
|
|
2523
|
+
this.assertUnscopedBatchDeleteTraces(args);
|
|
2480
2524
|
try {
|
|
2481
2525
|
if (args.traceIds.length === 0) return;
|
|
2482
2526
|
await this.client.command({
|
|
@@ -2503,6 +2547,7 @@ const TABLE_METRIC_EVENTS = "mastra_metric_events";
|
|
|
2503
2547
|
const TABLE_LOG_EVENTS = "mastra_log_events";
|
|
2504
2548
|
const TABLE_SCORE_EVENTS = "mastra_score_events";
|
|
2505
2549
|
const TABLE_FEEDBACK_EVENTS = "mastra_feedback_events";
|
|
2550
|
+
const TABLE_DELETION_REQUESTS = "mastra_deletion_requests";
|
|
2506
2551
|
const TABLE_METRIC_EVENTS_DELTA = "mastra_metric_events_delta";
|
|
2507
2552
|
const TABLE_LOG_EVENTS_DELTA = "mastra_log_events_delta";
|
|
2508
2553
|
const TABLE_SCORE_EVENTS_DELTA = "mastra_score_events_delta";
|
|
@@ -3198,6 +3243,23 @@ PARTITION BY toDate(timestamp)
|
|
|
3198
3243
|
ORDER BY (traceId, timestamp, feedbackId)
|
|
3199
3244
|
SETTINGS allow_nullable_key = 1
|
|
3200
3245
|
`;
|
|
3246
|
+
const DELETION_REQUESTS_DDL = `
|
|
3247
|
+
CREATE TABLE IF NOT EXISTS ${TABLE_DELETION_REQUESTS} (
|
|
3248
|
+
requestId String,
|
|
3249
|
+
organizationId String DEFAULT '',
|
|
3250
|
+
resourceId String DEFAULT '',
|
|
3251
|
+
signal LowCardinality(String),
|
|
3252
|
+
predicateType LowCardinality(String),
|
|
3253
|
+
predicateValues Array(String),
|
|
3254
|
+
requestedAt DateTime64(3),
|
|
3255
|
+
requestedBy String DEFAULT '',
|
|
3256
|
+
lastAppliedAt DateTime64(3) DEFAULT 0,
|
|
3257
|
+
purgeVerifiedAt DateTime64(3) DEFAULT 0,
|
|
3258
|
+
updatedAt DateTime64(3)
|
|
3259
|
+
)
|
|
3260
|
+
ENGINE = ReplacingMergeTree(updatedAt)
|
|
3261
|
+
ORDER BY (organizationId, resourceId, requestId)
|
|
3262
|
+
`;
|
|
3201
3263
|
function buildFeedbackEventsDeltaDDL() {
|
|
3202
3264
|
return `
|
|
3203
3265
|
CREATE TABLE IF NOT EXISTS ${TABLE_FEEDBACK_EVENTS_DELTA} (
|
|
@@ -3347,6 +3409,7 @@ const BASE_TABLE_DDL = [
|
|
|
3347
3409
|
LOG_EVENTS_DDL,
|
|
3348
3410
|
SCORE_EVENTS_DDL,
|
|
3349
3411
|
FEEDBACK_EVENTS_DDL,
|
|
3412
|
+
DELETION_REQUESTS_DDL,
|
|
3350
3413
|
DISCOVERY_VALUES_DDL,
|
|
3351
3414
|
DISCOVERY_PAIRS_DDL
|
|
3352
3415
|
];
|
|
@@ -3431,6 +3494,7 @@ const ALL_TABLE_NAMES = [
|
|
|
3431
3494
|
TABLE_LOG_EVENTS,
|
|
3432
3495
|
TABLE_SCORE_EVENTS,
|
|
3433
3496
|
TABLE_FEEDBACK_EVENTS,
|
|
3497
|
+
TABLE_DELETION_REQUESTS,
|
|
3434
3498
|
TABLE_METRIC_EVENTS_DELTA,
|
|
3435
3499
|
TABLE_LOG_EVENTS_DELTA,
|
|
3436
3500
|
TABLE_SCORE_EVENTS_DELTA,
|
|
@@ -4088,6 +4152,35 @@ function feedbackRecordToRow(feedback) {
|
|
|
4088
4152
|
};
|
|
4089
4153
|
}
|
|
4090
4154
|
//#endregion
|
|
4155
|
+
//#region src/storage/domains/observability/v-next/deletion-requests.ts
|
|
4156
|
+
const EPOCH = "1970-01-01T00:00:00.000Z";
|
|
4157
|
+
async function recordDeletionRequest(client, args) {
|
|
4158
|
+
const row = {
|
|
4159
|
+
requestId: args.requestId,
|
|
4160
|
+
organizationId: args.organizationId ?? "",
|
|
4161
|
+
resourceId: args.resourceId ?? "",
|
|
4162
|
+
signal: args.signal,
|
|
4163
|
+
predicateType: args.predicateType,
|
|
4164
|
+
predicateValues: args.predicateValues,
|
|
4165
|
+
requestedAt: args.requestedAt,
|
|
4166
|
+
requestedBy: args.requestedBy ?? "",
|
|
4167
|
+
lastAppliedAt: EPOCH,
|
|
4168
|
+
purgeVerifiedAt: EPOCH,
|
|
4169
|
+
updatedAt: args.requestedAt
|
|
4170
|
+
};
|
|
4171
|
+
await client.insert({
|
|
4172
|
+
table: TABLE_DELETION_REQUESTS,
|
|
4173
|
+
values: [row],
|
|
4174
|
+
format: "JSONEachRow",
|
|
4175
|
+
clickhouse_settings: isReplicationConfigured(args.replication) ? {
|
|
4176
|
+
...CH_INSERT_SETTINGS,
|
|
4177
|
+
insert_quorum: "auto",
|
|
4178
|
+
insert_quorum_parallel: 1
|
|
4179
|
+
} : CH_INSERT_SETTINGS
|
|
4180
|
+
});
|
|
4181
|
+
return row;
|
|
4182
|
+
}
|
|
4183
|
+
//#endregion
|
|
4091
4184
|
//#region src/storage/domains/observability/v-next/discovery.ts
|
|
4092
4185
|
async function queryJson$3(client, query, params = {}) {
|
|
4093
4186
|
return await (await client.query({
|
|
@@ -6091,6 +6184,269 @@ async function getScorePercentiles(client, args) {
|
|
|
6091
6184
|
return { series };
|
|
6092
6185
|
}
|
|
6093
6186
|
//#endregion
|
|
6187
|
+
//#region src/storage/domains/observability/v-next/trace-query.ts
|
|
6188
|
+
const TRACE_STATUS_SQL = `if(isNotNull(r.error), 'error', 'success')`;
|
|
6189
|
+
const TRACE_FIELDS = {
|
|
6190
|
+
traceId: {
|
|
6191
|
+
sql: "r.traceId",
|
|
6192
|
+
parameterType: "String"
|
|
6193
|
+
},
|
|
6194
|
+
threadId: {
|
|
6195
|
+
sql: "r.threadId",
|
|
6196
|
+
parameterType: "String"
|
|
6197
|
+
},
|
|
6198
|
+
resourceId: {
|
|
6199
|
+
sql: "r.resourceId",
|
|
6200
|
+
parameterType: "String"
|
|
6201
|
+
},
|
|
6202
|
+
startedAt: {
|
|
6203
|
+
sql: "r.startedAt",
|
|
6204
|
+
parameterType: "DateTime64(3, 'UTC')"
|
|
6205
|
+
},
|
|
6206
|
+
endedAt: {
|
|
6207
|
+
sql: "r.endedAt",
|
|
6208
|
+
parameterType: "DateTime64(3, 'UTC')"
|
|
6209
|
+
},
|
|
6210
|
+
entityName: {
|
|
6211
|
+
sql: "r.entityName",
|
|
6212
|
+
parameterType: "String"
|
|
6213
|
+
},
|
|
6214
|
+
entityType: {
|
|
6215
|
+
sql: "r.entityType",
|
|
6216
|
+
parameterType: "String"
|
|
6217
|
+
},
|
|
6218
|
+
environment: {
|
|
6219
|
+
sql: "r.environment",
|
|
6220
|
+
parameterType: "String"
|
|
6221
|
+
},
|
|
6222
|
+
status: {
|
|
6223
|
+
sql: TRACE_STATUS_SQL,
|
|
6224
|
+
parameterType: "String"
|
|
6225
|
+
}
|
|
6226
|
+
};
|
|
6227
|
+
const SPAN_FIELDS = {
|
|
6228
|
+
spanType: {
|
|
6229
|
+
sql: "s.spanType",
|
|
6230
|
+
parameterType: "String"
|
|
6231
|
+
},
|
|
6232
|
+
error: {
|
|
6233
|
+
sql: "s.error",
|
|
6234
|
+
parameterType: "String"
|
|
6235
|
+
}
|
|
6236
|
+
};
|
|
6237
|
+
const SCORE_FIELDS = {
|
|
6238
|
+
scorerId: {
|
|
6239
|
+
sql: "s.scorerId",
|
|
6240
|
+
parameterType: "String"
|
|
6241
|
+
},
|
|
6242
|
+
score: {
|
|
6243
|
+
sql: "s.score",
|
|
6244
|
+
parameterType: "Float64"
|
|
6245
|
+
}
|
|
6246
|
+
};
|
|
6247
|
+
const TRACE_SELECT = `
|
|
6248
|
+
r.traceId AS traceId,
|
|
6249
|
+
r.spanId AS rootSpanId,
|
|
6250
|
+
r.threadId AS threadId,
|
|
6251
|
+
r.resourceId AS resourceId,
|
|
6252
|
+
r.startedAt AS startedAt,
|
|
6253
|
+
r.endedAt AS endedAt,
|
|
6254
|
+
r.entityName AS entityName,
|
|
6255
|
+
r.entityType AS entityType,
|
|
6256
|
+
r.environment AS environment,
|
|
6257
|
+
${TRACE_STATUS_SQL} AS status`;
|
|
6258
|
+
var ParameterBuilder = class {
|
|
6259
|
+
params = {};
|
|
6260
|
+
#next = 1;
|
|
6261
|
+
add(value, type) {
|
|
6262
|
+
const name = `trace_query_${this.#next++}`;
|
|
6263
|
+
this.params[name] = type === "DateTime64(3, 'UTC')" ? new Date(value).toISOString().replace("T", " ").replace(/Z$/, "") : value;
|
|
6264
|
+
return `{${name}:${type}}`;
|
|
6265
|
+
}
|
|
6266
|
+
};
|
|
6267
|
+
function fieldDefinition(registry, field) {
|
|
6268
|
+
const definition = registry[field];
|
|
6269
|
+
if (definition === void 0) throw new Error(`Unsupported trusted trace-query field: ${field}`);
|
|
6270
|
+
return definition;
|
|
6271
|
+
}
|
|
6272
|
+
function resolveOrderField(field) {
|
|
6273
|
+
if (field === "startedAt" || field === "endedAt") return field;
|
|
6274
|
+
throw new Error(`Unsupported trusted trace-query field: ${field}`);
|
|
6275
|
+
}
|
|
6276
|
+
function compileScalarPredicate(predicate, registry, parameters) {
|
|
6277
|
+
if (predicate.type === "boolean") return predicate.args.map((arg) => `(${compileScalarPredicate(arg, registry, parameters)})`).join(predicate.operator === "and" ? " AND " : " OR ");
|
|
6278
|
+
if (predicate.type === "not") return `NOT (${compileScalarPredicate(predicate.arg, registry, parameters)})`;
|
|
6279
|
+
const field = fieldDefinition(registry, predicate.field);
|
|
6280
|
+
if (predicate.type === "presence") return `${predicate.operator === "exists" ? "isNotNull" : "isNull"}(${field.sql})`;
|
|
6281
|
+
if (predicate.type === "membership") {
|
|
6282
|
+
const values = predicate.values.map((value) => parameters.add(value, field.parameterType)).join(", ");
|
|
6283
|
+
return `ifNull(${`${field.sql} ${predicate.operator === "in" ? "IN" : "NOT IN"} (${values})`}, ${predicate.operator === "in" ? "0" : "1"})`;
|
|
6284
|
+
}
|
|
6285
|
+
const parameter = parameters.add(predicate.value, field.parameterType);
|
|
6286
|
+
const operator = {
|
|
6287
|
+
eq: "=",
|
|
6288
|
+
ne: "!=",
|
|
6289
|
+
lt: "<",
|
|
6290
|
+
lte: "<=",
|
|
6291
|
+
gt: ">",
|
|
6292
|
+
gte: ">="
|
|
6293
|
+
}[predicate.operator];
|
|
6294
|
+
if (operator === void 0) throw new Error(`Unsupported trusted trace-query operator: ${predicate.operator}`);
|
|
6295
|
+
return `ifNull(${field.sql} ${operator} ${parameter}, ${predicate.operator === "ne" ? "1" : "0"})`;
|
|
6296
|
+
}
|
|
6297
|
+
function collectRelationCollections(predicate, collections = /* @__PURE__ */ new Set()) {
|
|
6298
|
+
if (!predicate) return collections;
|
|
6299
|
+
if (predicate.type === "relation") collections.add(predicate.collection);
|
|
6300
|
+
else if (predicate.type === "boolean") for (const arg of predicate.args) collectRelationCollections(arg, collections);
|
|
6301
|
+
else if (predicate.type === "not") collectRelationCollections(predicate.arg, collections);
|
|
6302
|
+
return collections;
|
|
6303
|
+
}
|
|
6304
|
+
function compilePredicate(predicate, parameters) {
|
|
6305
|
+
if (predicate.type === "relation") {
|
|
6306
|
+
const registry = predicate.collection === "spans" ? SPAN_FIELDS : SCORE_FIELDS;
|
|
6307
|
+
const existence = `EXISTS (
|
|
6308
|
+
SELECT 1 FROM ${predicate.collection === "spans" ? "current_spans" : "current_scores"} s
|
|
6309
|
+
WHERE s.traceId = r.traceId
|
|
6310
|
+
AND (${compileScalarPredicate(predicate.predicate, registry, parameters)})
|
|
6311
|
+
)`;
|
|
6312
|
+
return predicate.quantifier === "some" ? existence : `NOT ${existence}`;
|
|
6313
|
+
}
|
|
6314
|
+
if (predicate.type === "boolean") return predicate.args.map((arg) => `(${compilePredicate(arg, parameters)})`).join(predicate.operator === "and" ? " AND " : " OR ");
|
|
6315
|
+
if (predicate.type === "not") return `NOT (${compilePredicate(predicate.arg, parameters)})`;
|
|
6316
|
+
return compileScalarPredicate(predicate, TRACE_FIELDS, parameters);
|
|
6317
|
+
}
|
|
6318
|
+
function compileClickHouseTraceQuery(plan) {
|
|
6319
|
+
const parameters = new ParameterBuilder();
|
|
6320
|
+
const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
|
|
6321
|
+
const to = parameters.add(plan.timeRange.to, "DateTime64(3, 'UTC')");
|
|
6322
|
+
const relationCollections = collectRelationCollections(plan.where);
|
|
6323
|
+
const ctes = [`current_roots AS (
|
|
6324
|
+
SELECT * FROM (
|
|
6325
|
+
SELECT *
|
|
6326
|
+
FROM ${TABLE_TRACE_ROOTS}
|
|
6327
|
+
ORDER BY dedupeKey
|
|
6328
|
+
LIMIT 1 BY dedupeKey
|
|
6329
|
+
)
|
|
6330
|
+
ORDER BY traceId, dedupeKey
|
|
6331
|
+
LIMIT 1 BY traceId
|
|
6332
|
+
)`, `root_scope AS (
|
|
6333
|
+
SELECT *
|
|
6334
|
+
FROM current_roots
|
|
6335
|
+
WHERE startedAt >= ${from}
|
|
6336
|
+
AND startedAt < ${to}
|
|
6337
|
+
)`];
|
|
6338
|
+
if (relationCollections.has("spans")) ctes.push(`current_spans AS (
|
|
6339
|
+
SELECT traceId, spanType, error
|
|
6340
|
+
FROM ${TABLE_SPAN_EVENTS}
|
|
6341
|
+
WHERE traceId IN (SELECT traceId FROM root_scope)
|
|
6342
|
+
ORDER BY dedupeKey
|
|
6343
|
+
LIMIT 1 BY dedupeKey
|
|
6344
|
+
)`);
|
|
6345
|
+
if (relationCollections.has("scores")) ctes.push(`current_scores AS (
|
|
6346
|
+
SELECT traceId, scorerId, score
|
|
6347
|
+
FROM ${TABLE_SCORE_EVENTS}
|
|
6348
|
+
WHERE isNotNull(traceId)
|
|
6349
|
+
AND traceId IN (SELECT traceId FROM root_scope)
|
|
6350
|
+
)`);
|
|
6351
|
+
const predicate = plan.where ? compilePredicate(plan.where, parameters) : "1";
|
|
6352
|
+
ctes.push(`candidates AS (
|
|
6353
|
+
SELECT ${TRACE_SELECT}
|
|
6354
|
+
FROM root_scope r
|
|
6355
|
+
WHERE ${predicate}
|
|
6356
|
+
)`);
|
|
6357
|
+
const candidates = `WITH ${ctes.join(",\n")}`;
|
|
6358
|
+
if (plan.result === "groups") return {
|
|
6359
|
+
query: `${candidates}
|
|
6360
|
+
SELECT threadId
|
|
6361
|
+
FROM candidates
|
|
6362
|
+
WHERE isNotNull(threadId) ${plan.cursor ? `AND threadId > ${parameters.add(plan.cursor.threadId, "String")}` : ""}
|
|
6363
|
+
GROUP BY threadId
|
|
6364
|
+
ORDER BY threadId ASC
|
|
6365
|
+
LIMIT ${parameters.add(plan.limit + 1, "UInt64")}`,
|
|
6366
|
+
query_params: parameters.params
|
|
6367
|
+
};
|
|
6368
|
+
const orderField = resolveOrderField(plan.orderBy.field);
|
|
6369
|
+
const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
|
|
6370
|
+
let pageCondition = "";
|
|
6371
|
+
if (plan.cursor) {
|
|
6372
|
+
const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
|
|
6373
|
+
const sortValue = parameters.add(plan.cursor.sortValue, "DateTime64(3, 'UTC')");
|
|
6374
|
+
pageCondition = `WHERE (${orderField} ${comparison} ${sortValue} OR (${orderField} = ${sortValue} AND traceId > ${parameters.add(plan.cursor.traceId, "String")}))`;
|
|
6375
|
+
}
|
|
6376
|
+
const limit = parameters.add(plan.limit + 1, "UInt64");
|
|
6377
|
+
return {
|
|
6378
|
+
query: `${candidates}
|
|
6379
|
+
SELECT *
|
|
6380
|
+
FROM candidates
|
|
6381
|
+
${pageCondition}
|
|
6382
|
+
ORDER BY ${orderField} ${direction}, traceId ASC
|
|
6383
|
+
LIMIT ${limit}`,
|
|
6384
|
+
query_params: parameters.params
|
|
6385
|
+
};
|
|
6386
|
+
}
|
|
6387
|
+
function asIsoTimestamp(value) {
|
|
6388
|
+
return new Date(value).toISOString();
|
|
6389
|
+
}
|
|
6390
|
+
function isClickHouseExecutionTimeout(error) {
|
|
6391
|
+
if (!error || typeof error !== "object") return false;
|
|
6392
|
+
const candidate = error;
|
|
6393
|
+
return String(candidate.code ?? "") === "159" || candidate.type === "TIMEOUT_EXCEEDED";
|
|
6394
|
+
}
|
|
6395
|
+
async function runWithClickHouseTraceQueryTimeout(client, timeoutMs, compiled, queryId) {
|
|
6396
|
+
const resolvedTimeoutMs = coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
6397
|
+
try {
|
|
6398
|
+
return await (await client.query({
|
|
6399
|
+
query: compiled.query,
|
|
6400
|
+
query_params: compiled.query_params,
|
|
6401
|
+
query_id: queryId,
|
|
6402
|
+
format: "JSONEachRow",
|
|
6403
|
+
clickhouse_settings: {
|
|
6404
|
+
...CH_SETTINGS,
|
|
6405
|
+
max_execution_time: resolvedTimeoutMs / 1e3
|
|
6406
|
+
}
|
|
6407
|
+
})).json();
|
|
6408
|
+
} catch (error) {
|
|
6409
|
+
if (isClickHouseExecutionTimeout(error)) throw new coreStorage.TraceQueryExecutionError();
|
|
6410
|
+
throw error;
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
async function queryTraces(client, plan, timeoutMs) {
|
|
6414
|
+
const rows = await runWithClickHouseTraceQueryTimeout(client, timeoutMs, compileClickHouseTraceQuery(plan));
|
|
6415
|
+
const visibleRows = rows.slice(0, plan.limit);
|
|
6416
|
+
if (plan.result === "groups") {
|
|
6417
|
+
const groups = visibleRows.map((row) => ({ threadId: String(row.threadId) }));
|
|
6418
|
+
const last = groups.at(-1);
|
|
6419
|
+
return coreStorage.traceQueryResponseSchema.parse({
|
|
6420
|
+
groups,
|
|
6421
|
+
page: { next: rows.length > plan.limit && last ? coreStorage.encodeTraceQueryCursor(plan, {
|
|
6422
|
+
result: "groups",
|
|
6423
|
+
threadId: last.threadId
|
|
6424
|
+
}) : null }
|
|
6425
|
+
});
|
|
6426
|
+
}
|
|
6427
|
+
const traces = visibleRows.map((row) => ({
|
|
6428
|
+
traceId: String(row.traceId),
|
|
6429
|
+
rootSpanId: String(row.rootSpanId),
|
|
6430
|
+
threadId: row.threadId == null ? null : String(row.threadId),
|
|
6431
|
+
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
6432
|
+
startedAt: asIsoTimestamp(row.startedAt),
|
|
6433
|
+
endedAt: asIsoTimestamp(row.endedAt),
|
|
6434
|
+
entityName: row.entityName == null ? null : String(row.entityName),
|
|
6435
|
+
entityType: row.entityType == null ? null : String(row.entityType),
|
|
6436
|
+
environment: row.environment == null ? null : String(row.environment),
|
|
6437
|
+
status: row.status
|
|
6438
|
+
}));
|
|
6439
|
+
const last = traces.at(-1);
|
|
6440
|
+
return coreStorage.traceQueryResponseSchema.parse({
|
|
6441
|
+
traces,
|
|
6442
|
+
page: { next: rows.length > plan.limit && last ? coreStorage.encodeTraceQueryCursor(plan, {
|
|
6443
|
+
result: "traces",
|
|
6444
|
+
sortValue: last[plan.orderBy.field],
|
|
6445
|
+
traceId: last.traceId
|
|
6446
|
+
}) : null }
|
|
6447
|
+
});
|
|
6448
|
+
}
|
|
6449
|
+
//#endregion
|
|
6094
6450
|
//#region src/storage/domains/observability/v-next/trace-roots.ts
|
|
6095
6451
|
/**
|
|
6096
6452
|
* Get the root span for a trace, reading from trace_roots as compatibility path.
|
|
@@ -6327,6 +6683,17 @@ function buildTraceCursor(row) {
|
|
|
6327
6683
|
}
|
|
6328
6684
|
//#endregion
|
|
6329
6685
|
//#region src/storage/domains/observability/v-next/tracing.ts
|
|
6686
|
+
/**
|
|
6687
|
+
* Tracing operations for ClickHouse v-next observability.
|
|
6688
|
+
*
|
|
6689
|
+
* Owns: batchCreateSpans, getSpan, getSpans, getTrace, getTraceLight,
|
|
6690
|
+
* listBranches, batchDeleteTraces, dangerouslyClearSpanEvents.
|
|
6691
|
+
* Delegates to trace-roots.ts: listTraces, getRootSpan.
|
|
6692
|
+
*
|
|
6693
|
+
* `listBranches` reads from the MV-fed `mastra_trace_branches` table (one row
|
|
6694
|
+
* per branch anchor span). It lives here -- alongside the other read paths
|
|
6695
|
+
* over the trace data -- since branches are conceptually a subset of traces.
|
|
6696
|
+
*/
|
|
6330
6697
|
const BRANCH_SPAN_TYPE_SQL_LIST = BRANCH_SPAN_TYPES.map((t) => `'${t}'`).join(", ");
|
|
6331
6698
|
/** Insert a single completed span. */
|
|
6332
6699
|
async function createSpan(client, args) {
|
|
@@ -6466,16 +6833,40 @@ async function getTraceLight(client, args) {
|
|
|
6466
6833
|
};
|
|
6467
6834
|
}
|
|
6468
6835
|
/**
|
|
6469
|
-
* Delete traces by traceId
|
|
6470
|
-
*
|
|
6836
|
+
* Delete traces by traceId, cascading to trace-derived tables and trace-linked
|
|
6837
|
+
* signal events (metrics, logs, scores, feedback). Signal rows with a NULL
|
|
6838
|
+
* traceId are never affected.
|
|
6839
|
+
*
|
|
6840
|
+
* On the tracing tables, rows are targeted by tracing identity: traceId +
|
|
6841
|
+
* dedupeKey (which starts with traceId). The dedupeKey condition is redundant
|
|
6842
|
+
* for correctness (dedupeKey = traceId:spanId) but satisfies the design-doc
|
|
6843
|
+
* requirement that trace deletes reference dedupeKey and helps the engine
|
|
6844
|
+
* narrow within the sorted ORDER BY key. Signal tables key on their own event
|
|
6845
|
+
* ids, so they are targeted by traceId alone.
|
|
6846
|
+
*
|
|
6847
|
+
* `trace_branches` must be deleted explicitly: its MV fires on insert only,
|
|
6848
|
+
* so span deletes never propagate to it. Delta tables self-expire via TTL and
|
|
6849
|
+
* discovery tables self-heal, so neither needs explicit deletes.
|
|
6471
6850
|
*
|
|
6472
|
-
*
|
|
6473
|
-
*
|
|
6474
|
-
*
|
|
6475
|
-
*
|
|
6851
|
+
* Records the predicate before using lightweight DELETE FROM on every table.
|
|
6852
|
+
* Lightweight deletes hide rows through ClickHouse's delete mask; physical
|
|
6853
|
+
* removal depends on the deployment's configured retention and merge policy.
|
|
6854
|
+
*
|
|
6855
|
+
* When the optional tenant scope (`organizationId` / `resourceId`) is set,
|
|
6856
|
+
* every DELETE additionally requires the row's tenant columns to match.
|
|
6476
6857
|
*/
|
|
6477
|
-
async function batchDeleteTraces(client, args) {
|
|
6858
|
+
async function batchDeleteTraces(client, args, replication) {
|
|
6478
6859
|
if (args.traceIds.length === 0) return;
|
|
6860
|
+
await recordDeletionRequest(client, {
|
|
6861
|
+
requestId: randomUUID(),
|
|
6862
|
+
organizationId: args.organizationId,
|
|
6863
|
+
resourceId: args.resourceId,
|
|
6864
|
+
signal: "traces",
|
|
6865
|
+
predicateType: "traceIds",
|
|
6866
|
+
predicateValues: [...args.traceIds],
|
|
6867
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6868
|
+
replication
|
|
6869
|
+
});
|
|
6479
6870
|
const params = {};
|
|
6480
6871
|
const traceInPlaceholders = [];
|
|
6481
6872
|
const dedupeOrParts = [];
|
|
@@ -6489,13 +6880,35 @@ async function batchDeleteTraces(client, args) {
|
|
|
6489
6880
|
}
|
|
6490
6881
|
const traceInList = traceInPlaceholders.join(", ");
|
|
6491
6882
|
const dedupeCondition = dedupeOrParts.length === 1 ? dedupeOrParts[0] : `(${dedupeOrParts.join(" OR ")})`;
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6883
|
+
let scopeCondition = "";
|
|
6884
|
+
if (args.organizationId !== void 0) {
|
|
6885
|
+
params.scope_org = args.organizationId;
|
|
6886
|
+
scopeCondition += ` AND organizationId = {scope_org:String}`;
|
|
6887
|
+
}
|
|
6888
|
+
if (args.resourceId !== void 0) {
|
|
6889
|
+
params.scope_res = args.resourceId;
|
|
6890
|
+
scopeCondition += ` AND resourceId = {scope_res:String}`;
|
|
6891
|
+
}
|
|
6892
|
+
const tracingTables = [
|
|
6893
|
+
TABLE_SPAN_EVENTS,
|
|
6894
|
+
TABLE_TRACE_ROOTS,
|
|
6895
|
+
TABLE_TRACE_BRANCHES
|
|
6896
|
+
];
|
|
6897
|
+
const signalTables = [
|
|
6898
|
+
TABLE_METRIC_EVENTS,
|
|
6899
|
+
TABLE_LOG_EVENTS,
|
|
6900
|
+
TABLE_SCORE_EVENTS,
|
|
6901
|
+
TABLE_FEEDBACK_EVENTS
|
|
6902
|
+
];
|
|
6903
|
+
await Promise.all([...tracingTables.map((table) => client.command({
|
|
6904
|
+
query: `DELETE FROM ${table} WHERE traceId IN (${traceInList}) AND ${dedupeCondition}${scopeCondition}`,
|
|
6905
|
+
query_params: params,
|
|
6906
|
+
clickhouse_settings: { lightweight_deletes_sync: "2" }
|
|
6907
|
+
})), ...signalTables.map((table) => client.command({
|
|
6908
|
+
query: `DELETE FROM ${table} WHERE traceId IN (${traceInList})${scopeCondition}`,
|
|
6909
|
+
query_params: params,
|
|
6910
|
+
clickhouse_settings: { lightweight_deletes_sync: "2" }
|
|
6911
|
+
}))]);
|
|
6499
6912
|
}
|
|
6500
6913
|
/**
|
|
6501
6914
|
* List trace branches with optional filtering, pagination, and ordering.
|
|
@@ -7033,6 +7446,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
7033
7446
|
#retention;
|
|
7034
7447
|
#replication;
|
|
7035
7448
|
#deltaCursorStrategyOverride;
|
|
7449
|
+
#traceQueryTimeoutMs;
|
|
7036
7450
|
#deltaCursorStrategy = "fallback";
|
|
7037
7451
|
constructor(config) {
|
|
7038
7452
|
super();
|
|
@@ -7041,6 +7455,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
7041
7455
|
this.#replication = replication;
|
|
7042
7456
|
this.#retention = config.retention;
|
|
7043
7457
|
this.#deltaCursorStrategyOverride = config.deltaCursorStrategy;
|
|
7458
|
+
this.#traceQueryTimeoutMs = coreStorage.resolveTraceQueryTimeoutMs(config.traceQueryTimeoutMs);
|
|
7044
7459
|
}
|
|
7045
7460
|
async init() {
|
|
7046
7461
|
const migrationStatus = await checkSignalTablesMigrationStatus(this.#client);
|
|
@@ -7144,11 +7559,16 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
7144
7559
|
};
|
|
7145
7560
|
}
|
|
7146
7561
|
getFeatures() {
|
|
7147
|
-
if (!deltaPollingSupported(this.#deltaCursorStrategy)) return [
|
|
7562
|
+
if (!deltaPollingSupported(this.#deltaCursorStrategy)) return [
|
|
7563
|
+
"metrics",
|
|
7564
|
+
"logs",
|
|
7565
|
+
"trace-query"
|
|
7566
|
+
];
|
|
7148
7567
|
return [
|
|
7149
7568
|
"metrics",
|
|
7150
7569
|
"logs",
|
|
7151
|
-
"delta-polling"
|
|
7570
|
+
"delta-polling",
|
|
7571
|
+
"trace-query"
|
|
7152
7572
|
];
|
|
7153
7573
|
}
|
|
7154
7574
|
async createSpan(args) {
|
|
@@ -7263,6 +7683,18 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
7263
7683
|
}, error);
|
|
7264
7684
|
}
|
|
7265
7685
|
}
|
|
7686
|
+
async queryTraces(plan) {
|
|
7687
|
+
try {
|
|
7688
|
+
return await queryTraces(this.#client, plan, this.#traceQueryTimeoutMs);
|
|
7689
|
+
} catch (error) {
|
|
7690
|
+
if (error instanceof MastraError || error instanceof coreStorage.TraceQueryExecutionError) throw error;
|
|
7691
|
+
throw new MastraError({
|
|
7692
|
+
id: createStorageErrorId("CLICKHOUSE", "QUERY_TRACES", "FAILED"),
|
|
7693
|
+
domain: ErrorDomain.STORAGE,
|
|
7694
|
+
category: ErrorCategory.THIRD_PARTY
|
|
7695
|
+
}, error);
|
|
7696
|
+
}
|
|
7697
|
+
}
|
|
7266
7698
|
async listTracesLight(args) {
|
|
7267
7699
|
try {
|
|
7268
7700
|
return await listTracesLight(this.#client, args, this.#deltaCursorStrategy);
|
|
@@ -7679,7 +8111,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
7679
8111
|
}
|
|
7680
8112
|
async batchDeleteTraces(args) {
|
|
7681
8113
|
try {
|
|
7682
|
-
await batchDeleteTraces(this.#client, args);
|
|
8114
|
+
await batchDeleteTraces(this.#client, args, this.#replication);
|
|
7683
8115
|
} catch (error) {
|
|
7684
8116
|
if (error instanceof MastraError) throw error;
|
|
7685
8117
|
throw new MastraError({
|
|
@@ -8539,7 +8971,8 @@ var ClickhouseStoreVNext = class extends ClickhouseStore {
|
|
|
8539
8971
|
this.name = "ClickhouseStoreVNext";
|
|
8540
8972
|
const observability = new ObservabilityStorageClickhouseVNext({
|
|
8541
8973
|
client: this.db,
|
|
8542
|
-
replication: config.replication
|
|
8974
|
+
replication: config.replication,
|
|
8975
|
+
traceQueryTimeoutMs: config.traceQueryTimeoutMs
|
|
8543
8976
|
});
|
|
8544
8977
|
this.stores = {
|
|
8545
8978
|
...this.stores,
|
|
@@ -8548,6 +8981,6 @@ var ClickhouseStoreVNext = class extends ClickhouseStore {
|
|
|
8548
8981
|
}
|
|
8549
8982
|
};
|
|
8550
8983
|
//#endregion
|
|
8551
|
-
export { COLUMN_TYPES, ClickhouseStore, ClickhouseStoreVNext, MemoryStorageClickhouse, ObservabilityStorageClickhouse, ObservabilityStorageClickhouseVNext, ScoresStorageClickhouse, TABLE_ENGINES, WorkflowsStorageClickhouse };
|
|
8984
|
+
export { COLUMN_TYPES, ClickhouseStore, ClickhouseStoreVNext, MemoryStorageClickhouse, ObservabilityStorageClickhouse, ObservabilityStorageClickhouseVNext, ScoresStorageClickhouse, TABLE_DELETION_REQUESTS, TABLE_ENGINES, WorkflowsStorageClickhouse, recordDeletionRequest };
|
|
8552
8985
|
|
|
8553
8986
|
//# sourceMappingURL=index.js.map
|