@mastra/clickhouse 1.26.0-alpha.3 → 1.26.0-alpha.4
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 +40 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +40 -25
- 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/trace-query.d.ts +3 -3
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/package.json +2 -2
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-alpha.
|
|
6
|
+
version: "1.26.0-alpha.4"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## When to use
|
|
@@ -290,7 +290,7 @@ Don't set `replication` on ClickHouse Cloud. Cloud rewrites `MergeTree` to `Shar
|
|
|
290
290
|
|
|
291
291
|
### Observability domain options
|
|
292
292
|
|
|
293
|
-
`ObservabilityStorageClickhouse` and `ObservabilityStorageClickhouseVNext` accept the same connection options as `ClickhouseStore` (`url`, `username`, `password`, or a pre-configured `client`). `ObservabilityStorageClickhouseVNext` also accepts `retention`, the per-signal TTL in days described in [ClickHouse native TTL](https://mastra.ai/reference/storage/retention), and `traceQuery`, the execution limits for [trace queries](https://mastra.ai/reference/observability/tracing/
|
|
293
|
+
`ObservabilityStorageClickhouse` and `ObservabilityStorageClickhouseVNext` accept the same connection options as `ClickhouseStore` (`url`, `username`, `password`, or a pre-configured `client`). `ObservabilityStorageClickhouseVNext` also accepts `retention`, the per-signal TTL in days described in [ClickHouse native TTL](https://mastra.ai/reference/storage/retention), and `traceQuery`, the execution limits for [trace queries](https://mastra.ai/reference/observability/tracing/advanced-queries).
|
|
294
294
|
|
|
295
295
|
## Hosting options
|
|
296
296
|
|
package/dist/index.cjs
CHANGED
|
@@ -6869,7 +6869,9 @@ const TRACE_STATUS_SQL = `if(isNotNull(r.error), 'error', 'success')`;
|
|
|
6869
6869
|
* Core before 1.68 has no field registry at all.
|
|
6870
6870
|
*/
|
|
6871
6871
|
const timeToFirstChunkSupported = "timeToFirstChunkMs" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
|
|
6872
|
-
|
|
6872
|
+
/** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
|
|
6873
|
+
const timeToFirstChunkSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
|
|
6874
|
+
const timeToFirstChunkFeatures = timeToFirstChunkSupported ? timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk", "trace-query-time-to-first-chunk-sort"] : ["trace-query-time-to-first-chunk"] : [];
|
|
6873
6875
|
function durationMsSql(startedAt, endedAt) {
|
|
6874
6876
|
return `dateDiff('millisecond', ${startedAt}, ${endedAt})`;
|
|
6875
6877
|
}
|
|
@@ -7212,7 +7214,7 @@ function fieldDefinition(registry, field) {
|
|
|
7212
7214
|
return definition;
|
|
7213
7215
|
}
|
|
7214
7216
|
function resolveOrderField(field) {
|
|
7215
|
-
if (field === "startedAt" || field === "endedAt") return field;
|
|
7217
|
+
if (field === "startedAt" || field === "endedAt" || field === "timeToFirstChunkMs") return field;
|
|
7216
7218
|
throw new Error(`Unsupported trusted trace-query field: ${field}`);
|
|
7217
7219
|
}
|
|
7218
7220
|
function isMetadataField(field) {
|
|
@@ -7490,9 +7492,11 @@ function traceOrder(plan) {
|
|
|
7490
7492
|
*/
|
|
7491
7493
|
function keysetCursorCondition(plan, order, parameters) {
|
|
7492
7494
|
if (plan.paginationMode !== "keyset" || !plan.cursor) return "";
|
|
7495
|
+
if (plan.cursor.sortValue === null) return `(isNull(sortValue) AND traceId > ${parameters.add(plan.cursor.traceId, "String")})`;
|
|
7496
|
+
const numeric = order.field === "timeToFirstChunkMs";
|
|
7493
7497
|
const comparison = order.direction === "ASC" ? ">" : "<";
|
|
7494
|
-
const sortValue = parameters.add(plan.cursor.sortValue, "DateTime64(3, 'UTC')");
|
|
7495
|
-
return `(sortValue ${comparison} ${sortValue} OR (sortValue = ${sortValue} AND traceId > ${parameters.add(plan.cursor.traceId, "String")}))`;
|
|
7498
|
+
const sortValue = parameters.add(plan.cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
|
|
7499
|
+
return `(sortValue ${comparison} ${sortValue} OR (sortValue = ${sortValue} AND traceId > ${parameters.add(plan.cursor.traceId, "String")})${numeric ? " OR isNull(sortValue)" : ""})`;
|
|
7496
7500
|
}
|
|
7497
7501
|
/**
|
|
7498
7502
|
* First statement of keyset and page mode: the page's traces (after the keyset cursor, `limit + 1`
|
|
@@ -7504,21 +7508,22 @@ function compileClickHouseTraceQueryKeys(plan) {
|
|
|
7504
7508
|
if (plan.paginationMode === "delta") throw new Error("Delta trace queries have no keys statement");
|
|
7505
7509
|
const parameters = new ParameterBuilder();
|
|
7506
7510
|
const order = traceOrder(plan);
|
|
7507
|
-
const ctes = compileClickHouseTraceCandidates(plan, "r.traceId AS traceId, r.dedupeKey AS dedupeKey, r.startedAt AS startedAt, r.endedAt AS endedAt", parameters, QUERY_TRACES_SCOPE);
|
|
7511
|
+
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);
|
|
7508
7512
|
const cursor = keysetCursorCondition(plan, order, parameters);
|
|
7509
7513
|
const pick = order.direction === "DESC" ? "max" : "min";
|
|
7514
|
+
const rootOrder = order.field === "timeToFirstChunkMs" ? `ifNull(${order.field}, ${pick === "max" ? "-inf" : "inf"})` : order.field;
|
|
7510
7515
|
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")}`;
|
|
7511
7516
|
return {
|
|
7512
7517
|
query: `WITH ${ctes.join(",\n")},
|
|
7513
7518
|
per_trace AS (
|
|
7514
7519
|
SELECT traceId, ${pick}(${order.field}) AS sortValue,
|
|
7515
|
-
${pick === "max" ? "argMax" : "argMin"}((dedupeKey, startedAt), ${
|
|
7520
|
+
${pick === "max" ? "argMax" : "argMin"}((dedupeKey, startedAt), ${rootOrder}) AS root
|
|
7516
7521
|
FROM candidates
|
|
7517
7522
|
GROUP BY traceId${cursor ? `\n HAVING ${cursor}` : ""}
|
|
7518
7523
|
)
|
|
7519
7524
|
SELECT traceId, root.1 AS dedupeKey, sortValue, root.2 AS rootStartedAt, count() OVER () AS __total
|
|
7520
7525
|
FROM per_trace
|
|
7521
|
-
ORDER BY sortValue ${order.direction}, traceId ASC
|
|
7526
|
+
ORDER BY sortValue ${order.direction} NULLS LAST, traceId ASC
|
|
7522
7527
|
${limit}`,
|
|
7523
7528
|
query_params: parameters.params,
|
|
7524
7529
|
sharedSnapshot: true
|
|
@@ -7536,16 +7541,16 @@ function compileClickHouseTraceRowsByKey(plan, keys) {
|
|
|
7536
7541
|
const ts = (value) => parameters.add(value, "DateTime64(3, 'UTC')");
|
|
7537
7542
|
const byEnd = order.field === "endedAt";
|
|
7538
7543
|
const tuples = keys.map((key) => {
|
|
7539
|
-
const started = ts(
|
|
7544
|
+
const started = ts(key.rootStartedAt);
|
|
7540
7545
|
const id = `${parameters.add(key.traceId, "String")}, ${parameters.add(key.dedupeKey, "String")}`;
|
|
7541
|
-
return byEnd ? `(${started}, ${id}, ${ts(key.sortValue)})` : `(${started}, ${id})`;
|
|
7546
|
+
return byEnd ? `(${started}, ${id}, ${ts(String(key.sortValue))})` : `(${started}, ${id})`;
|
|
7542
7547
|
});
|
|
7543
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("");
|
|
7544
7549
|
return {
|
|
7545
7550
|
query: `SELECT ${TRACE_SELECT}
|
|
7546
7551
|
FROM ${TABLE_TRACE_ROOTS} r
|
|
7547
7552
|
WHERE (r.startedAt, r.traceId, r.dedupeKey${byEnd ? ", r.endedAt" : ""}) IN (${tuples.join(", ")})${compileTenantScope(plan.scope, parameters)}${predicate}
|
|
7548
|
-
ORDER BY ${order.field} ${order.direction}, traceId ASC
|
|
7553
|
+
ORDER BY ${order.field} ${order.direction} NULLS LAST, traceId ASC
|
|
7549
7554
|
LIMIT 1 BY traceId`,
|
|
7550
7555
|
query_params: parameters.params
|
|
7551
7556
|
};
|
|
@@ -7936,6 +7941,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7936
7941
|
});
|
|
7937
7942
|
}
|
|
7938
7943
|
const keys = await run(compileClickHouseTraceQueryKeys(plan));
|
|
7944
|
+
const sortValue = (value) => plan.orderBy.field !== "timeToFirstChunkMs" ? asIsoTimestamp(value) : value == null ? null : Number(value);
|
|
7939
7945
|
let total = Number(keys[0]?.__total ?? 0);
|
|
7940
7946
|
if (plan.paginationMode === "page" && keys.length === 0 && plan.page > 0) {
|
|
7941
7947
|
const [stats] = await run(compileClickHouseTraceQueryTotal(plan));
|
|
@@ -7945,7 +7951,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7945
7951
|
const rows = visible.length > 0 ? await run(compileClickHouseTraceRowsByKey(plan, visible.map((key) => ({
|
|
7946
7952
|
traceId: String(key.traceId),
|
|
7947
7953
|
dedupeKey: String(key.dedupeKey),
|
|
7948
|
-
sortValue:
|
|
7954
|
+
sortValue: sortValue(key.sortValue),
|
|
7949
7955
|
rootStartedAt: asIsoTimestamp(key.rootStartedAt)
|
|
7950
7956
|
})))) : [];
|
|
7951
7957
|
if (plan.paginationMode === "page") return _mastra_core_storage.traceQueryResponseSchema.parse({
|
|
@@ -7963,7 +7969,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7963
7969
|
traces: await toTraceRecords(rows),
|
|
7964
7970
|
page: { next: keys.length > plan.limit && lastKey ? _mastra_core_storage.encodeTraceQueryCursor(plan, {
|
|
7965
7971
|
result: "traces",
|
|
7966
|
-
sortValue:
|
|
7972
|
+
sortValue: sortValue(lastKey.sortValue),
|
|
7967
7973
|
traceId: String(lastKey.traceId)
|
|
7968
7974
|
}) : null }
|
|
7969
7975
|
});
|
|
@@ -8043,10 +8049,10 @@ const displayFields = [
|
|
|
8043
8049
|
];
|
|
8044
8050
|
var Parameters = class {
|
|
8045
8051
|
values = {};
|
|
8046
|
-
add(value,
|
|
8052
|
+
add(value, type = typeof value === "number" ? "UInt64" : "String") {
|
|
8047
8053
|
const name = `span_${Object.keys(this.values).length}`;
|
|
8048
|
-
this.values[name] =
|
|
8049
|
-
return `{${name}:${
|
|
8054
|
+
this.values[name] = type === "DateTime64(3, 'UTC')" ? new Date(value).toISOString().replace("T", " ").replace(/Z$/, "") : value;
|
|
8055
|
+
return `{${name}:${type}}`;
|
|
8050
8056
|
}
|
|
8051
8057
|
};
|
|
8052
8058
|
function identityValues(row) {
|
|
@@ -8062,10 +8068,17 @@ function identityValues(row) {
|
|
|
8062
8068
|
function afterCursor$1(plan, parameters) {
|
|
8063
8069
|
if (!plan.cursor) return "1";
|
|
8064
8070
|
const cursor = plan.cursor;
|
|
8065
|
-
const timestamp = parameters.add(cursor.sortValue, true);
|
|
8066
8071
|
const field = `s.${plan.orderBy.field}`;
|
|
8067
|
-
const terms = [
|
|
8068
|
-
const equal = [
|
|
8072
|
+
const terms = [];
|
|
8073
|
+
const equal = [];
|
|
8074
|
+
if (cursor.sortValue === null) equal.push(`isNull(${field})`);
|
|
8075
|
+
else {
|
|
8076
|
+
const numeric = plan.orderBy.field === "timeToFirstChunkMs";
|
|
8077
|
+
const value = parameters.add(cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
|
|
8078
|
+
terms.push(`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${value}`);
|
|
8079
|
+
if (numeric) terms.push(`isNull(${field})`);
|
|
8080
|
+
equal.push(`${field} = ${value}`);
|
|
8081
|
+
}
|
|
8069
8082
|
for (const key of identityFields) {
|
|
8070
8083
|
const value = cursor[key];
|
|
8071
8084
|
if (value === null) equal.push(`isNull(s.${key})`);
|
|
@@ -8093,8 +8106,8 @@ function fieldExpression(field) {
|
|
|
8093
8106
|
}
|
|
8094
8107
|
function compileClickHouseSpanQuery(plan) {
|
|
8095
8108
|
const parameters = new Parameters();
|
|
8096
|
-
const from = parameters.add(plan.timeRange.from,
|
|
8097
|
-
const to = parameters.add(plan.timeRange.to,
|
|
8109
|
+
const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
|
|
8110
|
+
const to = parameters.add(plan.timeRange.to, "DateTime64(3, 'UTC')");
|
|
8098
8111
|
const scope = [];
|
|
8099
8112
|
if (plan.scope) {
|
|
8100
8113
|
scope.push(`organizationId = ${parameters.add(plan.scope.organizationId)}`);
|
|
@@ -8107,6 +8120,7 @@ function compileClickHouseSpanQuery(plan) {
|
|
|
8107
8120
|
const selectionFields = [.../* @__PURE__ */ new Set([
|
|
8108
8121
|
"startedAt",
|
|
8109
8122
|
"endedAt",
|
|
8123
|
+
plan.orderBy.field,
|
|
8110
8124
|
...predicateFields$1(plan.where)
|
|
8111
8125
|
])].filter((field) => !new Set(identityFields).has(field));
|
|
8112
8126
|
const expressions = selectionFields.map(fieldExpression);
|
|
@@ -8120,13 +8134,14 @@ function compileClickHouseSpanQuery(plan) {
|
|
|
8120
8134
|
ORDER BY ${identities}, e.endedAt DESC
|
|
8121
8135
|
LIMIT 1 BY ${identities}
|
|
8122
8136
|
)
|
|
8123
|
-
SELECT ${[
|
|
8137
|
+
SELECT ${[.../* @__PURE__ */ new Set([
|
|
8124
8138
|
...identityFields,
|
|
8125
8139
|
"startedAt",
|
|
8126
|
-
"endedAt"
|
|
8127
|
-
|
|
8140
|
+
"endedAt",
|
|
8141
|
+
plan.orderBy.field
|
|
8142
|
+
])].map((field) => `s.${field}`).join(", ")}
|
|
8128
8143
|
FROM candidates s WHERE s.startedAt >= ${from} AND s.startedAt < ${to} AND (${predicate.sql}) AND ${after}
|
|
8129
|
-
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
|
|
8144
|
+
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
|
|
8130
8145
|
LIMIT ${parameters.add(plan.limit + 1)}`,
|
|
8131
8146
|
query_params: {
|
|
8132
8147
|
...parameters.values,
|
|
@@ -8218,7 +8233,7 @@ async function querySpans(client, plan, timeoutMs) {
|
|
|
8218
8233
|
resourceId: last.resourceId,
|
|
8219
8234
|
traceId: last.traceId,
|
|
8220
8235
|
spanId: last.spanId,
|
|
8221
|
-
sortValue: last[plan.orderBy.field]
|
|
8236
|
+
sortValue: last[plan.orderBy.field] ?? null
|
|
8222
8237
|
}) : null;
|
|
8223
8238
|
return result;
|
|
8224
8239
|
}
|