@mastra/clickhouse 1.26.0 → 1.27.0-alpha.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 +234 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +234 -32
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +3 -2
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/span-query.d.ts +34 -1
- package/dist/storage/domains/observability/v-next/span-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-aggregate.d.ts +7 -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 +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -3648,6 +3648,9 @@ const ALL_MIGRATIONS = [
|
|
|
3648
3648
|
addColumn(TABLE_SPAN_EVENTS, "inputPreview", "Nullable(String)"),
|
|
3649
3649
|
addColumn(TABLE_SPAN_EVENTS, "outputPreview", "Nullable(String)"),
|
|
3650
3650
|
addColumn(TABLE_SPAN_EVENTS, "errorPreview", "Nullable(String)"),
|
|
3651
|
+
addColumn(TABLE_TRACE_BRANCHES, "entityVersionId", "Nullable(String)"),
|
|
3652
|
+
addColumn(TABLE_TRACE_BRANCHES, "parentEntityVersionId", "Nullable(String)"),
|
|
3653
|
+
addColumn(TABLE_TRACE_BRANCHES, "rootEntityVersionId", "Nullable(String)"),
|
|
3651
3654
|
addColumn(TABLE_SPAN_EVENTS, "entityVersionId", "Nullable(String)"),
|
|
3652
3655
|
addColumn(TABLE_SPAN_EVENTS, "parentEntityVersionId", "Nullable(String)"),
|
|
3653
3656
|
addColumn(TABLE_SPAN_EVENTS, "rootEntityVersionId", "Nullable(String)"),
|
|
@@ -7989,8 +7992,7 @@ async function queryThreads(client, plan, timeoutMs) {
|
|
|
7989
7992
|
});
|
|
7990
7993
|
}
|
|
7991
7994
|
/** Compile a direct span-query filter using its dedicated field mapping. */
|
|
7992
|
-
function compileSpanQueryPredicate(predicate) {
|
|
7993
|
-
const parameters = new ParameterBuilder();
|
|
7995
|
+
function compileSpanQueryPredicate(predicate, parameters = new ParameterBuilder()) {
|
|
7994
7996
|
return {
|
|
7995
7997
|
sql: compileScalarPredicate(predicate, SPAN_QUERY_FIELDS, parameters),
|
|
7996
7998
|
params: parameters.params
|
|
@@ -8151,6 +8153,131 @@ function compileClickHouseSpanQuery(plan) {
|
|
|
8151
8153
|
}
|
|
8152
8154
|
};
|
|
8153
8155
|
}
|
|
8156
|
+
const branchSpanTypes = new Set(BRANCH_SPAN_TYPE_VALUES);
|
|
8157
|
+
const sameColumn = (field) => [field];
|
|
8158
|
+
/**
|
|
8159
|
+
* Span fields `trace_branches` can serve, as the `trace_branches` columns each one reads. The two
|
|
8160
|
+
* tables are expected to diverge, so this list is explicit: a selection whose fields are not all
|
|
8161
|
+
* listed reads `span_events`. A new span-query field has to be classified here deliberately, and a
|
|
8162
|
+
* missing column then costs speed, not correctness.
|
|
8163
|
+
*/
|
|
8164
|
+
const TRACE_BRANCHES_SPAN_FIELD_COLUMNS = {
|
|
8165
|
+
...Object.fromEntries([
|
|
8166
|
+
"organizationId",
|
|
8167
|
+
"resourceId",
|
|
8168
|
+
"traceId",
|
|
8169
|
+
"spanId",
|
|
8170
|
+
"parentSpanId",
|
|
8171
|
+
"name",
|
|
8172
|
+
"spanType",
|
|
8173
|
+
"startedAt",
|
|
8174
|
+
"endedAt",
|
|
8175
|
+
"timeToFirstChunkMs",
|
|
8176
|
+
"threadId",
|
|
8177
|
+
"runId",
|
|
8178
|
+
"sessionId",
|
|
8179
|
+
"userId",
|
|
8180
|
+
"requestId",
|
|
8181
|
+
"serviceName",
|
|
8182
|
+
"experimentId",
|
|
8183
|
+
"environment",
|
|
8184
|
+
"executionSource",
|
|
8185
|
+
"entityType",
|
|
8186
|
+
"entityId",
|
|
8187
|
+
"entityName",
|
|
8188
|
+
"entityVersionId",
|
|
8189
|
+
"parentEntityType",
|
|
8190
|
+
"parentEntityId",
|
|
8191
|
+
"parentEntityName",
|
|
8192
|
+
"parentEntityVersionId",
|
|
8193
|
+
"rootEntityType",
|
|
8194
|
+
"rootEntityId",
|
|
8195
|
+
"rootEntityName",
|
|
8196
|
+
"rootEntityVersionId"
|
|
8197
|
+
].map((field) => [field, sameColumn(field)])),
|
|
8198
|
+
status: ["error"],
|
|
8199
|
+
error: ["error"],
|
|
8200
|
+
durationMs: ["startedAt", "endedAt"]
|
|
8201
|
+
};
|
|
8202
|
+
/**
|
|
8203
|
+
* Whether a span selection reads `trace_branches`: `where` must restrict `spanType` to branch
|
|
8204
|
+
* types, and `trace_branches` must serve every selected field (`served`, for tests).
|
|
8205
|
+
*/
|
|
8206
|
+
function readsTraceBranches(where, fields, served = TRACE_BRANCHES_SPAN_FIELD_COLUMNS) {
|
|
8207
|
+
return positiveSpanConjuncts(where).some(({ field, values }) => field === "spanType" && values.length > 0 && values.every((value) => branchSpanTypes.has(String(value)))) && [...fields].every((field) => Object.hasOwn(served, field));
|
|
8208
|
+
}
|
|
8209
|
+
/**
|
|
8210
|
+
* Top-level `spanType` / `name` equality or `in` conditions. Every span the predicate can match
|
|
8211
|
+
* satisfies them, and copies of a span never differ in either field, so they can be applied
|
|
8212
|
+
* before collapsing copies.
|
|
8213
|
+
*/
|
|
8214
|
+
function positiveSpanConjuncts(where) {
|
|
8215
|
+
return (where?.type === "boolean" && where.operator === "and" ? where.args : where ? [where] : []).flatMap((conjunct) => {
|
|
8216
|
+
if (conjunct.type !== "comparison" && conjunct.type !== "membership") return [];
|
|
8217
|
+
if (conjunct.field !== "spanType" && conjunct.field !== "name") return [];
|
|
8218
|
+
if (conjunct.type === "comparison" && conjunct.operator === "eq") return [{
|
|
8219
|
+
field: conjunct.field,
|
|
8220
|
+
values: [conjunct.value]
|
|
8221
|
+
}];
|
|
8222
|
+
if (conjunct.type === "membership" && conjunct.operator === "in") return [{
|
|
8223
|
+
field: conjunct.field,
|
|
8224
|
+
values: conjunct.values
|
|
8225
|
+
}];
|
|
8226
|
+
return [];
|
|
8227
|
+
});
|
|
8228
|
+
}
|
|
8229
|
+
/**
|
|
8230
|
+
* The spans `querySpans()` selects, as CTEs ending in `selected` (alias each row as you like):
|
|
8231
|
+
* one row per current completed span with `startedAt` in the window, matching `where`, carrying
|
|
8232
|
+
* the identity, `startedAt`, `endedAt`, `status`, and `fields`.
|
|
8233
|
+
*
|
|
8234
|
+
* Copies of a span collapse to the copy with the latest `endedAt`. When `where` requires branch
|
|
8235
|
+
* span types (agent and workflow runs, tool calls, …) and `trace_branches` serves every selected
|
|
8236
|
+
* field, the spans are read from `trace_branches`, whose sort key starts with `spanType`, instead of
|
|
8237
|
+
* every span in the window. That path can differ from `querySpans()` in two ways:
|
|
8238
|
+
* - `trace_branches` is filled only by its materialized view, with no backfill, so an install that
|
|
8239
|
+
* upgraded from @mastra/clickhouse 1.4–1.6 has no branch rows for spans written before then.
|
|
8240
|
+
* - It is a `ReplacingMergeTree` without a version column, so once a background merge collapses a
|
|
8241
|
+
* retried span's copies it keeps the last-inserted copy, not the one with the latest `endedAt`.
|
|
8242
|
+
* Counts are unaffected; `status` and duration of an out-of-order retry can differ.
|
|
8243
|
+
*
|
|
8244
|
+
* Like `querySpans()`, the read has no upper bound on `endedAt` (`PREWHERE endedAt >= from` only,
|
|
8245
|
+
* since a span that starts in the window can end any time later), so a historical window reads
|
|
8246
|
+
* every span that ended since `from`.
|
|
8247
|
+
*/
|
|
8248
|
+
function compileClickHouseSpanSelection(plan, fields, parameters) {
|
|
8249
|
+
const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
|
|
8250
|
+
const to = parameters.add(plan.timeRange.to, "DateTime64(3, 'UTC')");
|
|
8251
|
+
const prefilter = positiveSpanConjuncts(plan.where).map(({ field, values }) => values.length ? `\n AND e.${field} IN (${values.map((value) => parameters.add(String(value), "String")).join(", ")})` : "\n AND 0").join("");
|
|
8252
|
+
const predicate = plan.where ? compileSpanQueryPredicate(plan.where, parameters).sql : "1";
|
|
8253
|
+
let tenant = "";
|
|
8254
|
+
if (plan.scope) {
|
|
8255
|
+
tenant += `\n AND e.organizationId = ${parameters.add(plan.scope.organizationId, "String")}`;
|
|
8256
|
+
if (plan.scope.resourceId !== void 0) tenant += `\n AND e.resourceId = ${parameters.add(plan.scope.resourceId, "String")}`;
|
|
8257
|
+
}
|
|
8258
|
+
const selectionFields = [.../* @__PURE__ */ new Set([
|
|
8259
|
+
"startedAt",
|
|
8260
|
+
"endedAt",
|
|
8261
|
+
"status",
|
|
8262
|
+
...fields,
|
|
8263
|
+
...predicateFields$1(plan.where)
|
|
8264
|
+
])].filter((field) => !new Set(identityFields).has(field));
|
|
8265
|
+
const branches = readsTraceBranches(plan.where, selectionFields);
|
|
8266
|
+
const scan = `FROM ${branches ? TABLE_TRACE_BRANCHES : TABLE_SPAN_EVENTS} e
|
|
8267
|
+
PREWHERE e.endedAt >= ${from}
|
|
8268
|
+
WHERE e.startedAt >= ${from} AND e.startedAt < ${to}${tenant}${prefilter}`;
|
|
8269
|
+
return [`span_candidates AS (
|
|
8270
|
+
${branches ? `SELECT ${identityFields.map((field) => `e.${field} AS ${field}`).join(", ")}, ${selectionFields.map((field) => field === "endedAt" ? "max(e.endedAt) AS endedAt" : `argMax(tuple(${fieldExpression(field)}), e.endedAt).1 AS ${field}`).join(", ")}
|
|
8271
|
+
${scan}
|
|
8272
|
+
GROUP BY ${identityFields.map((field) => `e.${field}`).join(", ")}` : `SELECT ${identities}, ${selectionFields.map((field) => `${fieldExpression(field)} AS ${field}`).join(", ")}
|
|
8273
|
+
${scan}
|
|
8274
|
+
ORDER BY ${identities}, e.endedAt DESC
|
|
8275
|
+
LIMIT 1 BY ${identities}`}
|
|
8276
|
+
)`, `selected AS (
|
|
8277
|
+
SELECT * FROM span_candidates s
|
|
8278
|
+
WHERE s.startedAt >= ${from} AND s.startedAt < ${to} AND (${predicate})
|
|
8279
|
+
)`];
|
|
8280
|
+
}
|
|
8154
8281
|
const LEGACY_PAYLOAD_BATCH_ROWS = 32;
|
|
8155
8282
|
function iso$1(value) {
|
|
8156
8283
|
return new Date(value.includes("T") ? value : `${value.replace(" ", "T")}Z`).toISOString();
|
|
@@ -8554,6 +8681,28 @@ const DIMENSION_SQL = {
|
|
|
8554
8681
|
organizationId: "r.organizationId",
|
|
8555
8682
|
experimentId: "r.experimentId"
|
|
8556
8683
|
};
|
|
8684
|
+
/** Span column expressions (alias `r`, a `selected` span row) for each groupable span dimension. */
|
|
8685
|
+
const SPAN_DIMENSION_SQL = Object.fromEntries([
|
|
8686
|
+
"name",
|
|
8687
|
+
"spanType",
|
|
8688
|
+
"entityType",
|
|
8689
|
+
"entityId",
|
|
8690
|
+
"entityName",
|
|
8691
|
+
"parentEntityType",
|
|
8692
|
+
"parentEntityName",
|
|
8693
|
+
"rootEntityType",
|
|
8694
|
+
"rootEntityName",
|
|
8695
|
+
"environment",
|
|
8696
|
+
"status",
|
|
8697
|
+
"serviceName",
|
|
8698
|
+
"executionSource",
|
|
8699
|
+
"threadId",
|
|
8700
|
+
"resourceId",
|
|
8701
|
+
"userId",
|
|
8702
|
+
"sessionId",
|
|
8703
|
+
"organizationId",
|
|
8704
|
+
"experimentId"
|
|
8705
|
+
].map((field) => [field, `r.${field}`]));
|
|
8557
8706
|
const PERCENTILES = {
|
|
8558
8707
|
"duration.p50": .5,
|
|
8559
8708
|
"duration.p90": .9,
|
|
@@ -8580,13 +8729,41 @@ const COMPARISON_SQL = {
|
|
|
8580
8729
|
*/
|
|
8581
8730
|
function compileClickHouseTraceAggregate(plan) {
|
|
8582
8731
|
const parameters = new ParameterBuilder();
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8732
|
+
return compileClickHouseAggregate(plan, parameters, {
|
|
8733
|
+
ctes: compileClickHouseTraceCandidates(plan, "*", parameters, { oneRootPerTrace: true }),
|
|
8734
|
+
from: "candidates",
|
|
8735
|
+
dimensionSql: (field) => {
|
|
8736
|
+
if (field.startsWith("metadata.")) return traceMetadataValueSql(parameters.add(field.slice(9), "String"));
|
|
8737
|
+
return lookupDimensionSql(DIMENSION_SQL, field);
|
|
8738
|
+
},
|
|
8739
|
+
isErrorSql: "isNotNull(r.error)",
|
|
8740
|
+
usageKey: ["traceId"],
|
|
8741
|
+
usageBound: "traceId IN (SELECT traceId FROM candidates)"
|
|
8742
|
+
});
|
|
8743
|
+
}
|
|
8744
|
+
/**
|
|
8745
|
+
* Compiles a trusted span aggregate plan over the spans `querySpans()` selects. Usage is
|
|
8746
|
+
* attributed by `(traceId, spanId)`, so only the span that recorded a metric row carries it.
|
|
8747
|
+
*/
|
|
8748
|
+
function compileClickHouseSpanAggregate(plan) {
|
|
8749
|
+
const parameters = new ParameterBuilder();
|
|
8750
|
+
return compileClickHouseAggregate(plan, parameters, {
|
|
8751
|
+
ctes: compileClickHouseSpanSelection(plan, [...plan.dimensions, ...plan.measures.flatMap((measure) => measure.type === "countDistinct" && measure.field !== "traceId" ? [measure.field] : [])], parameters),
|
|
8752
|
+
from: "selected",
|
|
8753
|
+
dimensionSql: (field) => lookupDimensionSql(SPAN_DIMENSION_SQL, field),
|
|
8754
|
+
isErrorSql: "r.status = 'error'",
|
|
8755
|
+
usageKey: ["traceId", "spanId"],
|
|
8756
|
+
usageBound: "isNotNull(spanId)"
|
|
8757
|
+
});
|
|
8758
|
+
}
|
|
8759
|
+
function lookupDimensionSql(columns, field) {
|
|
8760
|
+
const sql = Object.hasOwn(columns, field) ? columns[field] : void 0;
|
|
8761
|
+
if (sql === void 0) throw new Error(`Unsupported trusted aggregate field: ${field}`);
|
|
8762
|
+
return sql;
|
|
8763
|
+
}
|
|
8764
|
+
function compileClickHouseAggregate(plan, parameters, source) {
|
|
8765
|
+
const { ctes } = source;
|
|
8766
|
+
const dimensionSql = source.dimensionSql;
|
|
8590
8767
|
const distinctSql = (field) => field === "traceId" ? "r.traceId" : dimensionSql(field);
|
|
8591
8768
|
const dimensionColumns = plan.dimensions.map((_, index) => `d${index}`);
|
|
8592
8769
|
const distinctColumns = /* @__PURE__ */ new Map();
|
|
@@ -8597,7 +8774,7 @@ function compileClickHouseTraceAggregate(plan) {
|
|
|
8597
8774
|
distinctColumns.set(measure.field, column);
|
|
8598
8775
|
factColumns.push(`CAST(${distinctSql(measure.field)} AS Nullable(String)) AS ${column}`);
|
|
8599
8776
|
}
|
|
8600
|
-
factColumns.push(`toFloat64(${durationMsSql("r.startedAt", "r.endedAt")}) AS durationMs`,
|
|
8777
|
+
factColumns.push(`toFloat64(${durationMsSql("r.startedAt", "r.endedAt")}) AS durationMs`, `${source.isErrorSql} AS isError`, `cityHash64(${source.usageKey.map((column) => `r.${column}`).join(", ")}) AS traceSeed`);
|
|
8601
8778
|
if (plan.interval !== void 0) {
|
|
8602
8779
|
const intervalMs = coreStorage.TRACE_AGGREGATE_INTERVAL_MS[plan.interval];
|
|
8603
8780
|
if (intervalMs === void 0) throw new Error(`Unsupported trusted trace-aggregate interval: ${plan.interval}`);
|
|
@@ -8607,14 +8784,14 @@ function compileClickHouseTraceAggregate(plan) {
|
|
|
8607
8784
|
const hasCost = measureKinds.has("cost");
|
|
8608
8785
|
let usageJoin = "";
|
|
8609
8786
|
if (hasCost || measureKinds.has("tokens")) {
|
|
8610
|
-
ctes.push(compileUsageCte(plan, parameters));
|
|
8611
|
-
usageJoin =
|
|
8787
|
+
ctes.push(compileUsageCte(plan, parameters, source));
|
|
8788
|
+
usageJoin = `\n LEFT JOIN usage u ON ${source.usageKey.map((column) => `u.${column} = r.${column}`).join(" AND ")}`;
|
|
8612
8789
|
USAGE_METRIC_NAMES.forEach((_, index) => factColumns.push(`if(u.hasUsage = 1, u.t${index}, NULL) AS t${index}`));
|
|
8613
8790
|
factColumns.push(`u.hasUsage = 1 AS usageBearing`, `if(u.hasUsage = 1 AND u.priced = 1, u.cost, NULL) AS traceCost`, `u.hasUsage = 1 AND u.priced = 1 AND u.pricingFailure = 0 AS covered`, `if(u.hasUsage = 1 AND u.priced = 1, u.unitMin, NULL) AS unitMin`, `if(u.hasUsage = 1 AND u.priced = 1, u.unitMax, NULL) AS unitMax`);
|
|
8614
8791
|
}
|
|
8615
8792
|
ctes.push(`facts AS (
|
|
8616
8793
|
SELECT ${factColumns.join(",\n ")}
|
|
8617
|
-
FROM
|
|
8794
|
+
FROM ${source.from} r${usageJoin}
|
|
8618
8795
|
)`);
|
|
8619
8796
|
const mixedUnitsSql = "minOrNull(unitMin) != maxOrNull(unitMax)";
|
|
8620
8797
|
const measureSql = (name) => {
|
|
@@ -8729,26 +8906,28 @@ function usageMetricIndex(name) {
|
|
|
8729
8906
|
return index;
|
|
8730
8907
|
}
|
|
8731
8908
|
/**
|
|
8732
|
-
* Builds `usage`: one row per
|
|
8733
|
-
* per-name token sums (`t<i>`, indexed by `TRACE_AGGREGATE_USAGE_METRIC_NAMES`) and
|
|
8734
|
-
* pricing state. Metric rows are matched by
|
|
8735
|
-
* `timestamp >= from` only: a trace that starts in the window can emit
|
|
8736
|
-
* the
|
|
8909
|
+
* Builds `usage`: one row per usage key (a trace, or a span) that has at least one token metric
|
|
8910
|
+
* row, holding per-name token sums (`t<i>`, indexed by `TRACE_AGGREGATE_USAGE_METRIC_NAMES`) and
|
|
8911
|
+
* its pricing state. Metric rows are matched by the source's usage bound, usage metric name,
|
|
8912
|
+
* tenant scope, and `timestamp >= from` only: a trace or span that starts in the window can emit
|
|
8913
|
+
* metrics after `to`. For spans that makes the read cost grow with the age of `from` (see
|
|
8914
|
+
* `usageBound`).
|
|
8737
8915
|
*
|
|
8738
8916
|
* Rows are deduplicated with `LIMIT 1 BY metricId` at query time. `ReplacingMergeTree` only
|
|
8739
8917
|
* collapses rows with equal sort keys `(name, timestamp, metricId)`, so retried copies with
|
|
8740
8918
|
* different timestamps never merge, and copies with equal keys stay separate until a background
|
|
8741
8919
|
* merge runs.
|
|
8742
8920
|
*/
|
|
8743
|
-
function compileUsageCte(plan, parameters) {
|
|
8921
|
+
function compileUsageCte(plan, parameters, source) {
|
|
8922
|
+
const key = source.usageKey.join(", ");
|
|
8744
8923
|
const names = USAGE_METRIC_NAMES.map((name) => parameters.add(name, "String"));
|
|
8745
8924
|
const costRow = `name IN (${coreStorage.TRACE_AGGREGATE_COST_METRIC_NAMES.map((name) => names[usageMetricIndex(name)]).join(", ")})`;
|
|
8746
8925
|
const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
|
|
8747
8926
|
const tenant = compileTenantScope(plan.scope, parameters);
|
|
8748
|
-
const priced = `${costRow} AND isNotNull(estimatedCost) AND isNotNull(costUnit) AND NOT hasError`;
|
|
8927
|
+
const priced = `${costRow} AND isNotNull(estimatedCost) AND isNotNull(costUnit) AND (NOT hasError OR partialCost)`;
|
|
8749
8928
|
const failed = `${costRow} AND (hasError OR (isNotNull(estimatedCost) AND isNull(costUnit)))`;
|
|
8750
8929
|
return `usage AS (
|
|
8751
|
-
SELECT
|
|
8930
|
+
SELECT ${key},
|
|
8752
8931
|
toUInt8(1) AS hasUsage,
|
|
8753
8932
|
${names.map((name, index) => `sumIf(value, name = ${name}) AS t${index}`).join(",\n ")},
|
|
8754
8933
|
sumIf(assumeNotNull(estimatedCost), ${priced}) AS cost,
|
|
@@ -8757,34 +8936,42 @@ function compileUsageCte(plan, parameters) {
|
|
|
8757
8936
|
minIf(assumeNotNull(costUnit), ${priced}) AS unitMin,
|
|
8758
8937
|
maxIf(assumeNotNull(costUnit), ${priced}) AS unitMax
|
|
8759
8938
|
FROM (
|
|
8760
|
-
SELECT
|
|
8939
|
+
SELECT ${key},
|
|
8761
8940
|
latest.1 AS name,
|
|
8762
8941
|
latest.2 AS value,
|
|
8763
8942
|
latest.3 AS estimatedCost,
|
|
8764
8943
|
latest.4 AS costUnit,
|
|
8765
|
-
latest.5 AS hasError
|
|
8944
|
+
latest.5 AS hasError,
|
|
8945
|
+
latest.6 AS partialCost
|
|
8766
8946
|
FROM (
|
|
8767
|
-
SELECT
|
|
8768
|
-
argMax(tuple(name, value, estimatedCost, costUnit, hasError), timestamp) AS latest
|
|
8947
|
+
SELECT ${key},
|
|
8948
|
+
argMax(tuple(name, value, estimatedCost, costUnit, hasError, partialCost), timestamp) AS latest
|
|
8769
8949
|
FROM (
|
|
8770
|
-
SELECT
|
|
8771
|
-
if(${costRow}, ifNull(JSONHas(costMetadata, 'error') AND JSONType(costMetadata, 'error') != 'Null', 0), 0) AS hasError
|
|
8950
|
+
SELECT ${key}, metricId, timestamp, name, value, estimatedCost, costUnit,
|
|
8951
|
+
if(${costRow}, ifNull(JSONHas(costMetadata, 'error') AND JSONType(costMetadata, 'error') != 'Null', 0), 0) AS hasError,
|
|
8952
|
+
if(${costRow}, ifNull(JSONExtractString(costMetadata, 'error') = 'partial_cost', 0), 0) AS partialCost
|
|
8772
8953
|
FROM ${TABLE_METRIC_EVENTS}
|
|
8773
|
-
WHERE
|
|
8954
|
+
WHERE ${source.usageBound}
|
|
8774
8955
|
AND name IN (${names.join(", ")})
|
|
8775
8956
|
AND timestamp >= ${from}${tenant}
|
|
8776
8957
|
)
|
|
8777
|
-
GROUP BY
|
|
8958
|
+
GROUP BY ${key}, metricId
|
|
8778
8959
|
)
|
|
8779
8960
|
)
|
|
8780
|
-
GROUP BY
|
|
8961
|
+
GROUP BY ${key}
|
|
8781
8962
|
)`;
|
|
8782
8963
|
}
|
|
8783
8964
|
function nullableNumber(value) {
|
|
8784
8965
|
return value === null || value === void 0 ? null : Number(value);
|
|
8785
8966
|
}
|
|
8786
|
-
|
|
8787
|
-
|
|
8967
|
+
function aggregateTraces(client, plan, timeoutMs) {
|
|
8968
|
+
return runAggregate(client, plan, compileClickHouseTraceAggregate(plan), timeoutMs);
|
|
8969
|
+
}
|
|
8970
|
+
async function aggregateSpans(client, plan, timeoutMs) {
|
|
8971
|
+
return await runAggregate(client, plan, compileClickHouseSpanAggregate(plan), timeoutMs);
|
|
8972
|
+
}
|
|
8973
|
+
async function runAggregate(client, plan, compiled, timeoutMs) {
|
|
8974
|
+
const result = await runWithClickHouseTraceQueryTimeout(client, { timeoutMs }, compiled);
|
|
8788
8975
|
let truncated;
|
|
8789
8976
|
let resultRows;
|
|
8790
8977
|
if (plan.interval === void 0) {
|
|
@@ -9680,6 +9867,7 @@ function buildBranchCursor(row) {
|
|
|
9680
9867
|
//#endregion
|
|
9681
9868
|
//#region src/storage/domains/observability/v-next/index.ts
|
|
9682
9869
|
const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
|
|
9870
|
+
const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate"] : [];
|
|
9683
9871
|
const threadQueryFeatures = typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [];
|
|
9684
9872
|
function buildSignalMigrationRequiredMessage(args) {
|
|
9685
9873
|
const tableList = args.tables.map((table) => ` - ${table.table} (${table.engine})`).join("\n");
|
|
@@ -10123,6 +10311,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
10123
10311
|
"trace-query-root-entity-id",
|
|
10124
10312
|
"trace-aggregate",
|
|
10125
10313
|
...spanQueryFeatures,
|
|
10314
|
+
...spanAggregateFeatures,
|
|
10126
10315
|
...threadQueryFeatures
|
|
10127
10316
|
];
|
|
10128
10317
|
return [
|
|
@@ -10147,6 +10336,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
10147
10336
|
"trace-query-root-entity-id",
|
|
10148
10337
|
"trace-aggregate",
|
|
10149
10338
|
...spanQueryFeatures,
|
|
10339
|
+
...spanAggregateFeatures,
|
|
10150
10340
|
...threadQueryFeatures
|
|
10151
10341
|
];
|
|
10152
10342
|
}
|
|
@@ -10298,6 +10488,18 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
10298
10488
|
}, error);
|
|
10299
10489
|
}
|
|
10300
10490
|
}
|
|
10491
|
+
async aggregateSpans(plan) {
|
|
10492
|
+
try {
|
|
10493
|
+
return await aggregateSpans(this.#client, plan, this.#traceQueryTimeoutMs);
|
|
10494
|
+
} catch (error) {
|
|
10495
|
+
if (error instanceof MastraError || error instanceof coreStorage.TraceQueryExecutionError || error instanceof coreStorage.TraceQueryResourceLimitError) throw error;
|
|
10496
|
+
throw new MastraError({
|
|
10497
|
+
id: createStorageErrorId("CLICKHOUSE", "AGGREGATE_SPANS", "FAILED"),
|
|
10498
|
+
domain: ErrorDomain.STORAGE,
|
|
10499
|
+
category: ErrorCategory.THIRD_PARTY
|
|
10500
|
+
}, error);
|
|
10501
|
+
}
|
|
10502
|
+
}
|
|
10301
10503
|
async getTraceQueryObservedFields(plan) {
|
|
10302
10504
|
try {
|
|
10303
10505
|
return await getTraceQueryObservedFields(this.#client, plan, this.#traceQueryDiscoveryLimits);
|