@mastra/clickhouse 1.26.0-alpha.3 → 1.26.0-alpha.5
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 +1 -1
- package/dist/index.cjs +103 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +103 -70
- 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/span-query.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 +44 -18
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -6846,7 +6846,9 @@ const TRACE_STATUS_SQL = `if(isNotNull(r.error), 'error', 'success')`;
|
|
|
6846
6846
|
* Core before 1.68 has no field registry at all.
|
|
6847
6847
|
*/
|
|
6848
6848
|
const timeToFirstChunkSupported = "timeToFirstChunkMs" in (coreStorage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
|
|
6849
|
-
|
|
6849
|
+
/** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
|
|
6850
|
+
const timeToFirstChunkSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
|
|
6851
|
+
const timeToFirstChunkFeatures = timeToFirstChunkSupported ? timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk", "trace-query-time-to-first-chunk-sort"] : ["trace-query-time-to-first-chunk"] : [];
|
|
6850
6852
|
function durationMsSql(startedAt, endedAt) {
|
|
6851
6853
|
return `dateDiff('millisecond', ${startedAt}, ${endedAt})`;
|
|
6852
6854
|
}
|
|
@@ -6899,6 +6901,10 @@ const TRACE_FIELDS = {
|
|
|
6899
6901
|
sql: "r.timeToFirstChunkMs",
|
|
6900
6902
|
parameterType: "Float64"
|
|
6901
6903
|
},
|
|
6904
|
+
entityId: {
|
|
6905
|
+
sql: "r.entityId",
|
|
6906
|
+
parameterType: "String"
|
|
6907
|
+
},
|
|
6902
6908
|
entityName: {
|
|
6903
6909
|
sql: "r.entityName",
|
|
6904
6910
|
parameterType: "String"
|
|
@@ -7189,7 +7195,7 @@ function fieldDefinition(registry, field) {
|
|
|
7189
7195
|
return definition;
|
|
7190
7196
|
}
|
|
7191
7197
|
function resolveOrderField(field) {
|
|
7192
|
-
if (field === "startedAt" || field === "endedAt") return field;
|
|
7198
|
+
if (field === "startedAt" || field === "endedAt" || field === "timeToFirstChunkMs") return field;
|
|
7193
7199
|
throw new Error(`Unsupported trusted trace-query field: ${field}`);
|
|
7194
7200
|
}
|
|
7195
7201
|
function isMetadataField(field) {
|
|
@@ -7297,6 +7303,28 @@ function compileTenantScope(scope, parameters) {
|
|
|
7297
7303
|
if (scope.resourceId !== void 0) sql += `\n AND resourceId = ${parameters.add(scope.resourceId, "String")}`;
|
|
7298
7304
|
return sql;
|
|
7299
7305
|
}
|
|
7306
|
+
/**
|
|
7307
|
+
* Order of the versions of one root span, current version first. `mastra_trace_roots` has no
|
|
7308
|
+
* version column, so until a background merge runs, every export of a root is its own row with the
|
|
7309
|
+
* same sort key, and merges never meet versions that ended on different days (it is partitioned by
|
|
7310
|
+
* `endedAt`). The version that ended last is current, as a resumed or completed root is exported
|
|
7311
|
+
* again with a later `endedAt`; a pending copy stores `endedAt = startedAt` and can lack `threadId`,
|
|
7312
|
+
* so a copy with a thread wins an equal-time tie. Remaining ties are broken by insertion order, last
|
|
7313
|
+
* insert first: the part's first block number, then the row's offset in the part. Tied versions
|
|
7314
|
+
* share a partition and sort key, so a merge meets them and keeps the last insert too, and reads
|
|
7315
|
+
* pick the same version before and after it. Both are virtual columns, so the order reads nothing
|
|
7316
|
+
* beyond `endedAt` and `threadId`.
|
|
7317
|
+
*
|
|
7318
|
+
* Merges don't follow this order across `endedAt`: within one day's partition a merge keeps the last
|
|
7319
|
+
* insert, not the version that ended last. If an outdated export of a root is redelivered after its
|
|
7320
|
+
* current version, on the same day, reads pick the current version until a merge, and the outdated
|
|
7321
|
+
* one afterwards (last write wins, as in the PG and DuckDB stores). Making merges keep the version
|
|
7322
|
+
* that ended last needs `ReplacingMergeTree(endedAt)`, a schema change.
|
|
7323
|
+
*
|
|
7324
|
+
* Every root collapse uses this order: trace rows, aggregates, thread rows and the discovery reads.
|
|
7325
|
+
*/
|
|
7326
|
+
const ROOT_VERSION_ORDER = `endedAt DESC, isNotNull(threadId) DESC, toUInt64(splitByChar('_', _part)[2]) DESC,
|
|
7327
|
+
_part_offset DESC`;
|
|
7300
7328
|
function compileClickHouseTraceScope(selection, relationCollections, parameters, scope, seedConjuncts = [], options = {}) {
|
|
7301
7329
|
const from = parameters.add(selection.timeRange.from, "DateTime64(3, 'UTC')");
|
|
7302
7330
|
const to = parameters.add(selection.timeRange.to, "DateTime64(3, 'UTC')");
|
|
@@ -7320,7 +7348,15 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters,
|
|
|
7320
7348
|
SELECT *
|
|
7321
7349
|
FROM ${TABLE_TRACE_ROOTS} r
|
|
7322
7350
|
WHERE startedAt >= ${from}
|
|
7323
|
-
AND startedAt < ${to}${tenant}${
|
|
7351
|
+
AND startedAt < ${to}${tenant}${bound}${seedFilter ? `
|
|
7352
|
+
AND traceId IN (
|
|
7353
|
+
SELECT traceId
|
|
7354
|
+
FROM ${TABLE_TRACE_ROOTS} r
|
|
7355
|
+
WHERE startedAt >= ${from}
|
|
7356
|
+
AND startedAt < ${to}${tenant}${seedFilter}
|
|
7357
|
+
)` : ""}
|
|
7358
|
+
ORDER BY startedAt, traceId, spanId, ${ROOT_VERSION_ORDER}
|
|
7359
|
+
LIMIT 1 BY traceId
|
|
7324
7360
|
)`] : [`current_roots AS (
|
|
7325
7361
|
SELECT *
|
|
7326
7362
|
FROM ${TABLE_TRACE_ROOTS}
|
|
@@ -7330,7 +7366,7 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters,
|
|
|
7330
7366
|
WHERE startedAt >= ${from}
|
|
7331
7367
|
AND startedAt < ${to}${tenant}${seedFilter}
|
|
7332
7368
|
)${tenant}
|
|
7333
|
-
ORDER BY traceId,
|
|
7369
|
+
ORDER BY traceId, startedAt, spanId, ${ROOT_VERSION_ORDER}
|
|
7334
7370
|
LIMIT 1 BY traceId
|
|
7335
7371
|
)`, `root_scope AS (
|
|
7336
7372
|
SELECT *
|
|
@@ -7460,69 +7496,61 @@ function traceOrder(plan) {
|
|
|
7460
7496
|
direction: plan.orderBy.direction === "asc" ? "ASC" : "DESC"
|
|
7461
7497
|
};
|
|
7462
7498
|
}
|
|
7463
|
-
/**
|
|
7464
|
-
* Keyset cursor on a trace's sort value. Applied after each trace is reduced to one sort value:
|
|
7465
|
-
* filtering root rows first would let a trace with several roots (or unmerged copies with
|
|
7466
|
-
* different `endedAt`) come back on a later page under a root that sorts after the cursor.
|
|
7467
|
-
*/
|
|
7499
|
+
/** Keyset cursor on a trace's sort value (the current root's), with traceId breaking ties. */
|
|
7468
7500
|
function keysetCursorCondition(plan, order, parameters) {
|
|
7469
7501
|
if (plan.paginationMode !== "keyset" || !plan.cursor) return "";
|
|
7502
|
+
if (plan.cursor.sortValue === null) return `(isNull(sortValue) AND traceId > ${parameters.add(plan.cursor.traceId, "String")})`;
|
|
7503
|
+
const numeric = order.field === "timeToFirstChunkMs";
|
|
7470
7504
|
const comparison = order.direction === "ASC" ? ">" : "<";
|
|
7471
|
-
const sortValue = parameters.add(plan.cursor.sortValue, "DateTime64(3, 'UTC')");
|
|
7472
|
-
return `(sortValue ${comparison} ${sortValue} OR (sortValue = ${sortValue} AND traceId > ${parameters.add(plan.cursor.traceId, "String")}))`;
|
|
7505
|
+
const sortValue = parameters.add(plan.cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
|
|
7506
|
+
return `(sortValue ${comparison} ${sortValue} OR (sortValue = ${sortValue} AND traceId > ${parameters.add(plan.cursor.traceId, "String")})${numeric ? " OR isNull(sortValue)" : ""})`;
|
|
7473
7507
|
}
|
|
7474
7508
|
/**
|
|
7475
7509
|
* First statement of keyset and page mode: the page's traces (after the keyset cursor, `limit + 1`
|
|
7476
|
-
* for keyset mode, `perPage` at the page offset for page mode) as
|
|
7477
|
-
* number of matching traces on every row.
|
|
7478
|
-
* value and the key, so the key always names a row with that sort value. No blob columns are read.
|
|
7510
|
+
* for keyset mode, `perPage` at the page offset for page mode) as keys of their current roots, and
|
|
7511
|
+
* the number of matching traces on every row. No blob columns are read.
|
|
7479
7512
|
*/
|
|
7480
7513
|
function compileClickHouseTraceQueryKeys(plan) {
|
|
7481
7514
|
if (plan.paginationMode === "delta") throw new Error("Delta trace queries have no keys statement");
|
|
7482
7515
|
const parameters = new ParameterBuilder();
|
|
7483
7516
|
const order = traceOrder(plan);
|
|
7484
|
-
const ctes = compileClickHouseTraceCandidates(plan, "r.traceId AS traceId, r.dedupeKey AS dedupeKey, r.startedAt AS startedAt, r.endedAt AS endedAt", parameters, QUERY_TRACES_SCOPE);
|
|
7517
|
+
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);
|
|
7485
7518
|
const cursor = keysetCursorCondition(plan, order, parameters);
|
|
7486
|
-
const pick = order.direction === "DESC" ? "max" : "min";
|
|
7487
7519
|
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")}`;
|
|
7488
7520
|
return {
|
|
7489
|
-
query: `WITH ${ctes.join(",\n")}
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
GROUP BY traceId${cursor ? `\n HAVING ${cursor}` : ""}
|
|
7495
|
-
)
|
|
7496
|
-
SELECT traceId, root.1 AS dedupeKey, sortValue, root.2 AS rootStartedAt, count() OVER () AS __total
|
|
7497
|
-
FROM per_trace
|
|
7498
|
-
ORDER BY sortValue ${order.direction}, traceId ASC
|
|
7521
|
+
query: `WITH ${ctes.join(",\n")}
|
|
7522
|
+
SELECT traceId, dedupeKey, ${order.field} AS sortValue, startedAt AS rootStartedAt, endedAt AS rootEndedAt,
|
|
7523
|
+
count() OVER () AS __total
|
|
7524
|
+
FROM candidates${cursor ? `\nWHERE ${cursor}` : ""}
|
|
7525
|
+
ORDER BY sortValue ${order.direction} NULLS LAST, traceId ASC
|
|
7499
7526
|
${limit}`,
|
|
7500
7527
|
query_params: parameters.params,
|
|
7501
7528
|
sharedSnapshot: true
|
|
7502
7529
|
};
|
|
7503
7530
|
}
|
|
7504
7531
|
/**
|
|
7505
|
-
* Second statement of keyset and page mode: the rows of the keyed roots, with `metadata`
|
|
7506
|
-
*
|
|
7507
|
-
*
|
|
7508
|
-
*
|
|
7532
|
+
* Second statement of keyset and page mode: the rows of the keyed roots, with `metadata` and the
|
|
7533
|
+
* stored previews, read by sort key and `endedAt`, so only the keyed version's rows match. Versions
|
|
7534
|
+
* that end at the same instant are ordered as in the keys statement (ROOT_VERSION_ORDER). The
|
|
7535
|
+
* relation-free `where` conjuncts are re-applied, so a version written between the two statements
|
|
7536
|
+
* that doesn't match isn't returned.
|
|
7537
|
+
*
|
|
7538
|
+
* Trade-off of matching on `endedAt`: if a newer version is written between the two statements and
|
|
7539
|
+
* a same-day merge then drops the keyed version, the trace has no row and drops off the page. Without
|
|
7540
|
+
* `endedAt` the lookup would find the replacement, but could also return a version other than the
|
|
7541
|
+
* keyed one whenever both are unmerged. The window is the gap between the two statements.
|
|
7509
7542
|
*/
|
|
7510
7543
|
function compileClickHouseTraceRowsByKey(plan, keys) {
|
|
7511
7544
|
const parameters = new ParameterBuilder();
|
|
7512
7545
|
const order = traceOrder(plan);
|
|
7513
7546
|
const ts = (value) => parameters.add(value, "DateTime64(3, 'UTC')");
|
|
7514
|
-
const
|
|
7515
|
-
const tuples = keys.map((key) => {
|
|
7516
|
-
const started = ts(byEnd ? key.rootStartedAt : key.sortValue);
|
|
7517
|
-
const id = `${parameters.add(key.traceId, "String")}, ${parameters.add(key.dedupeKey, "String")}`;
|
|
7518
|
-
return byEnd ? `(${started}, ${id}, ${ts(key.sortValue)})` : `(${started}, ${id})`;
|
|
7519
|
-
});
|
|
7547
|
+
const tuples = keys.map((key) => `(${ts(key.rootStartedAt)}, ${parameters.add(key.traceId, "String")}, ${parameters.add(key.dedupeKey, "String")}, ${ts(key.rootEndedAt)})`);
|
|
7520
7548
|
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("");
|
|
7521
7549
|
return {
|
|
7522
7550
|
query: `SELECT ${TRACE_SELECT}
|
|
7523
7551
|
FROM ${TABLE_TRACE_ROOTS} r
|
|
7524
|
-
WHERE (r.startedAt, r.traceId, r.dedupeKey
|
|
7525
|
-
ORDER BY ${order.field} ${order.direction}, traceId ASC
|
|
7552
|
+
WHERE (r.startedAt, r.traceId, r.dedupeKey, r.endedAt) IN (${tuples.join(", ")})${compileTenantScope(plan.scope, parameters)}${predicate}
|
|
7553
|
+
ORDER BY ${order.field} ${order.direction} NULLS LAST, traceId ASC, ${ROOT_VERSION_ORDER}
|
|
7526
7554
|
LIMIT 1 BY traceId`,
|
|
7527
7555
|
query_params: parameters.params
|
|
7528
7556
|
};
|
|
@@ -7541,9 +7569,7 @@ FROM candidates`,
|
|
|
7541
7569
|
/**
|
|
7542
7570
|
* The list statement of groups and delta mode (rows without `metadata` and the previews). Keyset
|
|
7543
7571
|
* and page mode list with compileClickHouseTraceQueryKeys and compileClickHouseTraceRowsByKey.
|
|
7544
|
-
*
|
|
7545
|
-
* mode under the row that sorts first in the list order, as in the keys statement; ties go to the
|
|
7546
|
-
* lowest dedupeKey, then the latest endedAt, so every poll picks the same row.
|
|
7572
|
+
* Candidates hold one current root per trace, as in the keys statement.
|
|
7547
7573
|
*/
|
|
7548
7574
|
function compileClickHouseTraceQuery(plan, deltaHead) {
|
|
7549
7575
|
const parameters = new ParameterBuilder();
|
|
@@ -7564,7 +7590,6 @@ LIMIT ${limit}`,
|
|
|
7564
7590
|
};
|
|
7565
7591
|
}
|
|
7566
7592
|
if (plan.paginationMode !== "delta") throw new Error("Keyset and page trace queries use compileClickHouseTraceQueryKeys and compileClickHouseTraceRowsByKey");
|
|
7567
|
-
const order = traceOrder(plan);
|
|
7568
7593
|
const watermark = coreStorage.getTraceQueryDeltaWatermark(plan, "clickhouse");
|
|
7569
7594
|
const after = watermark ? parseDeltaWatermark(watermark) : {
|
|
7570
7595
|
cursorId: "0",
|
|
@@ -7583,8 +7608,7 @@ LIMIT ${limit}`,
|
|
|
7583
7608
|
SELECT c.*, toString(d.latestCursorId) AS __delta_cursor
|
|
7584
7609
|
FROM candidates c
|
|
7585
7610
|
INNER JOIN delta_candidates d ON c.traceId = d.traceId
|
|
7586
|
-
ORDER BY d.latestCursorId ASC, c.traceId ASC
|
|
7587
|
-
LIMIT 1 BY c.traceId
|
|
7611
|
+
ORDER BY d.latestCursorId ASC, c.traceId ASC
|
|
7588
7612
|
LIMIT ${limit}`,
|
|
7589
7613
|
query_params: parameters.params,
|
|
7590
7614
|
sharedSnapshot: true
|
|
@@ -7628,6 +7652,7 @@ function compileClickHouseTraceRootPayloads(keys, scope, columns = "payload") {
|
|
|
7628
7652
|
query: `SELECT traceId, spanId AS rootSpanId, dedupeKey, organizationId, resourceId, ${columns === "payload" ? `metadataRaw AS metadata, ${PREVIEW_COLUMNS.join(", ")}` : "input, output, error"}
|
|
7629
7653
|
FROM ${TABLE_TRACE_ROOTS}
|
|
7630
7654
|
WHERE (startedAt, traceId, dedupeKey, endedAt, ifNull(organizationId, ''), isNull(organizationId), ifNull(resourceId, ''), isNull(resourceId)) IN (${tuples.join(", ")})${compileTenantScope(scope, parameters)}
|
|
7655
|
+
ORDER BY ${ROOT_VERSION_ORDER}
|
|
7631
7656
|
LIMIT 1 BY traceId, dedupeKey, organizationId, resourceId`,
|
|
7632
7657
|
query_params: parameters.params
|
|
7633
7658
|
};
|
|
@@ -7913,6 +7938,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7913
7938
|
});
|
|
7914
7939
|
}
|
|
7915
7940
|
const keys = await run(compileClickHouseTraceQueryKeys(plan));
|
|
7941
|
+
const sortValue = (value) => plan.orderBy.field !== "timeToFirstChunkMs" ? asIsoTimestamp(value) : value == null ? null : Number(value);
|
|
7916
7942
|
let total = Number(keys[0]?.__total ?? 0);
|
|
7917
7943
|
if (plan.paginationMode === "page" && keys.length === 0 && plan.page > 0) {
|
|
7918
7944
|
const [stats] = await run(compileClickHouseTraceQueryTotal(plan));
|
|
@@ -7922,8 +7948,9 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7922
7948
|
const rows = visible.length > 0 ? await run(compileClickHouseTraceRowsByKey(plan, visible.map((key) => ({
|
|
7923
7949
|
traceId: String(key.traceId),
|
|
7924
7950
|
dedupeKey: String(key.dedupeKey),
|
|
7925
|
-
sortValue:
|
|
7926
|
-
rootStartedAt: asIsoTimestamp(key.rootStartedAt)
|
|
7951
|
+
sortValue: sortValue(key.sortValue),
|
|
7952
|
+
rootStartedAt: asIsoTimestamp(key.rootStartedAt),
|
|
7953
|
+
rootEndedAt: asIsoTimestamp(key.rootEndedAt)
|
|
7927
7954
|
})))) : [];
|
|
7928
7955
|
if (plan.paginationMode === "page") return coreStorage.traceQueryResponseSchema.parse({
|
|
7929
7956
|
traces: await toTraceRecords(rows),
|
|
@@ -7940,7 +7967,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7940
7967
|
traces: await toTraceRecords(rows),
|
|
7941
7968
|
page: { next: keys.length > plan.limit && lastKey ? coreStorage.encodeTraceQueryCursor(plan, {
|
|
7942
7969
|
result: "traces",
|
|
7943
|
-
sortValue:
|
|
7970
|
+
sortValue: sortValue(lastKey.sortValue),
|
|
7944
7971
|
traceId: String(lastKey.traceId)
|
|
7945
7972
|
}) : null }
|
|
7946
7973
|
});
|
|
@@ -8020,10 +8047,10 @@ const displayFields = [
|
|
|
8020
8047
|
];
|
|
8021
8048
|
var Parameters = class {
|
|
8022
8049
|
values = {};
|
|
8023
|
-
add(value,
|
|
8050
|
+
add(value, type = typeof value === "number" ? "UInt64" : "String") {
|
|
8024
8051
|
const name = `span_${Object.keys(this.values).length}`;
|
|
8025
|
-
this.values[name] =
|
|
8026
|
-
return `{${name}:${
|
|
8052
|
+
this.values[name] = type === "DateTime64(3, 'UTC')" ? new Date(value).toISOString().replace("T", " ").replace(/Z$/, "") : value;
|
|
8053
|
+
return `{${name}:${type}}`;
|
|
8027
8054
|
}
|
|
8028
8055
|
};
|
|
8029
8056
|
function identityValues(row) {
|
|
@@ -8039,10 +8066,17 @@ function identityValues(row) {
|
|
|
8039
8066
|
function afterCursor$1(plan, parameters) {
|
|
8040
8067
|
if (!plan.cursor) return "1";
|
|
8041
8068
|
const cursor = plan.cursor;
|
|
8042
|
-
const timestamp = parameters.add(cursor.sortValue, true);
|
|
8043
8069
|
const field = `s.${plan.orderBy.field}`;
|
|
8044
|
-
const terms = [
|
|
8045
|
-
const equal = [
|
|
8070
|
+
const terms = [];
|
|
8071
|
+
const equal = [];
|
|
8072
|
+
if (cursor.sortValue === null) equal.push(`isNull(${field})`);
|
|
8073
|
+
else {
|
|
8074
|
+
const numeric = plan.orderBy.field === "timeToFirstChunkMs";
|
|
8075
|
+
const value = parameters.add(cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
|
|
8076
|
+
terms.push(`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${value}`);
|
|
8077
|
+
if (numeric) terms.push(`isNull(${field})`);
|
|
8078
|
+
equal.push(`${field} = ${value}`);
|
|
8079
|
+
}
|
|
8046
8080
|
for (const key of identityFields) {
|
|
8047
8081
|
const value = cursor[key];
|
|
8048
8082
|
if (value === null) equal.push(`isNull(s.${key})`);
|
|
@@ -8070,8 +8104,8 @@ function fieldExpression(field) {
|
|
|
8070
8104
|
}
|
|
8071
8105
|
function compileClickHouseSpanQuery(plan) {
|
|
8072
8106
|
const parameters = new Parameters();
|
|
8073
|
-
const from = parameters.add(plan.timeRange.from,
|
|
8074
|
-
const to = parameters.add(plan.timeRange.to,
|
|
8107
|
+
const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
|
|
8108
|
+
const to = parameters.add(plan.timeRange.to, "DateTime64(3, 'UTC')");
|
|
8075
8109
|
const scope = [];
|
|
8076
8110
|
if (plan.scope) {
|
|
8077
8111
|
scope.push(`organizationId = ${parameters.add(plan.scope.organizationId)}`);
|
|
@@ -8084,6 +8118,7 @@ function compileClickHouseSpanQuery(plan) {
|
|
|
8084
8118
|
const selectionFields = [.../* @__PURE__ */ new Set([
|
|
8085
8119
|
"startedAt",
|
|
8086
8120
|
"endedAt",
|
|
8121
|
+
plan.orderBy.field,
|
|
8087
8122
|
...predicateFields$1(plan.where)
|
|
8088
8123
|
])].filter((field) => !new Set(identityFields).has(field));
|
|
8089
8124
|
const expressions = selectionFields.map(fieldExpression);
|
|
@@ -8097,13 +8132,14 @@ function compileClickHouseSpanQuery(plan) {
|
|
|
8097
8132
|
ORDER BY ${identities}, e.endedAt DESC
|
|
8098
8133
|
LIMIT 1 BY ${identities}
|
|
8099
8134
|
)
|
|
8100
|
-
SELECT ${[
|
|
8135
|
+
SELECT ${[.../* @__PURE__ */ new Set([
|
|
8101
8136
|
...identityFields,
|
|
8102
8137
|
"startedAt",
|
|
8103
|
-
"endedAt"
|
|
8104
|
-
|
|
8138
|
+
"endedAt",
|
|
8139
|
+
plan.orderBy.field
|
|
8140
|
+
])].map((field) => `s.${field}`).join(", ")}
|
|
8105
8141
|
FROM candidates s WHERE s.startedAt >= ${from} AND s.startedAt < ${to} AND (${predicate.sql}) AND ${after}
|
|
8106
|
-
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
|
|
8142
|
+
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
|
|
8107
8143
|
LIMIT ${parameters.add(plan.limit + 1)}`,
|
|
8108
8144
|
query_params: {
|
|
8109
8145
|
...parameters.values,
|
|
@@ -8195,7 +8231,7 @@ async function querySpans(client, plan, timeoutMs) {
|
|
|
8195
8231
|
resourceId: last.resourceId,
|
|
8196
8232
|
traceId: last.traceId,
|
|
8197
8233
|
spanId: last.spanId,
|
|
8198
|
-
sortValue: last[plan.orderBy.field]
|
|
8234
|
+
sortValue: last[plan.orderBy.field] ?? null
|
|
8199
8235
|
}) : null;
|
|
8200
8236
|
return result;
|
|
8201
8237
|
}
|
|
@@ -8323,7 +8359,7 @@ function compileClickHouseThreadRowsQuery(plan, strategy = "full") {
|
|
|
8323
8359
|
const sortedRoots = `roots AS (
|
|
8324
8360
|
SELECT organizationId, traceId, ${rootDataFields.map((field) => `${rootExpression(field)} AS ${field}`).join(", ")}
|
|
8325
8361
|
FROM ${TABLE_TRACE_ROOTS} ${scope.length || strategy === "candidates" ? `WHERE ${[...scope, ...strategy === "candidates" ? [candidateTracesFilter] : []].join(" AND ")}` : ""}
|
|
8326
|
-
ORDER BY organizationId, traceId, startedAt ASC, spanId,
|
|
8362
|
+
ORDER BY organizationId, traceId, startedAt ASC, spanId, ${ROOT_VERSION_ORDER}
|
|
8327
8363
|
LIMIT 1 BY organizationId, traceId
|
|
8328
8364
|
)`;
|
|
8329
8365
|
return {
|
|
@@ -8425,7 +8461,7 @@ async function queryThreadRows(client, plan, timeoutMs, strategy = "auto") {
|
|
|
8425
8461
|
${contextFields.join(", ")}, if(spanType = 'agent_run', timeToFirstChunkMs, NULL) AS timeToFirstChunkMs,
|
|
8426
8462
|
if(isNull(error), 'success', 'error') AS status, ${PREVIEW_COLUMNS.join(", ")}
|
|
8427
8463
|
FROM ${TABLE_TRACE_ROOTS} WHERE ${rootWhere}
|
|
8428
|
-
ORDER BY
|
|
8464
|
+
ORDER BY ${ROOT_VERSION_ORDER} LIMIT 1 BY organizationId, traceId, spanId`,
|
|
8429
8465
|
query_params: { roots: keys(batch) }
|
|
8430
8466
|
}));
|
|
8431
8467
|
const byRoot = new Map(stored.map((row) => [rootKey(row), row]));
|
|
@@ -8433,7 +8469,7 @@ async function queryThreadRows(client, plan, timeoutMs, strategy = "auto") {
|
|
|
8433
8469
|
for (const batch of batches(missing, 32)) {
|
|
8434
8470
|
const payloads = await run({
|
|
8435
8471
|
query: `SELECT organizationId, traceId, spanId, input, output, error FROM ${TABLE_TRACE_ROOTS}
|
|
8436
|
-
WHERE ${rootWhere} ORDER BY
|
|
8472
|
+
WHERE ${rootWhere} ORDER BY ${ROOT_VERSION_ORDER} LIMIT 1 BY organizationId, traceId, spanId`,
|
|
8437
8473
|
query_params: { roots: keys(batch) }
|
|
8438
8474
|
});
|
|
8439
8475
|
for (const payload of payloads) {
|
|
@@ -8502,6 +8538,7 @@ const DIMENSION_SQL = {
|
|
|
8502
8538
|
entityName: "r.entityName",
|
|
8503
8539
|
name: "r.name",
|
|
8504
8540
|
spanType: "r.spanType",
|
|
8541
|
+
entityId: "r.entityId",
|
|
8505
8542
|
environment: "r.environment",
|
|
8506
8543
|
status: TRACE_STATUS_SQL,
|
|
8507
8544
|
serviceName: "r.serviceName",
|
|
@@ -8540,12 +8577,6 @@ const COMPARISON_SQL = {
|
|
|
8540
8577
|
function compileClickHouseTraceAggregate(plan) {
|
|
8541
8578
|
const parameters = new ParameterBuilder();
|
|
8542
8579
|
const ctes = compileClickHouseTraceCandidates(plan, "*", parameters, { oneRootPerTrace: true });
|
|
8543
|
-
ctes.push(`trace_roots AS (
|
|
8544
|
-
SELECT *
|
|
8545
|
-
FROM candidates
|
|
8546
|
-
ORDER BY traceId, dedupeKey
|
|
8547
|
-
LIMIT 1 BY traceId
|
|
8548
|
-
)`);
|
|
8549
8580
|
const dimensionSql = (field) => {
|
|
8550
8581
|
if (field.startsWith("metadata.")) return traceMetadataValueSql(parameters.add(field.slice(9), "String"));
|
|
8551
8582
|
const sql = Object.hasOwn(DIMENSION_SQL, field) ? DIMENSION_SQL[field] : void 0;
|
|
@@ -8579,7 +8610,7 @@ function compileClickHouseTraceAggregate(plan) {
|
|
|
8579
8610
|
}
|
|
8580
8611
|
ctes.push(`facts AS (
|
|
8581
8612
|
SELECT ${factColumns.join(",\n ")}
|
|
8582
|
-
FROM
|
|
8613
|
+
FROM candidates r${usageJoin}
|
|
8583
8614
|
)`);
|
|
8584
8615
|
const mixedUnitsSql = "minOrNull(unitMin) != maxOrNull(unitMax)";
|
|
8585
8616
|
const measureSql = (name) => {
|
|
@@ -10085,6 +10116,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
10085
10116
|
"trace-query-context-ids",
|
|
10086
10117
|
...timeToFirstChunkFeatures,
|
|
10087
10118
|
"trace-query-root-span-fields",
|
|
10119
|
+
"trace-query-root-entity-id",
|
|
10088
10120
|
"trace-aggregate",
|
|
10089
10121
|
...spanQueryFeatures,
|
|
10090
10122
|
...threadQueryFeatures
|
|
@@ -10108,6 +10140,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
10108
10140
|
"trace-query-context-ids",
|
|
10109
10141
|
...timeToFirstChunkFeatures,
|
|
10110
10142
|
"trace-query-root-span-fields",
|
|
10143
|
+
"trace-query-root-entity-id",
|
|
10111
10144
|
"trace-aggregate",
|
|
10112
10145
|
...spanQueryFeatures,
|
|
10113
10146
|
...threadQueryFeatures
|