@mastra/clickhouse 1.26.0-alpha.4 → 1.26.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/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +94 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +93 -69
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/thread-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-aggregate.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts +41 -15
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/docs/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: mastra-clickhouse
|
|
|
3
3
|
description: Documentation for @mastra/clickhouse. Use when working with @mastra/clickhouse APIs, configuration, or implementation.
|
|
4
4
|
metadata:
|
|
5
5
|
package: "@mastra/clickhouse"
|
|
6
|
-
version: "1.26.0
|
|
6
|
+
version: "1.26.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## When to use
|
package/dist/index.cjs
CHANGED
|
@@ -16,7 +16,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
}
|
|
17
17
|
return to;
|
|
18
18
|
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
20
20
|
value: mod,
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
@@ -1610,6 +1610,7 @@ var MemoryStorageClickhouse = class extends _mastra_core_storage.MemoryStorage {
|
|
|
1610
1610
|
metadataIndex++;
|
|
1611
1611
|
}
|
|
1612
1612
|
}
|
|
1613
|
+
const idScope = filter?.resourceId ? `WHERE id IN (SELECT id FROM ${_mastra_core_storage.TABLE_THREADS} WHERE resourceId = {resourceId:String})` : "";
|
|
1613
1614
|
const latestThreads = `
|
|
1614
1615
|
latest_threads AS (
|
|
1615
1616
|
SELECT
|
|
@@ -1620,7 +1621,7 @@ var MemoryStorageClickhouse = class extends _mastra_core_storage.MemoryStorage {
|
|
|
1620
1621
|
toDateTime64(createdAt, 3) as createdAt,
|
|
1621
1622
|
toDateTime64(updatedAt, 3) as updatedAt
|
|
1622
1623
|
FROM ${_mastra_core_storage.TABLE_THREADS}
|
|
1623
|
-
${
|
|
1624
|
+
${idScope}
|
|
1624
1625
|
ORDER BY updatedAt DESC
|
|
1625
1626
|
LIMIT 1 BY id
|
|
1626
1627
|
)`;
|
|
@@ -2441,9 +2442,7 @@ var ObservabilityStorageClickhouse = class extends _mastra_core_storage.Observab
|
|
|
2441
2442
|
case _mastra_core_storage.TraceStatus.RUNNING:
|
|
2442
2443
|
conditions.push(`endedAt IS NULL AND (error IS NULL OR error = '')`);
|
|
2443
2444
|
break;
|
|
2444
|
-
case _mastra_core_storage.TraceStatus.SUCCESS:
|
|
2445
|
-
conditions.push(`endedAt IS NOT NULL AND (error IS NULL OR error = '')`);
|
|
2446
|
-
break;
|
|
2445
|
+
case _mastra_core_storage.TraceStatus.SUCCESS: conditions.push(`endedAt IS NOT NULL AND (error IS NULL OR error = '')`);
|
|
2447
2446
|
}
|
|
2448
2447
|
if (filters.hasChildError !== void 0) {
|
|
2449
2448
|
const finalClause = (TABLE_ENGINES[_mastra_core_storage.TABLE_SPANS] ?? "MergeTree()").startsWith("ReplacingMergeTree") ? "FINAL" : "";
|
|
@@ -5284,7 +5283,8 @@ async function getFeedbackAggregate(client, args) {
|
|
|
5284
5283
|
const aggSql = getAggregationSql$2(args.aggregation);
|
|
5285
5284
|
const identity = buildFeedbackIdentityFilter(args);
|
|
5286
5285
|
const combined = mergeFilters$2(identity, buildFeedbackFilterConditions(args.filters));
|
|
5287
|
-
const
|
|
5286
|
+
const whereClause = toWhereClause$2(combined);
|
|
5287
|
+
const result = await queryJson$2(client, `SELECT ${aggSql} AS value FROM ${TABLE_FEEDBACK_EVENTS} FINAL ${whereClause}`, combined.params);
|
|
5288
5288
|
const value = result[0]?.value == null ? null : Number(result[0]?.value);
|
|
5289
5289
|
if (args.comparePeriod && args.filters?.timestamp) {
|
|
5290
5290
|
const ts = args.filters.timestamp;
|
|
@@ -5318,7 +5318,8 @@ async function getFeedbackAggregate(client, args) {
|
|
|
5318
5318
|
endExclusive: ts.endExclusive
|
|
5319
5319
|
}
|
|
5320
5320
|
}));
|
|
5321
|
-
const
|
|
5321
|
+
const prevWhereClause = toWhereClause$2(prevCombined);
|
|
5322
|
+
const prevResult = await queryJson$2(client, `SELECT ${aggSql} AS value FROM ${TABLE_FEEDBACK_EVENTS} FINAL ${prevWhereClause}`, prevCombined.params);
|
|
5322
5323
|
const previousValue = prevResult[0]?.value == null ? null : Number(prevResult[0]?.value);
|
|
5323
5324
|
let changePercent = null;
|
|
5324
5325
|
if (previousValue !== null && previousValue !== 0 && value !== null) changePercent = (value - previousValue) / Math.abs(previousValue) * 100;
|
|
@@ -6225,8 +6226,10 @@ function buildLegacySpanSelectExprs(legacyColumnTypes) {
|
|
|
6225
6226
|
const promotedIn = PROMOTED_METADATA_KEYS.map((k) => `'${k}'`).join(", ");
|
|
6226
6227
|
const metadataSearchExpr = has("metadata") ? `CAST(arrayFilter(x -> x.1 NOT IN (${promotedIn}) AND trim(BOTH ' ' FROM x.2) != '', arrayMap(x -> (x.1, trim(BOTH ' ' FROM x.2)), JSONExtractKeysAndValues(COALESCE("metadata", '{}'), 'String'))), 'Map(String, String)')` : `CAST(map(), 'Map(String, String)')`;
|
|
6227
6228
|
let tagsExpr = "[]";
|
|
6228
|
-
if (has("tags"))
|
|
6229
|
-
|
|
6229
|
+
if (has("tags")) {
|
|
6230
|
+
if (legacyColumnTypes.get("tags").includes("Array")) tagsExpr = `arrayFilter(x -> x != '', arrayDistinct(arrayMap(x -> trim(BOTH ' ' FROM x), "tags")))`;
|
|
6231
|
+
else tagsExpr = `arrayFilter(x -> x != '', arrayDistinct(arrayMap(x -> trim(BOTH ' ' FROM x), JSONExtract(COALESCE("tags", '[]'), 'Array(String)'))))`;
|
|
6232
|
+
}
|
|
6230
6233
|
const exprs = {
|
|
6231
6234
|
dedupeKey: `concat("traceId", ':', "spanId")`,
|
|
6232
6235
|
traceId: `"traceId"`,
|
|
@@ -6473,10 +6476,11 @@ function toWhereClause(filter) {
|
|
|
6473
6476
|
return filter.conditions.length ? `WHERE ${filter.conditions.join(" AND ")}` : "";
|
|
6474
6477
|
}
|
|
6475
6478
|
function currentScoresRelation(sourcePredicate) {
|
|
6479
|
+
const whereClause = sourcePredicate ? `WHERE ${sourcePredicate}` : "";
|
|
6476
6480
|
return `(
|
|
6477
6481
|
SELECT *
|
|
6478
6482
|
FROM ${TABLE_SCORE_EVENTS_CURRENT} FINAL
|
|
6479
|
-
${
|
|
6483
|
+
${whereClause}
|
|
6480
6484
|
)`;
|
|
6481
6485
|
}
|
|
6482
6486
|
/**
|
|
@@ -6924,6 +6928,10 @@ const TRACE_FIELDS = {
|
|
|
6924
6928
|
sql: "r.timeToFirstChunkMs",
|
|
6925
6929
|
parameterType: "Float64"
|
|
6926
6930
|
},
|
|
6931
|
+
entityId: {
|
|
6932
|
+
sql: "r.entityId",
|
|
6933
|
+
parameterType: "String"
|
|
6934
|
+
},
|
|
6927
6935
|
entityName: {
|
|
6928
6936
|
sql: "r.entityName",
|
|
6929
6937
|
parameterType: "String"
|
|
@@ -7322,6 +7330,28 @@ function compileTenantScope(scope, parameters) {
|
|
|
7322
7330
|
if (scope.resourceId !== void 0) sql += `\n AND resourceId = ${parameters.add(scope.resourceId, "String")}`;
|
|
7323
7331
|
return sql;
|
|
7324
7332
|
}
|
|
7333
|
+
/**
|
|
7334
|
+
* Order of the versions of one root span, current version first. `mastra_trace_roots` has no
|
|
7335
|
+
* version column, so until a background merge runs, every export of a root is its own row with the
|
|
7336
|
+
* same sort key, and merges never meet versions that ended on different days (it is partitioned by
|
|
7337
|
+
* `endedAt`). The version that ended last is current, as a resumed or completed root is exported
|
|
7338
|
+
* again with a later `endedAt`; a pending copy stores `endedAt = startedAt` and can lack `threadId`,
|
|
7339
|
+
* so a copy with a thread wins an equal-time tie. Remaining ties are broken by insertion order, last
|
|
7340
|
+
* insert first: the part's first block number, then the row's offset in the part. Tied versions
|
|
7341
|
+
* share a partition and sort key, so a merge meets them and keeps the last insert too, and reads
|
|
7342
|
+
* pick the same version before and after it. Both are virtual columns, so the order reads nothing
|
|
7343
|
+
* beyond `endedAt` and `threadId`.
|
|
7344
|
+
*
|
|
7345
|
+
* Merges don't follow this order across `endedAt`: within one day's partition a merge keeps the last
|
|
7346
|
+
* insert, not the version that ended last. If an outdated export of a root is redelivered after its
|
|
7347
|
+
* current version, on the same day, reads pick the current version until a merge, and the outdated
|
|
7348
|
+
* one afterwards (last write wins, as in the PG and DuckDB stores). Making merges keep the version
|
|
7349
|
+
* that ended last needs `ReplacingMergeTree(endedAt)`, a schema change.
|
|
7350
|
+
*
|
|
7351
|
+
* Every root collapse uses this order: trace rows, aggregates, thread rows and the discovery reads.
|
|
7352
|
+
*/
|
|
7353
|
+
const ROOT_VERSION_ORDER = `endedAt DESC, isNotNull(threadId) DESC, toUInt64(splitByChar('_', _part)[2]) DESC,
|
|
7354
|
+
_part_offset DESC`;
|
|
7325
7355
|
function compileClickHouseTraceScope(selection, relationCollections, parameters, scope, seedConjuncts = [], options = {}) {
|
|
7326
7356
|
const from = parameters.add(selection.timeRange.from, "DateTime64(3, 'UTC')");
|
|
7327
7357
|
const to = parameters.add(selection.timeRange.to, "DateTime64(3, 'UTC')");
|
|
@@ -7345,7 +7375,15 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters,
|
|
|
7345
7375
|
SELECT *
|
|
7346
7376
|
FROM ${TABLE_TRACE_ROOTS} r
|
|
7347
7377
|
WHERE startedAt >= ${from}
|
|
7348
|
-
AND startedAt < ${to}${tenant}${
|
|
7378
|
+
AND startedAt < ${to}${tenant}${bound}${seedFilter ? `
|
|
7379
|
+
AND traceId IN (
|
|
7380
|
+
SELECT traceId
|
|
7381
|
+
FROM ${TABLE_TRACE_ROOTS} r
|
|
7382
|
+
WHERE startedAt >= ${from}
|
|
7383
|
+
AND startedAt < ${to}${tenant}${seedFilter}
|
|
7384
|
+
)` : ""}
|
|
7385
|
+
ORDER BY startedAt, traceId, spanId, ${ROOT_VERSION_ORDER}
|
|
7386
|
+
LIMIT 1 BY traceId
|
|
7349
7387
|
)`] : [`current_roots AS (
|
|
7350
7388
|
SELECT *
|
|
7351
7389
|
FROM ${TABLE_TRACE_ROOTS}
|
|
@@ -7355,7 +7393,7 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters,
|
|
|
7355
7393
|
WHERE startedAt >= ${from}
|
|
7356
7394
|
AND startedAt < ${to}${tenant}${seedFilter}
|
|
7357
7395
|
)${tenant}
|
|
7358
|
-
ORDER BY traceId,
|
|
7396
|
+
ORDER BY traceId, startedAt, spanId, ${ROOT_VERSION_ORDER}
|
|
7359
7397
|
LIMIT 1 BY traceId
|
|
7360
7398
|
)`, `root_scope AS (
|
|
7361
7399
|
SELECT *
|
|
@@ -7485,11 +7523,7 @@ function traceOrder(plan) {
|
|
|
7485
7523
|
direction: plan.orderBy.direction === "asc" ? "ASC" : "DESC"
|
|
7486
7524
|
};
|
|
7487
7525
|
}
|
|
7488
|
-
/**
|
|
7489
|
-
* Keyset cursor on a trace's sort value. Applied after each trace is reduced to one sort value:
|
|
7490
|
-
* filtering root rows first would let a trace with several roots (or unmerged copies with
|
|
7491
|
-
* different `endedAt`) come back on a later page under a root that sorts after the cursor.
|
|
7492
|
-
*/
|
|
7526
|
+
/** Keyset cursor on a trace's sort value (the current root's), with traceId breaking ties. */
|
|
7493
7527
|
function keysetCursorCondition(plan, order, parameters) {
|
|
7494
7528
|
if (plan.paginationMode !== "keyset" || !plan.cursor) return "";
|
|
7495
7529
|
if (plan.cursor.sortValue === null) return `(isNull(sortValue) AND traceId > ${parameters.add(plan.cursor.traceId, "String")})`;
|
|
@@ -7500,9 +7534,8 @@ function keysetCursorCondition(plan, order, parameters) {
|
|
|
7500
7534
|
}
|
|
7501
7535
|
/**
|
|
7502
7536
|
* First statement of keyset and page mode: the page's traces (after the keyset cursor, `limit + 1`
|
|
7503
|
-
* for keyset mode, `perPage` at the page offset for page mode) as
|
|
7504
|
-
* number of matching traces on every row.
|
|
7505
|
-
* value and the key, so the key always names a row with that sort value. No blob columns are read.
|
|
7537
|
+
* for keyset mode, `perPage` at the page offset for page mode) as keys of their current roots, and
|
|
7538
|
+
* the number of matching traces on every row. No blob columns are read.
|
|
7506
7539
|
*/
|
|
7507
7540
|
function compileClickHouseTraceQueryKeys(plan) {
|
|
7508
7541
|
if (plan.paginationMode === "delta") throw new Error("Delta trace queries have no keys statement");
|
|
@@ -7510,19 +7543,12 @@ function compileClickHouseTraceQueryKeys(plan) {
|
|
|
7510
7543
|
const order = traceOrder(plan);
|
|
7511
7544
|
const ctes = compileClickHouseTraceCandidates(plan, "r.traceId AS traceId, r.dedupeKey AS dedupeKey, r.startedAt AS startedAt, r.endedAt AS endedAt, r.timeToFirstChunkMs AS timeToFirstChunkMs", parameters, QUERY_TRACES_SCOPE);
|
|
7512
7545
|
const cursor = keysetCursorCondition(plan, order, parameters);
|
|
7513
|
-
const pick = order.direction === "DESC" ? "max" : "min";
|
|
7514
|
-
const rootOrder = order.field === "timeToFirstChunkMs" ? `ifNull(${order.field}, ${pick === "max" ? "-inf" : "inf"})` : order.field;
|
|
7515
7546
|
const limit = plan.paginationMode === "page" ? `LIMIT ${parameters.add(plan.perPage, "UInt64")} OFFSET ${parameters.add(plan.page * plan.perPage, "UInt64")}` : `LIMIT ${parameters.add(plan.limit + 1, "UInt64")}`;
|
|
7516
7547
|
return {
|
|
7517
|
-
query: `WITH ${ctes.join(",\n")}
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
FROM candidates
|
|
7522
|
-
GROUP BY traceId${cursor ? `\n HAVING ${cursor}` : ""}
|
|
7523
|
-
)
|
|
7524
|
-
SELECT traceId, root.1 AS dedupeKey, sortValue, root.2 AS rootStartedAt, count() OVER () AS __total
|
|
7525
|
-
FROM per_trace
|
|
7548
|
+
query: `WITH ${ctes.join(",\n")}
|
|
7549
|
+
SELECT traceId, dedupeKey, ${order.field} AS sortValue, startedAt AS rootStartedAt, endedAt AS rootEndedAt,
|
|
7550
|
+
count() OVER () AS __total
|
|
7551
|
+
FROM candidates${cursor ? `\nWHERE ${cursor}` : ""}
|
|
7526
7552
|
ORDER BY sortValue ${order.direction} NULLS LAST, traceId ASC
|
|
7527
7553
|
${limit}`,
|
|
7528
7554
|
query_params: parameters.params,
|
|
@@ -7530,27 +7556,28 @@ ${limit}`,
|
|
|
7530
7556
|
};
|
|
7531
7557
|
}
|
|
7532
7558
|
/**
|
|
7533
|
-
* Second statement of keyset and page mode: the rows of the keyed roots, with `metadata`
|
|
7534
|
-
*
|
|
7535
|
-
*
|
|
7536
|
-
*
|
|
7559
|
+
* Second statement of keyset and page mode: the rows of the keyed roots, with `metadata` and the
|
|
7560
|
+
* stored previews, read by sort key and `endedAt`, so only the keyed version's rows match. Versions
|
|
7561
|
+
* that end at the same instant are ordered as in the keys statement (ROOT_VERSION_ORDER). The
|
|
7562
|
+
* relation-free `where` conjuncts are re-applied, so a version written between the two statements
|
|
7563
|
+
* that doesn't match isn't returned.
|
|
7564
|
+
*
|
|
7565
|
+
* Trade-off of matching on `endedAt`: if a newer version is written between the two statements and
|
|
7566
|
+
* a same-day merge then drops the keyed version, the trace has no row and drops off the page. Without
|
|
7567
|
+
* `endedAt` the lookup would find the replacement, but could also return a version other than the
|
|
7568
|
+
* keyed one whenever both are unmerged. The window is the gap between the two statements.
|
|
7537
7569
|
*/
|
|
7538
7570
|
function compileClickHouseTraceRowsByKey(plan, keys) {
|
|
7539
7571
|
const parameters = new ParameterBuilder();
|
|
7540
7572
|
const order = traceOrder(plan);
|
|
7541
7573
|
const ts = (value) => parameters.add(value, "DateTime64(3, 'UTC')");
|
|
7542
|
-
const
|
|
7543
|
-
const tuples = keys.map((key) => {
|
|
7544
|
-
const started = ts(key.rootStartedAt);
|
|
7545
|
-
const id = `${parameters.add(key.traceId, "String")}, ${parameters.add(key.dedupeKey, "String")}`;
|
|
7546
|
-
return byEnd ? `(${started}, ${id}, ${ts(String(key.sortValue))})` : `(${started}, ${id})`;
|
|
7547
|
-
});
|
|
7574
|
+
const tuples = keys.map((key) => `(${ts(key.rootStartedAt)}, ${parameters.add(key.traceId, "String")}, ${parameters.add(key.dedupeKey, "String")}, ${ts(key.rootEndedAt)})`);
|
|
7548
7575
|
const predicate = (plan.where ? (plan.where.type === "boolean" && plan.where.operator === "and" ? plan.where.args : [plan.where]).filter((conjunct) => collectRelationCollections(conjunct).size === 0) : []).map((conjunct) => `\n AND (${compilePredicate(conjunct, parameters)})`).join("");
|
|
7549
7576
|
return {
|
|
7550
7577
|
query: `SELECT ${TRACE_SELECT}
|
|
7551
7578
|
FROM ${TABLE_TRACE_ROOTS} r
|
|
7552
|
-
WHERE (r.startedAt, r.traceId, r.dedupeKey
|
|
7553
|
-
ORDER BY ${order.field} ${order.direction} NULLS LAST, traceId ASC
|
|
7579
|
+
WHERE (r.startedAt, r.traceId, r.dedupeKey, r.endedAt) IN (${tuples.join(", ")})${compileTenantScope(plan.scope, parameters)}${predicate}
|
|
7580
|
+
ORDER BY ${order.field} ${order.direction} NULLS LAST, traceId ASC, ${ROOT_VERSION_ORDER}
|
|
7554
7581
|
LIMIT 1 BY traceId`,
|
|
7555
7582
|
query_params: parameters.params
|
|
7556
7583
|
};
|
|
@@ -7569,9 +7596,7 @@ FROM candidates`,
|
|
|
7569
7596
|
/**
|
|
7570
7597
|
* The list statement of groups and delta mode (rows without `metadata` and the previews). Keyset
|
|
7571
7598
|
* and page mode list with compileClickHouseTraceQueryKeys and compileClickHouseTraceRowsByKey.
|
|
7572
|
-
*
|
|
7573
|
-
* mode under the row that sorts first in the list order, as in the keys statement; ties go to the
|
|
7574
|
-
* lowest dedupeKey, then the latest endedAt, so every poll picks the same row.
|
|
7599
|
+
* Candidates hold one current root per trace, as in the keys statement.
|
|
7575
7600
|
*/
|
|
7576
7601
|
function compileClickHouseTraceQuery(plan, deltaHead) {
|
|
7577
7602
|
const parameters = new ParameterBuilder();
|
|
@@ -7592,7 +7617,6 @@ LIMIT ${limit}`,
|
|
|
7592
7617
|
};
|
|
7593
7618
|
}
|
|
7594
7619
|
if (plan.paginationMode !== "delta") throw new Error("Keyset and page trace queries use compileClickHouseTraceQueryKeys and compileClickHouseTraceRowsByKey");
|
|
7595
|
-
const order = traceOrder(plan);
|
|
7596
7620
|
const watermark = _mastra_core_storage.getTraceQueryDeltaWatermark(plan, "clickhouse");
|
|
7597
7621
|
const after = watermark ? parseDeltaWatermark(watermark) : {
|
|
7598
7622
|
cursorId: "0",
|
|
@@ -7611,8 +7635,7 @@ LIMIT ${limit}`,
|
|
|
7611
7635
|
SELECT c.*, toString(d.latestCursorId) AS __delta_cursor
|
|
7612
7636
|
FROM candidates c
|
|
7613
7637
|
INNER JOIN delta_candidates d ON c.traceId = d.traceId
|
|
7614
|
-
ORDER BY d.latestCursorId ASC, c.traceId ASC
|
|
7615
|
-
LIMIT 1 BY c.traceId
|
|
7638
|
+
ORDER BY d.latestCursorId ASC, c.traceId ASC
|
|
7616
7639
|
LIMIT ${limit}`,
|
|
7617
7640
|
query_params: parameters.params,
|
|
7618
7641
|
sharedSnapshot: true
|
|
@@ -7656,6 +7679,7 @@ function compileClickHouseTraceRootPayloads(keys, scope, columns = "payload") {
|
|
|
7656
7679
|
query: `SELECT traceId, spanId AS rootSpanId, dedupeKey, organizationId, resourceId, ${columns === "payload" ? `metadataRaw AS metadata, ${PREVIEW_COLUMNS.join(", ")}` : "input, output, error"}
|
|
7657
7680
|
FROM ${TABLE_TRACE_ROOTS}
|
|
7658
7681
|
WHERE (startedAt, traceId, dedupeKey, endedAt, ifNull(organizationId, ''), isNull(organizationId), ifNull(resourceId, ''), isNull(resourceId)) IN (${tuples.join(", ")})${compileTenantScope(scope, parameters)}
|
|
7682
|
+
ORDER BY ${ROOT_VERSION_ORDER}
|
|
7659
7683
|
LIMIT 1 BY traceId, dedupeKey, organizationId, resourceId`,
|
|
7660
7684
|
query_params: parameters.params
|
|
7661
7685
|
};
|
|
@@ -7952,7 +7976,8 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7952
7976
|
traceId: String(key.traceId),
|
|
7953
7977
|
dedupeKey: String(key.dedupeKey),
|
|
7954
7978
|
sortValue: sortValue(key.sortValue),
|
|
7955
|
-
rootStartedAt: asIsoTimestamp(key.rootStartedAt)
|
|
7979
|
+
rootStartedAt: asIsoTimestamp(key.rootStartedAt),
|
|
7980
|
+
rootEndedAt: asIsoTimestamp(key.rootEndedAt)
|
|
7956
7981
|
})))) : [];
|
|
7957
7982
|
if (plan.paginationMode === "page") return _mastra_core_storage.traceQueryResponseSchema.parse({
|
|
7958
7983
|
traces: await toTraceRecords(rows),
|
|
@@ -8160,7 +8185,7 @@ async function querySpans(client, plan, timeoutMs) {
|
|
|
8160
8185
|
if (remaining <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
|
|
8161
8186
|
return await runWithClickHouseTraceQueryTimeout(client, {
|
|
8162
8187
|
timeoutMs: remaining,
|
|
8163
|
-
memoryLimitBytes:
|
|
8188
|
+
memoryLimitBytes: 536870912
|
|
8164
8189
|
}, compiled);
|
|
8165
8190
|
};
|
|
8166
8191
|
const selected = (await run(compileClickHouseSpanQuery(plan))).map((row) => ({
|
|
@@ -8277,14 +8302,14 @@ function afterCursor(plan, parameters) {
|
|
|
8277
8302
|
return `(${terms.join(" OR ")})`;
|
|
8278
8303
|
}
|
|
8279
8304
|
const THREAD_QUERY_EXECUTION_LIMITS = {
|
|
8280
|
-
memoryLimitBytes:
|
|
8305
|
+
memoryLimitBytes: 536870912,
|
|
8281
8306
|
maxThreads: 2,
|
|
8282
8307
|
maxBlockSize: 4096,
|
|
8283
|
-
spillThresholdBytes:
|
|
8308
|
+
spillThresholdBytes: 33554432
|
|
8284
8309
|
};
|
|
8285
8310
|
const MAX_CANDIDATE_THREADS = 16384;
|
|
8286
8311
|
const MAX_CANDIDATE_TRACES = 1e5;
|
|
8287
|
-
const MAX_CANDIDATE_ID_BYTES =
|
|
8312
|
+
const MAX_CANDIDATE_ID_BYTES = 4194304;
|
|
8288
8313
|
const MAX_FULL_HISTORY_ROOTS = 25e4;
|
|
8289
8314
|
function canUseThreadCandidates(probe) {
|
|
8290
8315
|
return Number(probe.threadCount) <= MAX_CANDIDATE_THREADS && Number(probe.traceCount) <= MAX_CANDIDATE_TRACES && Number(probe.threadBytes) + Number(probe.traceBytes) <= MAX_CANDIDATE_ID_BYTES;
|
|
@@ -8361,7 +8386,7 @@ function compileClickHouseThreadRowsQuery(plan, strategy = "full") {
|
|
|
8361
8386
|
const sortedRoots = `roots AS (
|
|
8362
8387
|
SELECT organizationId, traceId, ${rootDataFields.map((field) => `${rootExpression(field)} AS ${field}`).join(", ")}
|
|
8363
8388
|
FROM ${TABLE_TRACE_ROOTS} ${scope.length || strategy === "candidates" ? `WHERE ${[...scope, ...strategy === "candidates" ? [candidateTracesFilter] : []].join(" AND ")}` : ""}
|
|
8364
|
-
ORDER BY organizationId, traceId, startedAt ASC, spanId,
|
|
8389
|
+
ORDER BY organizationId, traceId, startedAt ASC, spanId, ${ROOT_VERSION_ORDER}
|
|
8365
8390
|
LIMIT 1 BY organizationId, traceId
|
|
8366
8391
|
)`;
|
|
8367
8392
|
return {
|
|
@@ -8394,7 +8419,7 @@ function* batches(rows, maxRows = 256) {
|
|
|
8394
8419
|
let bytes = 0;
|
|
8395
8420
|
for (const row of rows) {
|
|
8396
8421
|
const size = Buffer.byteLength(JSON.stringify(row), "utf8") * 2;
|
|
8397
|
-
if (batch.length && (batch.length >= maxRows || bytes + size >
|
|
8422
|
+
if (batch.length && (batch.length >= maxRows || bytes + size > 49152)) {
|
|
8398
8423
|
yield batch;
|
|
8399
8424
|
batch = [];
|
|
8400
8425
|
bytes = 0;
|
|
@@ -8463,7 +8488,7 @@ async function queryThreadRows(client, plan, timeoutMs, strategy = "auto") {
|
|
|
8463
8488
|
${contextFields.join(", ")}, if(spanType = 'agent_run', timeToFirstChunkMs, NULL) AS timeToFirstChunkMs,
|
|
8464
8489
|
if(isNull(error), 'success', 'error') AS status, ${PREVIEW_COLUMNS.join(", ")}
|
|
8465
8490
|
FROM ${TABLE_TRACE_ROOTS} WHERE ${rootWhere}
|
|
8466
|
-
ORDER BY
|
|
8491
|
+
ORDER BY ${ROOT_VERSION_ORDER} LIMIT 1 BY organizationId, traceId, spanId`,
|
|
8467
8492
|
query_params: { roots: keys(batch) }
|
|
8468
8493
|
}));
|
|
8469
8494
|
const byRoot = new Map(stored.map((row) => [rootKey(row), row]));
|
|
@@ -8471,7 +8496,7 @@ async function queryThreadRows(client, plan, timeoutMs, strategy = "auto") {
|
|
|
8471
8496
|
for (const batch of batches(missing, 32)) {
|
|
8472
8497
|
const payloads = await run({
|
|
8473
8498
|
query: `SELECT organizationId, traceId, spanId, input, output, error FROM ${TABLE_TRACE_ROOTS}
|
|
8474
|
-
WHERE ${rootWhere} ORDER BY
|
|
8499
|
+
WHERE ${rootWhere} ORDER BY ${ROOT_VERSION_ORDER} LIMIT 1 BY organizationId, traceId, spanId`,
|
|
8475
8500
|
query_params: { roots: keys(batch) }
|
|
8476
8501
|
});
|
|
8477
8502
|
for (const payload of payloads) {
|
|
@@ -8540,6 +8565,7 @@ const DIMENSION_SQL = {
|
|
|
8540
8565
|
entityName: "r.entityName",
|
|
8541
8566
|
name: "r.name",
|
|
8542
8567
|
spanType: "r.spanType",
|
|
8568
|
+
entityId: "r.entityId",
|
|
8543
8569
|
environment: "r.environment",
|
|
8544
8570
|
status: TRACE_STATUS_SQL,
|
|
8545
8571
|
serviceName: "r.serviceName",
|
|
@@ -8578,12 +8604,6 @@ const COMPARISON_SQL = {
|
|
|
8578
8604
|
function compileClickHouseTraceAggregate(plan) {
|
|
8579
8605
|
const parameters = new ParameterBuilder();
|
|
8580
8606
|
const ctes = compileClickHouseTraceCandidates(plan, "*", parameters, { oneRootPerTrace: true });
|
|
8581
|
-
ctes.push(`trace_roots AS (
|
|
8582
|
-
SELECT *
|
|
8583
|
-
FROM candidates
|
|
8584
|
-
ORDER BY traceId, dedupeKey
|
|
8585
|
-
LIMIT 1 BY traceId
|
|
8586
|
-
)`);
|
|
8587
8607
|
const dimensionSql = (field) => {
|
|
8588
8608
|
if (field.startsWith("metadata.")) return traceMetadataValueSql(parameters.add(field.slice(9), "String"));
|
|
8589
8609
|
const sql = Object.hasOwn(DIMENSION_SQL, field) ? DIMENSION_SQL[field] : void 0;
|
|
@@ -8617,7 +8637,7 @@ function compileClickHouseTraceAggregate(plan) {
|
|
|
8617
8637
|
}
|
|
8618
8638
|
ctes.push(`facts AS (
|
|
8619
8639
|
SELECT ${factColumns.join(",\n ")}
|
|
8620
|
-
FROM
|
|
8640
|
+
FROM candidates r${usageJoin}
|
|
8621
8641
|
)`);
|
|
8622
8642
|
const mixedUnitsSql = "minOrNull(unitMin) != maxOrNull(unitMax)";
|
|
8623
8643
|
const measureSql = (name) => {
|
|
@@ -9969,7 +9989,7 @@ async function detectExistingDeltaCursorStrategy(client) {
|
|
|
9969
9989
|
}
|
|
9970
9990
|
}
|
|
9971
9991
|
const TRACE_QUERY_DISCOVERY_DEFAULT_TIMEOUT_MS = 5e3;
|
|
9972
|
-
const TRACE_QUERY_DISCOVERY_DEFAULT_MEMORY_LIMIT_BYTES =
|
|
9992
|
+
const TRACE_QUERY_DISCOVERY_DEFAULT_MEMORY_LIMIT_BYTES = 268435456;
|
|
9973
9993
|
function resolveTraceQueryDiscoveryMemoryLimitBytes(memoryLimitBytes = TRACE_QUERY_DISCOVERY_DEFAULT_MEMORY_LIMIT_BYTES) {
|
|
9974
9994
|
if (!Number.isSafeInteger(memoryLimitBytes) || memoryLimitBytes <= 0) throw new RangeError("traceQuery.discovery.memoryLimitBytes must be a positive safe integer");
|
|
9975
9995
|
return memoryLimitBytes;
|
|
@@ -10123,6 +10143,7 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
|
|
|
10123
10143
|
"trace-query-context-ids",
|
|
10124
10144
|
...timeToFirstChunkFeatures,
|
|
10125
10145
|
"trace-query-root-span-fields",
|
|
10146
|
+
"trace-query-root-entity-id",
|
|
10126
10147
|
"trace-aggregate",
|
|
10127
10148
|
...spanQueryFeatures,
|
|
10128
10149
|
...threadQueryFeatures
|
|
@@ -10146,6 +10167,7 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
|
|
|
10146
10167
|
"trace-query-context-ids",
|
|
10147
10168
|
...timeToFirstChunkFeatures,
|
|
10148
10169
|
"trace-query-root-span-fields",
|
|
10170
|
+
"trace-query-root-entity-id",
|
|
10149
10171
|
"trace-aggregate",
|
|
10150
10172
|
...spanQueryFeatures,
|
|
10151
10173
|
...threadQueryFeatures
|
|
@@ -11360,10 +11382,12 @@ var WorkflowsStorageClickhouse = class extends _mastra_core_storage.WorkflowsSto
|
|
|
11360
11382
|
conditions.push(`JSONExtractString(snapshot, 'status') = {var_status:String}`);
|
|
11361
11383
|
values.var_status = status;
|
|
11362
11384
|
}
|
|
11363
|
-
if (resourceId)
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11385
|
+
if (resourceId) {
|
|
11386
|
+
if (await this.#db.hasColumn(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId")) {
|
|
11387
|
+
conditions.push(`resourceId = {var_resourceId:String}`);
|
|
11388
|
+
values.var_resourceId = resourceId;
|
|
11389
|
+
} else this.logger.warn(`[${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
11390
|
+
}
|
|
11367
11391
|
if (fromDate) {
|
|
11368
11392
|
conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
|
|
11369
11393
|
values.var_from_date = fromDate.getTime() / 1e3;
|