@mastra/duckdb 1.13.0-alpha.1 → 1.13.0-alpha.2
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 +1 -1
- package/dist/index.js +1 -1
- package/dist/{observability-DNuNWZFX.cjs → observability-D4eFjsKJ.cjs} +113 -52
- package/dist/observability-D4eFjsKJ.cjs.map +1 -0
- package/dist/{observability-Bk_y3hof.js → observability-DINJlR_a.js} +113 -52
- package/dist/observability-DINJlR_a.js.map +1 -0
- package/dist/storage/domains/observability/helpers.d.ts +17 -0
- package/dist/storage/domains/observability/helpers.d.ts.map +1 -1
- 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 +3 -3
- package/dist/observability-Bk_y3hof.js.map +0 -1
- package/dist/observability-DNuNWZFX.cjs.map +0 -1
package/dist/docs/SKILL.md
CHANGED
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-D4eFjsKJ.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-DINJlR_a.js").then(({ ObservabilityStorageDuckDB }) => {
|
|
559
559
|
const delegate = new ObservabilityStorageDuckDB({ db: this.db });
|
|
560
560
|
this.delegate = delegate;
|
|
561
561
|
return delegate;
|
|
@@ -688,6 +688,33 @@ function jsonV(val) {
|
|
|
688
688
|
return require_features.DuckDBConnection.sqlValue(JSON.stringify(val));
|
|
689
689
|
}
|
|
690
690
|
/**
|
|
691
|
+
* `span_events` JSON columns that can hold large payloads. Missing values are
|
|
692
|
+
* stored as the JSON literal `null` instead of SQL NULL.
|
|
693
|
+
*
|
|
694
|
+
* DuckDB only reads the selected rows of a large-string column when the
|
|
695
|
+
* column's validity mask is constant (no NULLs in the row group). Any SQL NULL
|
|
696
|
+
* makes it fall back to reading the whole 2,048-row vector, which loads every
|
|
697
|
+
* large string in it even when the query needs one row. Keeping these columns
|
|
698
|
+
* NULL-free lets point lookups and page reads load only what they return.
|
|
699
|
+
*/
|
|
700
|
+
const SPAN_PAYLOAD_COLUMNS = [
|
|
701
|
+
"attributes",
|
|
702
|
+
"input",
|
|
703
|
+
"output",
|
|
704
|
+
"requestContext"
|
|
705
|
+
];
|
|
706
|
+
/** Stored in {@link SPAN_PAYLOAD_COLUMNS} in place of SQL NULL; read back as missing. */
|
|
707
|
+
const JSON_NULL_PLACEHOLDER = "null";
|
|
708
|
+
/** Like {@link jsonV}, but writes the JSON `null` placeholder instead of SQL NULL. */
|
|
709
|
+
function payloadJsonV(val) {
|
|
710
|
+
if (val === null || val === void 0) return `'${JSON_NULL_PLACEHOLDER}'`;
|
|
711
|
+
return jsonV(val);
|
|
712
|
+
}
|
|
713
|
+
/** SQL for a payload column that maps the JSON `null` placeholder back to SQL NULL. */
|
|
714
|
+
function payloadColumnSql(column) {
|
|
715
|
+
return `NULLIF(${column}, '${JSON_NULL_PLACEHOLDER}')`;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
691
718
|
* Trim, drop blank, and dedupe tags before they are written, matching the
|
|
692
719
|
* PostgreSQL and ClickHouse stores so tag predicates and discovery agree.
|
|
693
720
|
*/
|
|
@@ -2935,7 +2962,7 @@ const TRACE_SELECT = `
|
|
|
2935
2962
|
r.entityId AS entityId,
|
|
2936
2963
|
r.parentSpanId AS parentSpanId,
|
|
2937
2964
|
r.metadata AS metadata,
|
|
2938
|
-
r.input AS input,
|
|
2965
|
+
${payloadColumnSql("r.input")} AS input,
|
|
2939
2966
|
r.threadId AS threadId,
|
|
2940
2967
|
r.resourceId AS resourceId,
|
|
2941
2968
|
r.startedAt AS startedAt,
|
|
@@ -3218,6 +3245,11 @@ function compileDuckDBTraceScope(relatedCollections, scope, timeRange, rootPredi
|
|
|
3218
3245
|
GROUP BY r.traceId
|
|
3219
3246
|
HAVING bool_or(r.timestamp >= CAST(? AS TIMESTAMP) AND r.timestamp < CAST(? AS TIMESTAMP))
|
|
3220
3247
|
AND bool_or(${rowConditions.join("\n AND ")})
|
|
3248
|
+
)`,
|
|
3249
|
+
`root_bounds AS (
|
|
3250
|
+
SELECT min(timestamp) AS minTs, max(timestamp) AS maxTs
|
|
3251
|
+
FROM span_events
|
|
3252
|
+
WHERE parentSpanId IS NULL AND traceId IN (SELECT traceId FROM root_trace_ids)
|
|
3221
3253
|
)`,
|
|
3222
3254
|
`root_events AS (
|
|
3223
3255
|
SELECT
|
|
@@ -3228,6 +3260,8 @@ function compileDuckDBTraceScope(relatedCollections, scope, timeRange, rootPredi
|
|
|
3228
3260
|
END AS startedAt
|
|
3229
3261
|
FROM span_events
|
|
3230
3262
|
WHERE parentSpanId IS NULL
|
|
3263
|
+
AND timestamp >= (SELECT minTs FROM root_bounds)
|
|
3264
|
+
AND timestamp <= (SELECT maxTs FROM root_bounds)
|
|
3231
3265
|
AND traceId IN (SELECT traceId FROM root_trace_ids)
|
|
3232
3266
|
)`,
|
|
3233
3267
|
`current_roots AS (
|
|
@@ -3250,7 +3284,15 @@ function compileDuckDBTraceScope(relatedCollections, scope, timeRange, rootPredi
|
|
|
3250
3284
|
)`
|
|
3251
3285
|
];
|
|
3252
3286
|
if (relatedCollections.has("spans")) {
|
|
3253
|
-
ctes.push(`
|
|
3287
|
+
ctes.push(`span_bounds AS (
|
|
3288
|
+
-- Exact event-time range of the in-scope traces, from a narrow
|
|
3289
|
+
-- (traceId, timestamp) pass. The join below cannot be pushed into the
|
|
3290
|
+
-- scan, so the range keeps it from decompressing every span row.
|
|
3291
|
+
SELECT min(timestamp) AS minTs, max(timestamp) AS maxTs
|
|
3292
|
+
FROM span_events
|
|
3293
|
+
WHERE traceId IN (SELECT traceId FROM root_scope)
|
|
3294
|
+
),
|
|
3295
|
+
current_span_rows AS (
|
|
3254
3296
|
SELECT
|
|
3255
3297
|
e.*,
|
|
3256
3298
|
coalesce(
|
|
@@ -3262,7 +3304,9 @@ function compileDuckDBTraceScope(relatedCollections, scope, timeRange, rootPredi
|
|
|
3262
3304
|
ORDER BY CASE WHEN e.endedAt IS NULL THEN 1 ELSE 0 END ASC, e.cursorId DESC
|
|
3263
3305
|
) AS currentRank
|
|
3264
3306
|
FROM span_events e
|
|
3265
|
-
INNER JOIN root_scope roots ON roots.traceId = e.traceId
|
|
3307
|
+
INNER JOIN root_scope roots ON roots.traceId = e.traceId
|
|
3308
|
+
WHERE e.timestamp >= (SELECT minTs FROM span_bounds)
|
|
3309
|
+
AND e.timestamp <= (SELECT maxTs FROM span_bounds)${tenantConditions("e").map((condition) => `\n AND ${condition}`).join("")}
|
|
3266
3310
|
),
|
|
3267
3311
|
current_spans AS (
|
|
3268
3312
|
SELECT
|
|
@@ -3812,7 +3856,7 @@ async function querySpans(db, plan) {
|
|
|
3812
3856
|
return `(${key} AND cursorId IS NOT DISTINCT FROM ? AND endedAt = ?)`;
|
|
3813
3857
|
}).join(" OR ");
|
|
3814
3858
|
const payloads = await query(`SELECT DISTINCT ${identities},
|
|
3815
|
-
substring(CAST(input AS VARCHAR), 1, ${_mastra_core_storage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS inputPreview, substring(CAST(output AS VARCHAR), 1, ${_mastra_core_storage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS outputPreview
|
|
3859
|
+
substring(CAST(${payloadColumnSql("input")} AS VARCHAR), 1, ${_mastra_core_storage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS inputPreview, substring(CAST(${payloadColumnSql("output")} AS VARCHAR), 1, ${_mastra_core_storage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS outputPreview
|
|
3816
3860
|
FROM span_events WHERE ${payloadWhere}`, payloadValues);
|
|
3817
3861
|
const metricValues = [];
|
|
3818
3862
|
const metricWhere = keys(page, metricValues);
|
|
@@ -4067,13 +4111,15 @@ const COLUMNS_SQL = [
|
|
|
4067
4111
|
"error",
|
|
4068
4112
|
"requestContext"
|
|
4069
4113
|
].join(", ");
|
|
4114
|
+
const PAYLOAD_COLUMN_SET = new Set(SPAN_PAYLOAD_COLUMNS);
|
|
4070
4115
|
/**
|
|
4071
4116
|
* Reconstruction query uses `arg_max(field, timestamp) FILTER (WHERE field IS NOT NULL)`
|
|
4072
4117
|
* so that the final end event supplies the terminal span fields without wiping
|
|
4073
|
-
* stable values emitted on the start event.
|
|
4118
|
+
* stable values emitted on the start event. Payload columns also skip the JSON
|
|
4119
|
+
* `null` placeholder, which stands in for a missing value.
|
|
4074
4120
|
*/
|
|
4075
4121
|
function argMaxNonNull(col) {
|
|
4076
|
-
return `arg_max(${col}, timestamp) FILTER (WHERE ${col} IS NOT NULL) as ${col}`;
|
|
4122
|
+
return `arg_max(${col}, timestamp) FILTER (WHERE ${col} IS NOT NULL${PAYLOAD_COLUMN_SET.has(col) ? ` AND ${col} <> '${JSON_NULL_PLACEHOLDER}'` : ""}) as ${col}`;
|
|
4077
4123
|
}
|
|
4078
4124
|
const SPAN_RECONSTRUCT_SELECT = `
|
|
4079
4125
|
SELECT
|
|
@@ -4184,28 +4230,55 @@ function buildPostAggReconstructSelect(postAgg, orderByField) {
|
|
|
4184
4230
|
`;
|
|
4185
4231
|
}
|
|
4186
4232
|
/**
|
|
4187
|
-
* Reconstruct spans
|
|
4188
|
-
*
|
|
4233
|
+
* Reconstruct the spans of one list page, reading payload columns only for
|
|
4234
|
+
* the page's own events.
|
|
4235
|
+
*
|
|
4236
|
+
* Two queries:
|
|
4237
|
+
* 1. `anchorSql` (a `WITH ...` prefix defining `anchorCte`) picks the page's
|
|
4238
|
+
* `(traceId, spanId)` anchors, and a narrow pass over `traceId`, `spanId`
|
|
4239
|
+
* and `timestamp` finds the exact time range of their events.
|
|
4240
|
+
* 2. Reconstruction scans only that range for exactly those anchors.
|
|
4189
4241
|
*
|
|
4190
|
-
* The `(traceId, spanId) IN (
|
|
4191
|
-
*
|
|
4192
|
-
*
|
|
4193
|
-
*
|
|
4242
|
+
* The `(traceId, spanId) IN (...)` semi-join cannot be pushed into the table
|
|
4243
|
+
* scan, so without a range DuckDB decompresses every payload column of every
|
|
4244
|
+
* row it reads. A lower bound alone (the earliest anchor) prunes nothing for
|
|
4245
|
+
* the oldest pages, which then read the whole table and can run out of memory.
|
|
4246
|
+
* Both bounds are passed as literal parameters so they always reach the scan
|
|
4247
|
+
* as plain range filters that zone maps can prune on.
|
|
4194
4248
|
*
|
|
4195
|
-
*
|
|
4196
|
-
*
|
|
4197
|
-
*
|
|
4198
|
-
*
|
|
4249
|
+
* Anchors and bounds are frozen after the first query, so spans written in
|
|
4250
|
+
* between cannot change which rows are on the page. An event a page span
|
|
4251
|
+
* receives in between is left for the next read, as if the page had been
|
|
4252
|
+
* read a moment earlier.
|
|
4199
4253
|
*/
|
|
4200
|
-
function
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4254
|
+
async function reconstructAnchoredPage(db, anchorSql, anchorCte, params, reconstructSelect, orderBySql) {
|
|
4255
|
+
const anchors = await db.query(`
|
|
4256
|
+
${anchorSql}
|
|
4257
|
+
SELECT anchors.traceId, anchors.spanId, bounds.minTs, bounds.maxTs
|
|
4258
|
+
FROM ${anchorCte} AS anchors,
|
|
4259
|
+
(
|
|
4260
|
+
SELECT min(timestamp) AS minTs, max(timestamp) AS maxTs
|
|
4261
|
+
FROM span_events
|
|
4262
|
+
WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM ${anchorCte})
|
|
4263
|
+
) AS bounds
|
|
4264
|
+
`, params);
|
|
4265
|
+
if (anchors.length === 0) return [];
|
|
4266
|
+
const { minTs, maxTs } = anchors[0];
|
|
4267
|
+
const anchorValues = anchors.map(() => "(?, ?)").join(", ");
|
|
4268
|
+
return db.query(`
|
|
4269
|
+
${reconstructSelect}
|
|
4270
|
+
WHERE timestamp >= ? AND timestamp <= ?
|
|
4271
|
+
AND (traceId, spanId) IN (SELECT * FROM (VALUES ${anchorValues}))
|
|
4272
|
+
GROUP BY traceId, spanId
|
|
4273
|
+
${orderBySql}
|
|
4274
|
+
`, [
|
|
4275
|
+
minTs,
|
|
4276
|
+
maxTs,
|
|
4277
|
+
...anchors.flatMap((anchor) => [anchor.traceId, anchor.spanId])
|
|
4278
|
+
]);
|
|
4206
4279
|
}
|
|
4207
4280
|
/**
|
|
4208
|
-
* Same time-bound trick as {@link
|
|
4281
|
+
* Same time-bound trick as {@link reconstructAnchoredPage}, but with the bound
|
|
4209
4282
|
* precomputed in JS and passed as a `?` parameter. The delta-poll query shape
|
|
4210
4283
|
* references its candidate CTE multiple times, which makes DuckDB materialize
|
|
4211
4284
|
* it and lose the dynamic-filter pushdown a scalar subquery bound relies on —
|
|
@@ -4439,15 +4512,15 @@ function toValuesTuple(row) {
|
|
|
4439
4512
|
v(row.environment),
|
|
4440
4513
|
v(row.source),
|
|
4441
4514
|
v(row.serviceName),
|
|
4442
|
-
|
|
4515
|
+
payloadJsonV(row.attributes),
|
|
4443
4516
|
jsonV(row.metadata),
|
|
4444
4517
|
jsonV(row.tags),
|
|
4445
4518
|
jsonV(row.scope),
|
|
4446
4519
|
jsonV(row.links),
|
|
4447
|
-
|
|
4448
|
-
|
|
4520
|
+
payloadJsonV(row.input),
|
|
4521
|
+
payloadJsonV(row.output),
|
|
4449
4522
|
jsonV(row.error),
|
|
4450
|
-
|
|
4523
|
+
payloadJsonV(row.requestContext)
|
|
4451
4524
|
].join(", ");
|
|
4452
4525
|
}
|
|
4453
4526
|
async function insertSpanEvents(db, rows) {
|
|
@@ -4661,7 +4734,7 @@ async function listTraceRows(db, args, reconstructSelect, mapRow, toSpans) {
|
|
|
4661
4734
|
},
|
|
4662
4735
|
spans: []
|
|
4663
4736
|
};
|
|
4664
|
-
const
|
|
4737
|
+
const spans = (await reconstructAnchoredPage(db, `
|
|
4665
4738
|
WITH page_roots AS (
|
|
4666
4739
|
SELECT traceId, spanId, min(timestamp) AS anchorStartedAt
|
|
4667
4740
|
FROM span_events AS ${outerAlias}
|
|
@@ -4670,14 +4743,11 @@ async function listTraceRows(db, args, reconstructSelect, mapRow, toSpans) {
|
|
|
4670
4743
|
${prefilterOrderBy}
|
|
4671
4744
|
LIMIT ? OFFSET ?
|
|
4672
4745
|
)
|
|
4673
|
-
|
|
4674
|
-
${buildOrderByClause(orderBy)}, traceId, spanId
|
|
4675
|
-
`;
|
|
4676
|
-
const spans = (await db.query(pageSql, [
|
|
4746
|
+
`, "page_roots", [
|
|
4677
4747
|
...prefilterParams,
|
|
4678
4748
|
perPage,
|
|
4679
4749
|
offset
|
|
4680
|
-
])).map((row) => mapRow(row));
|
|
4750
|
+
], reconstructSelect, `${buildOrderByClause(orderBy)}, traceId, spanId`)).map((row) => mapRow(row));
|
|
4681
4751
|
return {
|
|
4682
4752
|
pagination: {
|
|
4683
4753
|
total,
|
|
@@ -4726,20 +4796,17 @@ async function listTraceRows(db, args, reconstructSelect, mapRow, toSpans) {
|
|
|
4726
4796
|
},
|
|
4727
4797
|
spans: []
|
|
4728
4798
|
};
|
|
4729
|
-
const
|
|
4799
|
+
const spans = (await reconstructAnchoredPage(db, `
|
|
4730
4800
|
${cteSql},
|
|
4731
4801
|
page_roots AS (
|
|
4732
4802
|
SELECT traceId, spanId, startedAt AS anchorStartedAt
|
|
4733
4803
|
FROM root_spans ${postAggWhere} ${orderByClause} ${paginationClause}
|
|
4734
4804
|
)
|
|
4735
|
-
|
|
4736
|
-
${orderByClause}
|
|
4737
|
-
`;
|
|
4738
|
-
const spans = (await db.query(dataSql, [
|
|
4805
|
+
`, "page_roots", [
|
|
4739
4806
|
...prefilterParams,
|
|
4740
4807
|
...postAggParams,
|
|
4741
4808
|
...paginationParams
|
|
4742
|
-
])).map((row) => mapRow(row));
|
|
4809
|
+
], reconstructSelect, orderByClause)).map((row) => mapRow(row));
|
|
4743
4810
|
return {
|
|
4744
4811
|
pagination: {
|
|
4745
4812
|
total,
|
|
@@ -5043,7 +5110,7 @@ async function listBranches(db, args) {
|
|
|
5043
5110
|
branches: [],
|
|
5044
5111
|
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
5045
5112
|
};
|
|
5046
|
-
const
|
|
5113
|
+
const spans = (await reconstructAnchoredPage(db, `
|
|
5047
5114
|
WITH page_anchors AS (
|
|
5048
5115
|
SELECT traceId, spanId, min(timestamp) AS anchorStartedAt
|
|
5049
5116
|
FROM span_events AS ${outerAlias}
|
|
@@ -5052,14 +5119,11 @@ async function listBranches(db, args) {
|
|
|
5052
5119
|
${prefilterOrderBy}
|
|
5053
5120
|
LIMIT ? OFFSET ?
|
|
5054
5121
|
)
|
|
5055
|
-
|
|
5056
|
-
${buildOrderByClause(orderBy)}, traceId, spanId
|
|
5057
|
-
`;
|
|
5058
|
-
const spans = (await db.query(pageSql, [
|
|
5122
|
+
`, "page_anchors", [
|
|
5059
5123
|
...prefilterParams,
|
|
5060
5124
|
perPage,
|
|
5061
5125
|
offset
|
|
5062
|
-
])).map((row) => rowToSpanRecord(row));
|
|
5126
|
+
], SPAN_RECONSTRUCT_SELECT, `${buildOrderByClause(orderBy)}, traceId, spanId`)).map((row) => rowToSpanRecord(row));
|
|
5063
5127
|
return {
|
|
5064
5128
|
pagination: {
|
|
5065
5129
|
total,
|
|
@@ -5106,20 +5170,17 @@ async function listBranches(db, args) {
|
|
|
5106
5170
|
branches: [],
|
|
5107
5171
|
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
5108
5172
|
};
|
|
5109
|
-
const
|
|
5173
|
+
const spans = (await reconstructAnchoredPage(db, `
|
|
5110
5174
|
${cteSql},
|
|
5111
5175
|
page_anchors AS (
|
|
5112
5176
|
SELECT traceId, spanId, startedAt AS anchorStartedAt
|
|
5113
5177
|
FROM branch_anchors ${postAggWhere} ${orderByClause} ${paginationClause}
|
|
5114
5178
|
)
|
|
5115
|
-
|
|
5116
|
-
${orderByClause}
|
|
5117
|
-
`;
|
|
5118
|
-
const spans = (await db.query(dataSql, [
|
|
5179
|
+
`, "page_anchors", [
|
|
5119
5180
|
...prefilterParams,
|
|
5120
5181
|
...postAggParams,
|
|
5121
5182
|
...paginationParams
|
|
5122
|
-
])).map((row) => rowToSpanRecord(row));
|
|
5183
|
+
], SPAN_RECONSTRUCT_SELECT, orderByClause)).map((row) => rowToSpanRecord(row));
|
|
5123
5184
|
return {
|
|
5124
5185
|
pagination: {
|
|
5125
5186
|
total,
|
|
@@ -5543,4 +5604,4 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
|
|
|
5543
5604
|
//#endregion
|
|
5544
5605
|
exports.ObservabilityStorageDuckDB = ObservabilityStorageDuckDB;
|
|
5545
5606
|
|
|
5546
|
-
//# sourceMappingURL=observability-
|
|
5607
|
+
//# sourceMappingURL=observability-D4eFjsKJ.cjs.map
|