@mastra/duckdb 1.14.0 → 1.14.1-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/docs/references/reference-vectors-duckdb.md +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{observability-Bub8vOtT.js → observability-CXUQZgpz.js} +263 -28
- package/dist/observability-CXUQZgpz.js.map +1 -0
- package/dist/{observability-DJafBDSz.cjs → observability-G5gsNol7.cjs} +262 -27
- package/dist/observability-G5gsNol7.cjs.map +1 -0
- package/dist/storage/domains/observability/ddl.d.ts +1 -1
- package/dist/storage/domains/observability/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/helpers.d.ts +8 -0
- package/dist/storage/domains/observability/helpers.d.ts.map +1 -1
- package/dist/storage/domains/observability/preview-compat.d.ts +47 -0
- package/dist/storage/domains/observability/preview-compat.d.ts.map +1 -0
- package/dist/storage/domains/observability/span-query.d.ts +2 -0
- package/dist/storage/domains/observability/span-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/trace-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/tracing.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/observability-Bub8vOtT.js.map +0 -1
- package/dist/observability-DJafBDSz.cjs.map +0 -1
package/dist/docs/SKILL.md
CHANGED
|
@@ -216,7 +216,7 @@ DuckDB vector store supports MongoDB-like filter operators:
|
|
|
216
216
|
| ---------- | ------------------------------------------ |
|
|
217
217
|
| Comparison | `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte` |
|
|
218
218
|
| Logical | `$and`, `$or`, `$not`, `$nor` |
|
|
219
|
-
| Array | `$in`, `$nin`
|
|
219
|
+
| Array | `$in`, `$nin`, `$all` |
|
|
220
220
|
| Element | `$exists` |
|
|
221
221
|
| Text | `$contains` |
|
|
222
222
|
|
package/dist/index.cjs
CHANGED
|
@@ -556,7 +556,7 @@ var ObservabilityStorageDuckDB = class extends _mastra_core_storage.Observabilit
|
|
|
556
556
|
async loadDelegate() {
|
|
557
557
|
if (this.delegate) return this.delegate;
|
|
558
558
|
if (this.unavailableError) return null;
|
|
559
|
-
if (!this.loadPromise) this.loadPromise = Promise.resolve().then(() => require("./observability-
|
|
559
|
+
if (!this.loadPromise) this.loadPromise = Promise.resolve().then(() => require("./observability-G5gsNol7.cjs")).then(({ ObservabilityStorageDuckDB }) => {
|
|
560
560
|
const delegate = new ObservabilityStorageDuckDB({ db: this.db });
|
|
561
561
|
this.delegate = delegate;
|
|
562
562
|
return delegate;
|
package/dist/index.js
CHANGED
|
@@ -555,7 +555,7 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
|
|
|
555
555
|
async loadDelegate() {
|
|
556
556
|
if (this.delegate) return this.delegate;
|
|
557
557
|
if (this.unavailableError) return null;
|
|
558
|
-
if (!this.loadPromise) this.loadPromise = import("./observability-
|
|
558
|
+
if (!this.loadPromise) this.loadPromise = import("./observability-CXUQZgpz.js").then(({ ObservabilityStorageDuckDB }) => {
|
|
559
559
|
const delegate = new ObservabilityStorageDuckDB({ db: this.db });
|
|
560
560
|
this.delegate = delegate;
|
|
561
561
|
return delegate;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as DuckDBConnection, r as DuckDBQueryTimeoutError, t as spanQueryFeatures } from "./features-D3sUOsCL.js";
|
|
2
2
|
import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
|
|
3
3
|
import * as coreStorage from "@mastra/core/storage";
|
|
4
|
-
import { BRANCH_SPAN_TYPES, METRIC_DISTINCT_COLUMNS, ObservabilityStorage,
|
|
4
|
+
import { BRANCH_SPAN_TYPES, METRIC_DISTINCT_COLUMNS, ObservabilityStorage, computeTraceStatus, createStorageErrorId, executeRetentionPrune, feedbackRecordSchema, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, resolveRetentionTargets, retentionCutoffMs, toTraceSpans } from "@mastra/core/storage";
|
|
5
5
|
import { coreFeatures } from "@mastra/core/features";
|
|
6
6
|
import { EntityType } from "@mastra/core/observability";
|
|
7
7
|
import { parseFieldKey } from "@mastra/core/utils";
|
|
@@ -108,7 +108,22 @@ CREATE TABLE IF NOT EXISTS span_events (
|
|
|
108
108
|
links JSON,
|
|
109
109
|
input JSON,
|
|
110
110
|
output JSON,
|
|
111
|
-
error JSON
|
|
111
|
+
error JSON,
|
|
112
|
+
|
|
113
|
+
-- Previews of input/output/error in the shared core format, set on the
|
|
114
|
+
-- event rows that carry the matching payload ('' = nothing to preview).
|
|
115
|
+
-- NULL marks a row written before previews were stored, or a row that does
|
|
116
|
+
-- not carry that payload.
|
|
117
|
+
inputPreview VARCHAR,
|
|
118
|
+
outputPreview VARCHAR,
|
|
119
|
+
errorPreview VARCHAR,
|
|
120
|
+
|
|
121
|
+
-- Whether this row carries a non-null input/output/error payload. Lets the
|
|
122
|
+
-- light trace list pick each preview's row without reading the payloads.
|
|
123
|
+
-- NULL on rows written before these columns existed.
|
|
124
|
+
hasInputPayload BOOLEAN,
|
|
125
|
+
hasOutputPayload BOOLEAN,
|
|
126
|
+
hasErrorPayload BOOLEAN
|
|
112
127
|
)`;
|
|
113
128
|
/** DDL for the metric_events append-only table. */
|
|
114
129
|
const METRIC_EVENTS_DDL = `
|
|
@@ -349,6 +364,12 @@ const ALL_MIGRATIONS = [
|
|
|
349
364
|
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
|
|
350
365
|
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
|
|
351
366
|
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
|
|
367
|
+
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS inputPreview VARCHAR`,
|
|
368
|
+
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS outputPreview VARCHAR`,
|
|
369
|
+
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS errorPreview VARCHAR`,
|
|
370
|
+
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS hasInputPayload BOOLEAN`,
|
|
371
|
+
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS hasOutputPayload BOOLEAN`,
|
|
372
|
+
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS hasErrorPayload BOOLEAN`,
|
|
352
373
|
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
|
|
353
374
|
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
|
|
354
375
|
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
|
|
@@ -679,6 +700,46 @@ function buildPaginationClause(pagination) {
|
|
|
679
700
|
};
|
|
680
701
|
}
|
|
681
702
|
//#endregion
|
|
703
|
+
//#region src/storage/domains/observability/preview-compat.ts
|
|
704
|
+
const generator = coreStorage.buildStoredPreview;
|
|
705
|
+
const canGeneratePreviews = typeof generator === "function";
|
|
706
|
+
/** SQL detection only; preview formatting stays in TypeScript. Empty strings are intentional. */
|
|
707
|
+
function missingPreviewSql(column) {
|
|
708
|
+
return canGeneratePreviews ? `${column} IS NULL` : "FALSE";
|
|
709
|
+
}
|
|
710
|
+
function needsPreview(value) {
|
|
711
|
+
return canGeneratePreviews && value == null;
|
|
712
|
+
}
|
|
713
|
+
function storedPreview(preview, value, serialized) {
|
|
714
|
+
return canGeneratePreviews ? generator(preview, value, serialized) : preview ?? null;
|
|
715
|
+
}
|
|
716
|
+
function readPreview(preview, payload, jsonEncoded = false) {
|
|
717
|
+
const supplied = preview;
|
|
718
|
+
if (!canGeneratePreviews) return supplied ?? null;
|
|
719
|
+
if (supplied != null) return supplied.trim() || null;
|
|
720
|
+
let value = payload;
|
|
721
|
+
if (needsPreview(preview) && jsonEncoded && typeof payload === "string") try {
|
|
722
|
+
value = JSON.parse(payload);
|
|
723
|
+
} catch {
|
|
724
|
+
value = null;
|
|
725
|
+
}
|
|
726
|
+
return storedPreview(supplied, value, jsonEncoded && typeof payload === "string" ? payload : void 0) || null;
|
|
727
|
+
}
|
|
728
|
+
/** Older response builders may shorten previews or turn empty strings into null. */
|
|
729
|
+
function buildSpanQueryResponse(...args) {
|
|
730
|
+
if (canGeneratePreviews) return coreStorage.buildSpanQueryResponse(...args);
|
|
731
|
+
const [plan, selected, payloads, metrics] = args;
|
|
732
|
+
const response = coreStorage.buildSpanQueryResponse(plan, selected, [], metrics);
|
|
733
|
+
const byIdentity = new Map(payloads.map((payload) => [coreStorage.spanQueryIdentityKey(payload), payload]));
|
|
734
|
+
for (const span of response.spans) {
|
|
735
|
+
const payload = byIdentity.get(coreStorage.spanQueryIdentityKey(span));
|
|
736
|
+
span.inputPreview = payload?.inputPreview ?? null;
|
|
737
|
+
span.outputPreview = payload?.outputPreview ?? null;
|
|
738
|
+
span.errorPreview = payload?.errorPreview ?? null;
|
|
739
|
+
}
|
|
740
|
+
return response;
|
|
741
|
+
}
|
|
742
|
+
//#endregion
|
|
682
743
|
//#region src/storage/domains/observability/helpers.ts
|
|
683
744
|
/** Shorthand for {@link DuckDBConnection.sqlValue}. */
|
|
684
745
|
const v = DuckDBConnection.sqlValue;
|
|
@@ -759,6 +820,18 @@ function parseJsonArray(value) {
|
|
|
759
820
|
const parsed = parseJson(value);
|
|
760
821
|
return Array.isArray(parsed) ? parsed : null;
|
|
761
822
|
}
|
|
823
|
+
/**
|
|
824
|
+
* Preview of a payload read back from a span_events JSON column, for rows written before previews
|
|
825
|
+
* were stored. JSON text goes through `buildPreviewFromJson`, so the result matches what the writer
|
|
826
|
+
* would have stored for the same value; an already decoded value goes through `buildPreview`.
|
|
827
|
+
*/
|
|
828
|
+
function previewFromPayload(value) {
|
|
829
|
+
return readPreview(null, value, typeof value === "string");
|
|
830
|
+
}
|
|
831
|
+
/** A stored preview as the API returns it: `''` (nothing to preview) and NULL both become null. */
|
|
832
|
+
function apiPreview(value) {
|
|
833
|
+
return readPreview(value);
|
|
834
|
+
}
|
|
762
835
|
//#endregion
|
|
763
836
|
//#region src/storage/domains/observability/polling.ts
|
|
764
837
|
const OBSERVABILITY_DELTA_POLLING_FEATURE = "observability-delta-polling";
|
|
@@ -2962,7 +3035,12 @@ const TRACE_SELECT = `
|
|
|
2962
3035
|
r.entityId AS entityId,
|
|
2963
3036
|
r.parentSpanId AS parentSpanId,
|
|
2964
3037
|
r.metadata AS metadata,
|
|
2965
|
-
|
|
3038
|
+
r.inputPreview AS inputPreview,
|
|
3039
|
+
r.outputPreview AS outputPreview,
|
|
3040
|
+
r.errorPreview AS errorPreview,
|
|
3041
|
+
r.eventType AS rootEventType,
|
|
3042
|
+
r.timestamp AS rootTimestamp,
|
|
3043
|
+
r.cursorId AS rootCursorId,
|
|
2966
3044
|
r.threadId AS threadId,
|
|
2967
3045
|
r.resourceId AS resourceId,
|
|
2968
3046
|
r.startedAt AS startedAt,
|
|
@@ -3613,12 +3691,44 @@ async function getTraceQueryValues(db, plan) {
|
|
|
3613
3691
|
function asIsoTimestamp(value) {
|
|
3614
3692
|
return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
|
|
3615
3693
|
}
|
|
3694
|
+
const TRACE_PREVIEW_PAYLOADS = [
|
|
3695
|
+
["inputPreview", "input"],
|
|
3696
|
+
["outputPreview", "output"],
|
|
3697
|
+
["errorPreview", "error"]
|
|
3698
|
+
];
|
|
3699
|
+
/**
|
|
3700
|
+
* Builds the previews of listed roots whose current row was written before previews were stored
|
|
3701
|
+
* (NULL preview), from that same row's payload. Only the flagged rows' payloads are read, so the
|
|
3702
|
+
* fallback fades out as old rows age out of the listed range. Rows written since then carry a
|
|
3703
|
+
* non-NULL preview (`''` when there is nothing to preview) for every payload they hold.
|
|
3704
|
+
*/
|
|
3705
|
+
async function fillMissingTracePreviews(db, rows) {
|
|
3706
|
+
const flagged = rows.filter((row) => row.traceId != null && TRACE_PREVIEW_PAYLOADS.some(([preview]) => needsPreview(row[preview])));
|
|
3707
|
+
if (flagged.length === 0) return;
|
|
3708
|
+
const values = [];
|
|
3709
|
+
const where = flagged.map((row) => {
|
|
3710
|
+
values.push(row.traceId, row.rootSpanId, row.rootEventType, row.rootTimestamp, row.rootCursorId);
|
|
3711
|
+
return "(traceId = ? AND spanId = ? AND eventType = ? AND timestamp = ? AND cursorId IS NOT DISTINCT FROM ?)";
|
|
3712
|
+
}).join(" OR ");
|
|
3713
|
+
const payloads = await db.query(`SELECT traceId,
|
|
3714
|
+
${payloadColumnSql("input")} AS input,
|
|
3715
|
+
${payloadColumnSql("output")} AS output,
|
|
3716
|
+
error
|
|
3717
|
+
FROM span_events
|
|
3718
|
+
WHERE parentSpanId IS NULL AND (${where})`, values);
|
|
3719
|
+
const byTrace = new Map(payloads.map((payload) => [String(payload.traceId), payload]));
|
|
3720
|
+
for (const row of flagged) {
|
|
3721
|
+
const payload = byTrace.get(String(row.traceId));
|
|
3722
|
+
for (const [preview, column] of TRACE_PREVIEW_PAYLOADS) if (needsPreview(row[preview])) row[preview] = previewFromPayload(payload?.[column] ?? null);
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3616
3725
|
async function queryTraces(db, plan) {
|
|
3617
3726
|
if (plan.paginationMode === "delta") assertDeltaPollingEnabled();
|
|
3618
3727
|
if (plan.paginationMode === "page") {
|
|
3619
3728
|
const query = compileDuckDBTraceQuery(plan);
|
|
3620
3729
|
const rows = await db.query(query.sql, query.values);
|
|
3621
3730
|
const total = Number(rows[0]?.total ?? 0);
|
|
3731
|
+
await fillMissingTracePreviews(db, rows);
|
|
3622
3732
|
const traces = rows.filter((row) => row.traceId != null).map((row) => ({
|
|
3623
3733
|
traceId: String(row.traceId),
|
|
3624
3734
|
rootSpanId: String(row.rootSpanId),
|
|
@@ -3627,7 +3737,9 @@ async function queryTraces(db, plan) {
|
|
|
3627
3737
|
parentSpanId: row.parentSpanId ?? null,
|
|
3628
3738
|
createdAt: asIsoTimestamp(row.startedAt),
|
|
3629
3739
|
metadata: parseJson(row.metadata) ?? null,
|
|
3630
|
-
inputPreview:
|
|
3740
|
+
inputPreview: apiPreview(row.inputPreview),
|
|
3741
|
+
outputPreview: apiPreview(row.outputPreview),
|
|
3742
|
+
errorPreview: apiPreview(row.errorPreview),
|
|
3631
3743
|
threadId: row.threadId == null ? null : String(row.threadId),
|
|
3632
3744
|
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
3633
3745
|
startedAt: asIsoTimestamp(row.startedAt),
|
|
@@ -3663,6 +3775,7 @@ async function queryTraces(db, plan) {
|
|
|
3663
3775
|
}) : null }
|
|
3664
3776
|
});
|
|
3665
3777
|
}
|
|
3778
|
+
await fillMissingTracePreviews(db, visibleRows);
|
|
3666
3779
|
const traces = visibleRows.map((row) => ({
|
|
3667
3780
|
traceId: String(row.traceId),
|
|
3668
3781
|
rootSpanId: String(row.rootSpanId),
|
|
@@ -3671,7 +3784,9 @@ async function queryTraces(db, plan) {
|
|
|
3671
3784
|
parentSpanId: row.parentSpanId ?? null,
|
|
3672
3785
|
createdAt: asIsoTimestamp(row.startedAt),
|
|
3673
3786
|
metadata: parseJson(row.metadata) ?? null,
|
|
3674
|
-
inputPreview:
|
|
3787
|
+
inputPreview: apiPreview(row.inputPreview),
|
|
3788
|
+
outputPreview: apiPreview(row.outputPreview),
|
|
3789
|
+
errorPreview: apiPreview(row.errorPreview),
|
|
3675
3790
|
threadId: row.threadId == null ? null : String(row.threadId),
|
|
3676
3791
|
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
3677
3792
|
startedAt: asIsoTimestamp(row.startedAt),
|
|
@@ -3834,6 +3949,11 @@ function compileDuckDBSpanQuery(plan) {
|
|
|
3834
3949
|
values
|
|
3835
3950
|
};
|
|
3836
3951
|
}
|
|
3952
|
+
const previewPayloads = [
|
|
3953
|
+
["inputPreview", payloadColumnSql("input")],
|
|
3954
|
+
["outputPreview", payloadColumnSql("output")],
|
|
3955
|
+
["errorPreview", "error"]
|
|
3956
|
+
];
|
|
3837
3957
|
async function querySpans(db, plan) {
|
|
3838
3958
|
const deadline = performance.now() + coreStorage.TRACE_QUERY_DEFAULT_TIMEOUT_MS;
|
|
3839
3959
|
const query = async (sql, values) => {
|
|
@@ -3862,9 +3982,24 @@ async function querySpans(db, plan) {
|
|
|
3862
3982
|
payloadValues.push(row.cursorId, row.endedAt);
|
|
3863
3983
|
return `(${key} AND cursorId IS NOT DISTINCT FROM ? AND endedAt = ?)`;
|
|
3864
3984
|
}).join(" OR ");
|
|
3865
|
-
const payloads = await query(`SELECT DISTINCT ${identities},
|
|
3866
|
-
|
|
3867
|
-
FROM span_events WHERE ${payloadWhere}`, payloadValues)
|
|
3985
|
+
const payloads = (await query(`SELECT DISTINCT ${identities},
|
|
3986
|
+
${previewPayloads.map(([preview, payload]) => `${preview}, CASE WHEN ${missingPreviewSql(preview)} THEN CAST(${payload} AS VARCHAR) END AS ${preview}Payload`).join(",\n ")}
|
|
3987
|
+
FROM span_events WHERE ${payloadWhere}`, payloadValues)).map((row) => {
|
|
3988
|
+
const payload = {
|
|
3989
|
+
organizationId: row.organizationId,
|
|
3990
|
+
resourceId: row.resourceId,
|
|
3991
|
+
traceId: row.traceId,
|
|
3992
|
+
spanId: row.spanId,
|
|
3993
|
+
inputPreview: null,
|
|
3994
|
+
outputPreview: null,
|
|
3995
|
+
errorPreview: null
|
|
3996
|
+
};
|
|
3997
|
+
for (const [preview] of previewPayloads) {
|
|
3998
|
+
const stored = row[preview];
|
|
3999
|
+
payload[preview] = readPreview(stored, row[`${preview}Payload`], true);
|
|
4000
|
+
}
|
|
4001
|
+
return payload;
|
|
4002
|
+
});
|
|
3868
4003
|
const metricValues = [];
|
|
3869
4004
|
const metricWhere = keys(page, metricValues);
|
|
3870
4005
|
const metrics = await query(`SELECT ${identities}, name, estimatedCost, costUnit,
|
|
@@ -3873,7 +4008,7 @@ async function querySpans(db, plan) {
|
|
|
3873
4008
|
FROM metric_events WHERE (${metricWhere}) AND name LIKE 'mastra_model_%'
|
|
3874
4009
|
LIMIT ${coreStorage.SPAN_QUERY_MAX_COST_METRICS + 1}`, metricValues);
|
|
3875
4010
|
if (metrics.length > coreStorage.SPAN_QUERY_MAX_COST_METRICS) throw new coreStorage.TraceQueryResourceLimitError();
|
|
3876
|
-
return
|
|
4011
|
+
return buildSpanQueryResponse(plan, selected, payloads, metrics);
|
|
3877
4012
|
}
|
|
3878
4013
|
//#endregion
|
|
3879
4014
|
//#region src/storage/domains/observability/trace-aggregate.ts
|
|
@@ -4233,7 +4368,13 @@ const COLUMNS_SQL = [
|
|
|
4233
4368
|
"input",
|
|
4234
4369
|
"output",
|
|
4235
4370
|
"error",
|
|
4236
|
-
"requestContext"
|
|
4371
|
+
"requestContext",
|
|
4372
|
+
"inputPreview",
|
|
4373
|
+
"outputPreview",
|
|
4374
|
+
"errorPreview",
|
|
4375
|
+
"hasInputPayload",
|
|
4376
|
+
"hasOutputPayload",
|
|
4377
|
+
"hasErrorPayload"
|
|
4237
4378
|
].join(", ");
|
|
4238
4379
|
const PAYLOAD_COLUMN_SET = new Set(SPAN_PAYLOAD_COLUMNS);
|
|
4239
4380
|
/**
|
|
@@ -4245,6 +4386,32 @@ const PAYLOAD_COLUMN_SET = new Set(SPAN_PAYLOAD_COLUMNS);
|
|
|
4245
4386
|
function argMaxNonNull(col) {
|
|
4246
4387
|
return `arg_max(${col}, timestamp) FILTER (WHERE ${col} IS NOT NULL${PAYLOAD_COLUMN_SET.has(col) ? ` AND ${col} <> '${JSON_NULL_PLACEHOLDER}'` : ""}) as ${col}`;
|
|
4247
4388
|
}
|
|
4389
|
+
/** The payload column each stored preview column describes, and its payload-presence flag. */
|
|
4390
|
+
const PREVIEW_PAYLOADS = [
|
|
4391
|
+
[
|
|
4392
|
+
"inputPreview",
|
|
4393
|
+
"input",
|
|
4394
|
+
"hasInputPayload"
|
|
4395
|
+
],
|
|
4396
|
+
[
|
|
4397
|
+
"outputPreview",
|
|
4398
|
+
"output",
|
|
4399
|
+
"hasOutputPayload"
|
|
4400
|
+
],
|
|
4401
|
+
[
|
|
4402
|
+
"errorPreview",
|
|
4403
|
+
"error",
|
|
4404
|
+
"hasErrorPayload"
|
|
4405
|
+
]
|
|
4406
|
+
];
|
|
4407
|
+
/**
|
|
4408
|
+
* Select the preview from the latest event carrying its payload (or an explicit preview).
|
|
4409
|
+
* Wrapping the preview preserves NULL on that event instead of reviving an older preview.
|
|
4410
|
+
* The stored presence flag tells blank content from a payload-less event without reading the
|
|
4411
|
+
* payload. Rows written before the flag (NULL) always qualify: their NULL preview sends the span
|
|
4412
|
+
* to {@link fillMissingReconstructedPreviews}, which rebuilds it from the payloads.
|
|
4413
|
+
*/
|
|
4414
|
+
const RECONSTRUCT_PREVIEWS_SQL = PREVIEW_PAYLOADS.map(([preview, , present]) => `(coalesce(arg_max(struct_pack(value := ${preview}), timestamp) FILTER (WHERE coalesce(${present}, true) OR ${preview} <> ''), arg_max(struct_pack(value := ${preview}), timestamp))).value as ${preview}`).join(",\n ");
|
|
4248
4415
|
const SPAN_RECONSTRUCT_SELECT = `
|
|
4249
4416
|
SELECT
|
|
4250
4417
|
traceId, spanId,
|
|
@@ -4300,8 +4467,8 @@ const SPAN_RECONSTRUCT_SELECT_LIGHT = `
|
|
|
4300
4467
|
`;
|
|
4301
4468
|
/**
|
|
4302
4469
|
* Lightweight list variant — also reconstructs `metadata` for the list's
|
|
4303
|
-
* configurable columns and
|
|
4304
|
-
*
|
|
4470
|
+
* configurable columns and the stored previews (see {@link RECONSTRUCT_PREVIEWS_SQL}),
|
|
4471
|
+
* so the list never ships the payload blobs to the caller.
|
|
4305
4472
|
*/
|
|
4306
4473
|
const SPAN_RECONSTRUCT_SELECT_LIGHT_LIST = `
|
|
4307
4474
|
SELECT
|
|
@@ -4319,7 +4486,7 @@ const SPAN_RECONSTRUCT_SELECT_LIGHT_LIST = `
|
|
|
4319
4486
|
${argMaxNonNull("resourceId")},
|
|
4320
4487
|
${argMaxNonNull("error")},
|
|
4321
4488
|
${argMaxNonNull("metadata")},
|
|
4322
|
-
${
|
|
4489
|
+
${RECONSTRUCT_PREVIEWS_SQL}
|
|
4323
4490
|
FROM span_events
|
|
4324
4491
|
`;
|
|
4325
4492
|
/**
|
|
@@ -4441,6 +4608,26 @@ function rowToLightSpanRecord(row) {
|
|
|
4441
4608
|
updatedAt: rowEndedAt(row)
|
|
4442
4609
|
};
|
|
4443
4610
|
}
|
|
4611
|
+
/**
|
|
4612
|
+
* Builds the previews of spans written before previews were stored (NULL after
|
|
4613
|
+
* {@link RECONSTRUCT_PREVIEWS_SQL}) from those spans' reconstructed payloads. Runs only while such
|
|
4614
|
+
* spans are on the page.
|
|
4615
|
+
*/
|
|
4616
|
+
async function fillMissingReconstructedPreviews(db, rows) {
|
|
4617
|
+
const flagged = rows.filter((row) => PREVIEW_PAYLOADS.some(([preview]) => needsPreview(row[preview])));
|
|
4618
|
+
if (flagged.length === 0) return;
|
|
4619
|
+
const traceIds = [...new Set(flagged.map((row) => row.traceId))];
|
|
4620
|
+
const payloads = await db.query(`SELECT traceId, spanId, ${PREVIEW_PAYLOADS.map(([, payload]) => argMaxNonNull(payload)).join(", ")}
|
|
4621
|
+
FROM span_events
|
|
4622
|
+
WHERE traceId IN (${traceIds.map(() => "?").join(", ")})
|
|
4623
|
+
AND (traceId, spanId) IN (SELECT * FROM (VALUES ${flagged.map(() => "(?, ?)").join(", ")}))
|
|
4624
|
+
GROUP BY traceId, spanId`, [...traceIds, ...flagged.flatMap((row) => [row.traceId, row.spanId])]);
|
|
4625
|
+
const byKey = new Map(payloads.map((payload) => [`${payload.traceId}\u0000${payload.spanId}`, payload]));
|
|
4626
|
+
for (const row of flagged) {
|
|
4627
|
+
const payload = byKey.get(`${row.traceId}\u0000${row.spanId}`);
|
|
4628
|
+
for (const [preview, column] of PREVIEW_PAYLOADS) if (needsPreview(row[preview])) row[preview] = previewFromPayload(payload?.[column] ?? null);
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4444
4631
|
function rowToLightSpanRecordWithPreview(row) {
|
|
4445
4632
|
const record = rowToLightSpanRecord(row);
|
|
4446
4633
|
return {
|
|
@@ -4449,7 +4636,9 @@ function rowToLightSpanRecordWithPreview(row) {
|
|
|
4449
4636
|
threadId: row.threadId ?? null,
|
|
4450
4637
|
resourceId: row.resourceId ?? null,
|
|
4451
4638
|
metadata: parseJson(row.metadata),
|
|
4452
|
-
inputPreview:
|
|
4639
|
+
inputPreview: apiPreview(row.inputPreview),
|
|
4640
|
+
outputPreview: apiPreview(row.outputPreview),
|
|
4641
|
+
errorPreview: apiPreview(row.errorPreview)
|
|
4453
4642
|
};
|
|
4454
4643
|
}
|
|
4455
4644
|
function rowToSpanRecord(row) {
|
|
@@ -4607,6 +4796,28 @@ function partitionAnchorFilters(filters) {
|
|
|
4607
4796
|
hasChildError
|
|
4608
4797
|
};
|
|
4609
4798
|
}
|
|
4799
|
+
/** JSON text of a payload value, or null when it is missing. */
|
|
4800
|
+
function serializePayload(value) {
|
|
4801
|
+
if (value === null || value === void 0) return null;
|
|
4802
|
+
return JSON.stringify(value) ?? null;
|
|
4803
|
+
}
|
|
4804
|
+
/**
|
|
4805
|
+
* The payload JSON and stored preview for one payload slot of an event row. A row that carries the
|
|
4806
|
+
* slot gets a preview built from the serialized value actually written, unless the record carries
|
|
4807
|
+
* an upstream preview; `''` when there is nothing to preview. A row that does not carry the slot
|
|
4808
|
+
* gets no payload and `''`, so a NULL preview only ever means a row written before previews.
|
|
4809
|
+
*/
|
|
4810
|
+
function previewOf(carried, value, upstream) {
|
|
4811
|
+
if (!carried) return {
|
|
4812
|
+
json: null,
|
|
4813
|
+
preview: ""
|
|
4814
|
+
};
|
|
4815
|
+
const json = serializePayload(value);
|
|
4816
|
+
return {
|
|
4817
|
+
json,
|
|
4818
|
+
preview: storedPreview(upstream, json === null ? null : value, json)
|
|
4819
|
+
};
|
|
4820
|
+
}
|
|
4610
4821
|
function toValuesTuple(row) {
|
|
4611
4822
|
return [
|
|
4612
4823
|
v(row.eventType),
|
|
@@ -4641,10 +4852,16 @@ function toValuesTuple(row) {
|
|
|
4641
4852
|
jsonV(row.tags),
|
|
4642
4853
|
jsonV(row.scope),
|
|
4643
4854
|
jsonV(row.links),
|
|
4644
|
-
payloadJsonV(row.input),
|
|
4645
|
-
payloadJsonV(row.output),
|
|
4646
|
-
|
|
4647
|
-
payloadJsonV(row.requestContext)
|
|
4855
|
+
row.input === null ? payloadJsonV(null) : v(row.input),
|
|
4856
|
+
row.output === null ? payloadJsonV(null) : v(row.output),
|
|
4857
|
+
row.error === null ? "NULL" : v(row.error),
|
|
4858
|
+
payloadJsonV(row.requestContext),
|
|
4859
|
+
v(row.inputPreview),
|
|
4860
|
+
v(row.outputPreview),
|
|
4861
|
+
v(row.errorPreview),
|
|
4862
|
+
v(row.hasInputPayload),
|
|
4863
|
+
v(row.hasOutputPayload),
|
|
4864
|
+
v(row.hasErrorPayload)
|
|
4648
4865
|
].join(", ");
|
|
4649
4866
|
}
|
|
4650
4867
|
async function insertSpanEvents(db, rows) {
|
|
@@ -4654,6 +4871,7 @@ async function insertSpanEvents(db, rows) {
|
|
|
4654
4871
|
}
|
|
4655
4872
|
function createStartSpanRow(s) {
|
|
4656
4873
|
const payloadOnEndRow = !!s.endedAt && !s.isEvent;
|
|
4874
|
+
const input = previewOf(!payloadOnEndRow, s.input, s.inputPreview);
|
|
4657
4875
|
return {
|
|
4658
4876
|
eventType: "start",
|
|
4659
4877
|
timestamp: s.startedAt,
|
|
@@ -4686,13 +4904,22 @@ function createStartSpanRow(s) {
|
|
|
4686
4904
|
tags: s.tags == null ? null : normalizeTags(s.tags),
|
|
4687
4905
|
scope: s.scope ?? null,
|
|
4688
4906
|
links: null,
|
|
4689
|
-
input:
|
|
4907
|
+
input: input.json,
|
|
4690
4908
|
output: null,
|
|
4691
4909
|
error: null,
|
|
4692
|
-
requestContext: payloadOnEndRow ? null : s.requestContext ?? null
|
|
4910
|
+
requestContext: payloadOnEndRow ? null : s.requestContext ?? null,
|
|
4911
|
+
inputPreview: input.preview,
|
|
4912
|
+
outputPreview: "",
|
|
4913
|
+
errorPreview: "",
|
|
4914
|
+
hasInputPayload: input.json !== null,
|
|
4915
|
+
hasOutputPayload: false,
|
|
4916
|
+
hasErrorPayload: false
|
|
4693
4917
|
};
|
|
4694
4918
|
}
|
|
4695
4919
|
function createEndSpanRow(s) {
|
|
4920
|
+
const input = previewOf(true, s.input, s.inputPreview);
|
|
4921
|
+
const output = previewOf(true, s.output, s.outputPreview);
|
|
4922
|
+
const error = previewOf(true, s.error, s.errorPreview);
|
|
4696
4923
|
return {
|
|
4697
4924
|
eventType: "end",
|
|
4698
4925
|
timestamp: s.endedAt,
|
|
@@ -4725,10 +4952,16 @@ function createEndSpanRow(s) {
|
|
|
4725
4952
|
tags: s.tags == null ? null : normalizeTags(s.tags),
|
|
4726
4953
|
scope: s.scope ?? null,
|
|
4727
4954
|
links: s.links ?? null,
|
|
4728
|
-
input:
|
|
4729
|
-
output:
|
|
4730
|
-
error:
|
|
4731
|
-
requestContext: s.requestContext ?? null
|
|
4955
|
+
input: input.json,
|
|
4956
|
+
output: output.json,
|
|
4957
|
+
error: error.json,
|
|
4958
|
+
requestContext: s.requestContext ?? null,
|
|
4959
|
+
inputPreview: input.preview,
|
|
4960
|
+
outputPreview: output.preview,
|
|
4961
|
+
errorPreview: error.preview,
|
|
4962
|
+
hasInputPayload: input.json !== null,
|
|
4963
|
+
hasOutputPayload: output.json !== null,
|
|
4964
|
+
hasErrorPayload: error.json !== null
|
|
4732
4965
|
};
|
|
4733
4966
|
}
|
|
4734
4967
|
/** Insert a 'start' event for a new span. */
|
|
@@ -5033,14 +5266,16 @@ async function listTraces(db, args) {
|
|
|
5033
5266
|
async function listTracesLight(db, args) {
|
|
5034
5267
|
const { filters, pagination, orderBy } = listTracesArgsSchema.parse(args);
|
|
5035
5268
|
const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getTraceDeltaCursor(db, filters) : void 0;
|
|
5036
|
-
const { pagination: resultPagination, spans } = await listTraceRows(db, {
|
|
5269
|
+
const { pagination: resultPagination, spans: rows } = await listTraceRows(db, {
|
|
5037
5270
|
filters,
|
|
5038
5271
|
pagination,
|
|
5039
5272
|
orderBy
|
|
5040
|
-
}, SPAN_RECONSTRUCT_SELECT_LIGHT_LIST,
|
|
5273
|
+
}, SPAN_RECONSTRUCT_SELECT_LIGHT_LIST, (row) => row, (rows) => rows);
|
|
5274
|
+
const pageRows = rows;
|
|
5275
|
+
await fillMissingReconstructedPreviews(db, pageRows);
|
|
5041
5276
|
return {
|
|
5042
5277
|
pagination: resultPagination,
|
|
5043
|
-
spans,
|
|
5278
|
+
spans: pageRows.map(rowToLightSpanRecordWithPreview),
|
|
5044
5279
|
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
5045
5280
|
};
|
|
5046
5281
|
}
|
|
@@ -5731,4 +5966,4 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
|
|
|
5731
5966
|
//#endregion
|
|
5732
5967
|
export { ObservabilityStorageDuckDB };
|
|
5733
5968
|
|
|
5734
|
-
//# sourceMappingURL=observability-
|
|
5969
|
+
//# sourceMappingURL=observability-CXUQZgpz.js.map
|