@mastra/duckdb 1.3.2 → 1.4.0-alpha.1
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/CHANGELOG.md +20 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +10 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/{observability-TS5QIHIC.js → observability-M35AJUGT.js} +722 -60
- package/dist/observability-M35AJUGT.js.map +1 -0
- package/dist/{observability-J6N3H7W7.cjs → observability-V2KYD7UF.cjs} +721 -59
- package/dist/observability-V2KYD7UF.cjs.map +1 -0
- package/dist/storage/domains/observability/ddl.d.ts +10 -5
- package/dist/storage/domains/observability/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/feedback.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +1 -0
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/logs.d.ts.map +1 -1
- package/dist/storage/domains/observability/metrics.d.ts.map +1 -1
- package/dist/storage/domains/observability/migration.d.ts +14 -0
- package/dist/storage/domains/observability/migration.d.ts.map +1 -1
- package/dist/storage/domains/observability/polling.d.ts +7 -0
- package/dist/storage/domains/observability/polling.d.ts.map +1 -0
- package/dist/storage/domains/observability/scores.d.ts.map +1 -1
- package/dist/storage/domains/observability/tracing.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/observability-J6N3H7W7.cjs.map +0 -1
- package/dist/observability-TS5QIHIC.js.map +0 -1
|
@@ -5,13 +5,30 @@ var error = require('@mastra/core/error');
|
|
|
5
5
|
var storage = require('@mastra/core/storage');
|
|
6
6
|
var observability = require('@mastra/core/observability');
|
|
7
7
|
var utils = require('@mastra/core/utils');
|
|
8
|
+
var features = require('@mastra/core/features');
|
|
8
9
|
|
|
9
10
|
// src/storage/domains/observability/ddl.ts
|
|
11
|
+
var SPAN_EVENTS_CURSOR_SEQUENCE_DDL = `
|
|
12
|
+
CREATE SEQUENCE IF NOT EXISTS span_events_cursor_id_seq START 1
|
|
13
|
+
`;
|
|
14
|
+
var METRIC_EVENTS_CURSOR_SEQUENCE_DDL = `
|
|
15
|
+
CREATE SEQUENCE IF NOT EXISTS metric_events_cursor_id_seq START 1
|
|
16
|
+
`;
|
|
17
|
+
var LOG_EVENTS_CURSOR_SEQUENCE_DDL = `
|
|
18
|
+
CREATE SEQUENCE IF NOT EXISTS log_events_cursor_id_seq START 1
|
|
19
|
+
`;
|
|
20
|
+
var SCORE_EVENTS_CURSOR_SEQUENCE_DDL = `
|
|
21
|
+
CREATE SEQUENCE IF NOT EXISTS score_events_cursor_id_seq START 1
|
|
22
|
+
`;
|
|
23
|
+
var FEEDBACK_EVENTS_CURSOR_SEQUENCE_DDL = `
|
|
24
|
+
CREATE SEQUENCE IF NOT EXISTS feedback_events_cursor_id_seq START 1
|
|
25
|
+
`;
|
|
10
26
|
var SPAN_EVENTS_DDL = `
|
|
11
27
|
CREATE TABLE IF NOT EXISTS span_events (
|
|
12
28
|
-- Event metadata
|
|
13
29
|
eventType VARCHAR NOT NULL,
|
|
14
30
|
timestamp TIMESTAMP NOT NULL,
|
|
31
|
+
cursorId BIGINT,
|
|
15
32
|
|
|
16
33
|
-- IDs
|
|
17
34
|
traceId VARCHAR NOT NULL,
|
|
@@ -58,6 +75,7 @@ var METRIC_EVENTS_DDL = `
|
|
|
58
75
|
CREATE TABLE IF NOT EXISTS metric_events (
|
|
59
76
|
-- Event metadata
|
|
60
77
|
timestamp TIMESTAMP NOT NULL,
|
|
78
|
+
cursorId BIGINT,
|
|
61
79
|
|
|
62
80
|
-- IDs
|
|
63
81
|
metricId VARCHAR NOT NULL PRIMARY KEY,
|
|
@@ -110,6 +128,7 @@ var LOG_EVENTS_DDL = `
|
|
|
110
128
|
CREATE TABLE IF NOT EXISTS log_events (
|
|
111
129
|
-- Event metadata
|
|
112
130
|
timestamp TIMESTAMP NOT NULL,
|
|
131
|
+
cursorId BIGINT,
|
|
113
132
|
|
|
114
133
|
-- IDs
|
|
115
134
|
logId VARCHAR NOT NULL PRIMARY KEY,
|
|
@@ -157,6 +176,7 @@ var SCORE_EVENTS_DDL = `
|
|
|
157
176
|
CREATE TABLE IF NOT EXISTS score_events (
|
|
158
177
|
-- Event metadata
|
|
159
178
|
timestamp TIMESTAMP NOT NULL,
|
|
179
|
+
cursorId BIGINT,
|
|
160
180
|
|
|
161
181
|
-- IDs
|
|
162
182
|
scoreId VARCHAR NOT NULL PRIMARY KEY,
|
|
@@ -208,6 +228,7 @@ var FEEDBACK_EVENTS_DDL = `
|
|
|
208
228
|
CREATE TABLE IF NOT EXISTS feedback_events (
|
|
209
229
|
-- Event metadata
|
|
210
230
|
timestamp TIMESTAMP NOT NULL,
|
|
231
|
+
cursorId BIGINT,
|
|
211
232
|
|
|
212
233
|
-- IDs
|
|
213
234
|
feedbackId VARCHAR NOT NULL PRIMARY KEY,
|
|
@@ -256,11 +277,34 @@ CREATE TABLE IF NOT EXISTS feedback_events (
|
|
|
256
277
|
metadata JSON,
|
|
257
278
|
scope JSON
|
|
258
279
|
)`;
|
|
259
|
-
var ALL_DDL = [
|
|
280
|
+
var ALL_DDL = [
|
|
281
|
+
SPAN_EVENTS_CURSOR_SEQUENCE_DDL,
|
|
282
|
+
METRIC_EVENTS_CURSOR_SEQUENCE_DDL,
|
|
283
|
+
LOG_EVENTS_CURSOR_SEQUENCE_DDL,
|
|
284
|
+
SCORE_EVENTS_CURSOR_SEQUENCE_DDL,
|
|
285
|
+
FEEDBACK_EVENTS_CURSOR_SEQUENCE_DDL,
|
|
286
|
+
SPAN_EVENTS_DDL,
|
|
287
|
+
METRIC_EVENTS_DDL,
|
|
288
|
+
LOG_EVENTS_DDL,
|
|
289
|
+
SCORE_EVENTS_DDL,
|
|
290
|
+
FEEDBACK_EVENTS_DDL
|
|
291
|
+
];
|
|
260
292
|
var ALL_MIGRATIONS = [
|
|
261
|
-
|
|
293
|
+
`CREATE SEQUENCE IF NOT EXISTS span_events_cursor_id_seq START 1`,
|
|
294
|
+
`CREATE SEQUENCE IF NOT EXISTS metric_events_cursor_id_seq START 1`,
|
|
295
|
+
`CREATE SEQUENCE IF NOT EXISTS log_events_cursor_id_seq START 1`,
|
|
296
|
+
`CREATE SEQUENCE IF NOT EXISTS score_events_cursor_id_seq START 1`,
|
|
297
|
+
`CREATE SEQUENCE IF NOT EXISTS feedback_events_cursor_id_seq START 1`,
|
|
298
|
+
// Existing rows intentionally keep NULL cursorId values; delta polling only
|
|
299
|
+
// applies to rows written by insert paths that explicitly call nextval().
|
|
300
|
+
// Databases upgraded from a prior version may still carry a
|
|
301
|
+
// `DEFAULT nextval(...)` on cursorId, which breaks DuckDB WAL replay; that
|
|
302
|
+
// remediation lives in `dropLegacyCursorIdDefaults` and only runs when the
|
|
303
|
+
// bad default is detected in information_schema.
|
|
304
|
+
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
|
|
262
305
|
`ALTER TABLE span_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
|
|
263
|
-
// Metrics
|
|
306
|
+
// Metrics. Legacy rows remain page-visible but are not part of delta polling.
|
|
307
|
+
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
|
|
264
308
|
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
|
|
265
309
|
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
|
|
266
310
|
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
|
|
@@ -284,7 +328,8 @@ var ALL_MIGRATIONS = [
|
|
|
284
328
|
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS costMetadata JSON`,
|
|
285
329
|
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS metadata JSON`,
|
|
286
330
|
`ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS scope JSON`,
|
|
287
|
-
// Logs
|
|
331
|
+
// Logs. Legacy rows remain page-visible but are not part of delta polling.
|
|
332
|
+
`ALTER TABLE log_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
|
|
288
333
|
`ALTER TABLE log_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
|
|
289
334
|
`ALTER TABLE log_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
|
|
290
335
|
`ALTER TABLE log_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
|
|
@@ -308,7 +353,8 @@ var ALL_MIGRATIONS = [
|
|
|
308
353
|
`ALTER TABLE log_events ADD COLUMN IF NOT EXISTS tags JSON`,
|
|
309
354
|
`ALTER TABLE log_events ADD COLUMN IF NOT EXISTS metadata JSON`,
|
|
310
355
|
`ALTER TABLE log_events ADD COLUMN IF NOT EXISTS scope JSON`,
|
|
311
|
-
// Scores
|
|
356
|
+
// Scores. Legacy rows remain page-visible but are not part of delta polling.
|
|
357
|
+
`ALTER TABLE score_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
|
|
312
358
|
`ALTER TABLE score_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
|
|
313
359
|
`ALTER TABLE score_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
|
|
314
360
|
`ALTER TABLE score_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
|
|
@@ -336,7 +382,8 @@ var ALL_MIGRATIONS = [
|
|
|
336
382
|
`ALTER TABLE score_events ADD COLUMN IF NOT EXISTS source VARCHAR`,
|
|
337
383
|
`ALTER TABLE score_events ADD COLUMN IF NOT EXISTS scoreSource VARCHAR`,
|
|
338
384
|
`ALTER TABLE score_events ALTER COLUMN traceId DROP NOT NULL`,
|
|
339
|
-
// Feedback
|
|
385
|
+
// Feedback. Legacy rows remain page-visible but are not part of delta polling.
|
|
386
|
+
`ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
|
|
340
387
|
`ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
|
|
341
388
|
`ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
|
|
342
389
|
`ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
|
|
@@ -600,6 +647,45 @@ function parseJsonArray(value) {
|
|
|
600
647
|
const parsed = parseJson(value);
|
|
601
648
|
return Array.isArray(parsed) ? parsed : null;
|
|
602
649
|
}
|
|
650
|
+
var OBSERVABILITY_DELTA_POLLING_FEATURE = "observability-delta-polling";
|
|
651
|
+
function deltaPollingFeatureEnabled() {
|
|
652
|
+
return features.coreFeatures.has(OBSERVABILITY_DELTA_POLLING_FEATURE);
|
|
653
|
+
}
|
|
654
|
+
function assertDeltaPollingEnabled() {
|
|
655
|
+
if (deltaPollingFeatureEnabled()) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
throw new error.MastraError({
|
|
659
|
+
id: "OBSERVABILITY_DELTA_POLLING_NOT_SUPPORTED",
|
|
660
|
+
domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
|
|
661
|
+
category: error.ErrorCategory.SYSTEM,
|
|
662
|
+
text: "This storage provider does not support observability delta polling"
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
function encodeDeltaCursor(value) {
|
|
666
|
+
return String(value ?? 0);
|
|
667
|
+
}
|
|
668
|
+
function validateCursorId(cursor) {
|
|
669
|
+
if (!/^\d+$/.test(cursor)) {
|
|
670
|
+
throw new error.MastraError({
|
|
671
|
+
id: "OBSERVABILITY_INVALID_DELTA_CURSOR",
|
|
672
|
+
domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
|
|
673
|
+
category: error.ErrorCategory.USER,
|
|
674
|
+
text: "Invalid observability delta cursor"
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
return cursor;
|
|
678
|
+
}
|
|
679
|
+
function extendWhereClause(baseClause, extraConditions) {
|
|
680
|
+
const conditions = extraConditions.filter(Boolean);
|
|
681
|
+
if (conditions.length === 0) {
|
|
682
|
+
return baseClause;
|
|
683
|
+
}
|
|
684
|
+
if (!baseClause) {
|
|
685
|
+
return `WHERE ${conditions.join(" AND ")}`;
|
|
686
|
+
}
|
|
687
|
+
return `${baseClause} AND ${conditions.join(" AND ")}`;
|
|
688
|
+
}
|
|
603
689
|
|
|
604
690
|
// src/storage/domains/observability/feedback.ts
|
|
605
691
|
var FEEDBACK_GROUP_BY_COLUMNS = /* @__PURE__ */ new Set([
|
|
@@ -796,7 +882,7 @@ async function createFeedback(db, args) {
|
|
|
796
882
|
const feedbackUserId = f.feedbackUserId ?? f.userId ?? null;
|
|
797
883
|
await db.execute(
|
|
798
884
|
`INSERT INTO feedback_events (
|
|
799
|
-
feedbackId, timestamp, traceId, spanId, experimentId,
|
|
885
|
+
feedbackId, timestamp, cursorId, traceId, spanId, experimentId,
|
|
800
886
|
entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
|
|
801
887
|
userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
|
|
802
888
|
feedbackUserId, sourceId, feedbackSource, feedbackType, value, comment, tags, metadata, scope
|
|
@@ -804,6 +890,7 @@ async function createFeedback(db, args) {
|
|
|
804
890
|
VALUES (${[
|
|
805
891
|
v(f.feedbackId),
|
|
806
892
|
v(f.timestamp),
|
|
893
|
+
"nextval('feedback_events_cursor_id_seq')",
|
|
807
894
|
v(f.traceId),
|
|
808
895
|
v(f.spanId ?? null),
|
|
809
896
|
v(f.experimentId ?? null),
|
|
@@ -851,6 +938,7 @@ async function batchCreateFeedback(db, args) {
|
|
|
851
938
|
return `(${[
|
|
852
939
|
v(legacyFeedback.feedbackId),
|
|
853
940
|
v(legacyFeedback.timestamp),
|
|
941
|
+
"nextval('feedback_events_cursor_id_seq')",
|
|
854
942
|
v(legacyFeedback.traceId),
|
|
855
943
|
v(legacyFeedback.spanId ?? null),
|
|
856
944
|
v(legacyFeedback.experimentId ?? null),
|
|
@@ -889,7 +977,7 @@ async function batchCreateFeedback(db, args) {
|
|
|
889
977
|
});
|
|
890
978
|
await db.execute(
|
|
891
979
|
`INSERT INTO feedback_events (
|
|
892
|
-
feedbackId, timestamp, traceId, spanId, experimentId,
|
|
980
|
+
feedbackId, timestamp, cursorId, traceId, spanId, experimentId,
|
|
893
981
|
entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
|
|
894
982
|
userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
|
|
895
983
|
feedbackUserId, sourceId, feedbackSource, feedbackType, value, comment, tags, metadata, scope
|
|
@@ -899,15 +987,41 @@ async function batchCreateFeedback(db, args) {
|
|
|
899
987
|
);
|
|
900
988
|
}
|
|
901
989
|
async function listFeedback(db, args) {
|
|
902
|
-
const
|
|
903
|
-
const page = Number(
|
|
904
|
-
const perPage = Number(
|
|
905
|
-
const orderBy = { field: args.orderBy?.field ?? "timestamp", direction: args.orderBy?.direction ?? "DESC" };
|
|
990
|
+
const { mode, filters, pagination, orderBy, after, limit } = storage.listFeedbackArgsSchema.parse(args);
|
|
991
|
+
const page = Number(pagination.page);
|
|
992
|
+
const perPage = Number(pagination.perPage);
|
|
906
993
|
const { clause: filterClause, params: filterParams } = buildWhereClause(filters, {
|
|
907
994
|
source: "feedbackSource"
|
|
908
995
|
});
|
|
996
|
+
if (mode === "delta") {
|
|
997
|
+
assertDeltaPollingEnabled();
|
|
998
|
+
const streamHeadCursor = await getStreamHeadCursor(db);
|
|
999
|
+
if (after === void 0) {
|
|
1000
|
+
return {
|
|
1001
|
+
feedback: [],
|
|
1002
|
+
delta: { limit, hasMore: false },
|
|
1003
|
+
deltaCursor: streamHeadCursor
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
const afterCursorId = validateCursorId(after);
|
|
1007
|
+
const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
|
|
1008
|
+
const rows2 = await db.query(
|
|
1009
|
+
`SELECT * FROM feedback_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
|
|
1010
|
+
[...filterParams, afterCursorId, limit + 1]
|
|
1011
|
+
);
|
|
1012
|
+
const visibleRows = rows2.slice(0, limit).map((row) => ({
|
|
1013
|
+
cursorId: row.cursorId,
|
|
1014
|
+
feedback: rowToFeedbackRecord(row)
|
|
1015
|
+
}));
|
|
1016
|
+
return {
|
|
1017
|
+
feedback: visibleRows.map((row) => row.feedback),
|
|
1018
|
+
delta: { limit, hasMore: rows2.length > limit },
|
|
1019
|
+
deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
909
1022
|
const orderByClause = buildOrderByClause(orderBy);
|
|
910
1023
|
const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
|
|
1024
|
+
const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor(db, filterClause, filterParams) : void 0;
|
|
911
1025
|
const countResult = await db.query(
|
|
912
1026
|
`SELECT COUNT(*) as total FROM feedback_events ${filterClause}`,
|
|
913
1027
|
filterParams
|
|
@@ -919,9 +1033,26 @@ async function listFeedback(db, args) {
|
|
|
919
1033
|
);
|
|
920
1034
|
return {
|
|
921
1035
|
pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
|
|
922
|
-
feedback: rows.map((row) => rowToFeedbackRecord(row))
|
|
1036
|
+
feedback: rows.map((row) => rowToFeedbackRecord(row)),
|
|
1037
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
923
1038
|
};
|
|
924
1039
|
}
|
|
1040
|
+
async function getDeltaCursor(db, filterClause, filterParams) {
|
|
1041
|
+
const rows = await db.query(
|
|
1042
|
+
`SELECT max(cursorId) AS cursorId FROM feedback_events ${filterClause}`,
|
|
1043
|
+
filterParams
|
|
1044
|
+
);
|
|
1045
|
+
const cursorId = rows[0]?.cursorId;
|
|
1046
|
+
if (cursorId !== null && cursorId !== void 0) {
|
|
1047
|
+
return encodeDeltaCursor(cursorId);
|
|
1048
|
+
}
|
|
1049
|
+
const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM feedback_events`);
|
|
1050
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
1051
|
+
}
|
|
1052
|
+
async function getStreamHeadCursor(db) {
|
|
1053
|
+
const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM feedback_events`);
|
|
1054
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
1055
|
+
}
|
|
925
1056
|
async function getFeedbackAggregate(db, args) {
|
|
926
1057
|
const aggSql = getAggregationSql(args.aggregation);
|
|
927
1058
|
const { clause, params } = buildFeedbackWhereClause(args, true);
|
|
@@ -1049,11 +1180,10 @@ async function getFeedbackPercentiles(db, args) {
|
|
|
1049
1180
|
}
|
|
1050
1181
|
return { series };
|
|
1051
1182
|
}
|
|
1052
|
-
|
|
1053
|
-
// src/storage/domains/observability/logs.ts
|
|
1054
1183
|
var COLUMNS = [
|
|
1055
1184
|
"logId",
|
|
1056
1185
|
"timestamp",
|
|
1186
|
+
"cursorId",
|
|
1057
1187
|
"level",
|
|
1058
1188
|
"message",
|
|
1059
1189
|
"data",
|
|
@@ -1130,6 +1260,7 @@ async function batchCreateLogs(db, args) {
|
|
|
1130
1260
|
return `(${[
|
|
1131
1261
|
v(log.logId),
|
|
1132
1262
|
v(log.timestamp),
|
|
1263
|
+
"nextval('log_events_cursor_id_seq')",
|
|
1133
1264
|
v(log.level),
|
|
1134
1265
|
v(log.message),
|
|
1135
1266
|
jsonV(log.data),
|
|
@@ -1166,13 +1297,40 @@ async function batchCreateLogs(db, args) {
|
|
|
1166
1297
|
await db.execute(`INSERT INTO log_events (${COLUMNS_SQL}) VALUES ${tuples.join(",\n")} ON CONFLICT DO NOTHING`);
|
|
1167
1298
|
}
|
|
1168
1299
|
async function listLogs(db, args) {
|
|
1169
|
-
const
|
|
1170
|
-
const
|
|
1171
|
-
const
|
|
1172
|
-
const
|
|
1173
|
-
const { clause: filterClause, params: filterParams } = buildWhereClause(
|
|
1300
|
+
const { mode, filters, pagination, orderBy, after, limit } = storage.listLogsArgsSchema.parse(args);
|
|
1301
|
+
const filterRecord = filters;
|
|
1302
|
+
const page = Number(pagination.page);
|
|
1303
|
+
const perPage = Number(pagination.perPage);
|
|
1304
|
+
const { clause: filterClause, params: filterParams } = buildWhereClause(filterRecord);
|
|
1305
|
+
if (mode === "delta") {
|
|
1306
|
+
assertDeltaPollingEnabled();
|
|
1307
|
+
const streamHeadCursor = await getStreamHeadCursor2(db);
|
|
1308
|
+
if (after === void 0) {
|
|
1309
|
+
return {
|
|
1310
|
+
logs: [],
|
|
1311
|
+
delta: { limit, hasMore: false },
|
|
1312
|
+
deltaCursor: streamHeadCursor
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
const afterCursorId = validateCursorId(after);
|
|
1316
|
+
const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
|
|
1317
|
+
const rows2 = await db.query(
|
|
1318
|
+
`SELECT * FROM log_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
|
|
1319
|
+
[...filterParams, afterCursorId, limit + 1]
|
|
1320
|
+
);
|
|
1321
|
+
const visibleRows = rows2.slice(0, limit).map((row) => ({
|
|
1322
|
+
cursorId: row.cursorId,
|
|
1323
|
+
log: rowToLogRecord(row)
|
|
1324
|
+
}));
|
|
1325
|
+
return {
|
|
1326
|
+
logs: visibleRows.map((row) => row.log),
|
|
1327
|
+
delta: { limit, hasMore: rows2.length > limit },
|
|
1328
|
+
deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1174
1331
|
const orderByClause = buildOrderByClause(orderBy);
|
|
1175
1332
|
const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
|
|
1333
|
+
const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor2(db, filterClause, filterParams) : void 0;
|
|
1176
1334
|
const countResult = await db.query(
|
|
1177
1335
|
`SELECT COUNT(*) as total FROM log_events ${filterClause}`,
|
|
1178
1336
|
filterParams
|
|
@@ -1185,9 +1343,26 @@ async function listLogs(db, args) {
|
|
|
1185
1343
|
const logs = rows.map((row) => rowToLogRecord(row));
|
|
1186
1344
|
return {
|
|
1187
1345
|
pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
|
|
1188
|
-
logs
|
|
1346
|
+
logs,
|
|
1347
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
1189
1348
|
};
|
|
1190
1349
|
}
|
|
1350
|
+
async function getDeltaCursor2(db, filterClause, filterParams) {
|
|
1351
|
+
const rows = await db.query(
|
|
1352
|
+
`SELECT max(cursorId) AS cursorId FROM log_events ${filterClause}`,
|
|
1353
|
+
filterParams
|
|
1354
|
+
);
|
|
1355
|
+
const cursorId = rows[0]?.cursorId;
|
|
1356
|
+
if (cursorId !== null && cursorId !== void 0) {
|
|
1357
|
+
return encodeDeltaCursor(cursorId);
|
|
1358
|
+
}
|
|
1359
|
+
const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM log_events`);
|
|
1360
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
1361
|
+
}
|
|
1362
|
+
async function getStreamHeadCursor2(db) {
|
|
1363
|
+
const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM log_events`);
|
|
1364
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
1365
|
+
}
|
|
1191
1366
|
function resolveDistinctColumnSql(distinctColumn) {
|
|
1192
1367
|
if (!distinctColumn) {
|
|
1193
1368
|
throw new Error(`count_distinct aggregation requires a 'distinctColumn' argument`);
|
|
@@ -1244,6 +1419,7 @@ function buildMetricNameFilter(name) {
|
|
|
1244
1419
|
var METRIC_COLUMNS = [
|
|
1245
1420
|
"metricId",
|
|
1246
1421
|
"timestamp",
|
|
1422
|
+
"cursorId",
|
|
1247
1423
|
"name",
|
|
1248
1424
|
"value",
|
|
1249
1425
|
"traceId",
|
|
@@ -1392,6 +1568,7 @@ async function batchCreateMetrics(db, args) {
|
|
|
1392
1568
|
return `(${[
|
|
1393
1569
|
v(m.metricId),
|
|
1394
1570
|
v(m.timestamp),
|
|
1571
|
+
"nextval('metric_events_cursor_id_seq')",
|
|
1395
1572
|
v(m.name),
|
|
1396
1573
|
v(m.value),
|
|
1397
1574
|
v(m.traceId ?? null),
|
|
@@ -1435,13 +1612,40 @@ async function batchCreateMetrics(db, args) {
|
|
|
1435
1612
|
);
|
|
1436
1613
|
}
|
|
1437
1614
|
async function listMetrics(db, args) {
|
|
1438
|
-
const
|
|
1439
|
-
const
|
|
1440
|
-
const
|
|
1441
|
-
const
|
|
1442
|
-
const { clause: filterClause, params: filterParams } = buildWhereClause(
|
|
1615
|
+
const { mode, filters, pagination, orderBy, after, limit } = storage.listMetricsArgsSchema.parse(args);
|
|
1616
|
+
const filterRecord = filters;
|
|
1617
|
+
const page = Number(pagination.page);
|
|
1618
|
+
const perPage = Number(pagination.perPage);
|
|
1619
|
+
const { clause: filterClause, params: filterParams } = buildWhereClause(filterRecord);
|
|
1620
|
+
if (mode === "delta") {
|
|
1621
|
+
assertDeltaPollingEnabled();
|
|
1622
|
+
const streamHeadCursor = await getStreamHeadCursor3(db);
|
|
1623
|
+
if (after === void 0) {
|
|
1624
|
+
return {
|
|
1625
|
+
metrics: [],
|
|
1626
|
+
delta: { limit, hasMore: false },
|
|
1627
|
+
deltaCursor: streamHeadCursor
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
const afterCursorId = validateCursorId(after);
|
|
1631
|
+
const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
|
|
1632
|
+
const rows2 = await db.query(
|
|
1633
|
+
`SELECT * FROM metric_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
|
|
1634
|
+
[...filterParams, afterCursorId, limit + 1]
|
|
1635
|
+
);
|
|
1636
|
+
const visibleRows = rows2.slice(0, limit).map((row) => ({
|
|
1637
|
+
cursorId: row.cursorId,
|
|
1638
|
+
metric: rowToMetricRecord(row)
|
|
1639
|
+
}));
|
|
1640
|
+
return {
|
|
1641
|
+
metrics: visibleRows.map((row) => row.metric),
|
|
1642
|
+
delta: { limit, hasMore: rows2.length > limit },
|
|
1643
|
+
deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1443
1646
|
const orderByClause = buildOrderByClause(orderBy);
|
|
1444
1647
|
const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
|
|
1648
|
+
const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor3(db, filterClause, filterParams) : void 0;
|
|
1445
1649
|
const countResult = await db.query(
|
|
1446
1650
|
`SELECT COUNT(*) AS total FROM metric_events ${filterClause}`,
|
|
1447
1651
|
filterParams
|
|
@@ -1453,9 +1657,26 @@ async function listMetrics(db, args) {
|
|
|
1453
1657
|
);
|
|
1454
1658
|
return {
|
|
1455
1659
|
pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
|
|
1456
|
-
metrics: rows.map((row) => rowToMetricRecord(row))
|
|
1660
|
+
metrics: rows.map((row) => rowToMetricRecord(row)),
|
|
1661
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
1457
1662
|
};
|
|
1458
1663
|
}
|
|
1664
|
+
async function getDeltaCursor3(db, filterClause, filterParams) {
|
|
1665
|
+
const rows = await db.query(
|
|
1666
|
+
`SELECT max(cursorId) AS cursorId FROM metric_events ${filterClause}`,
|
|
1667
|
+
filterParams
|
|
1668
|
+
);
|
|
1669
|
+
const cursorId = rows[0]?.cursorId;
|
|
1670
|
+
if (cursorId !== null && cursorId !== void 0) {
|
|
1671
|
+
return encodeDeltaCursor(cursorId);
|
|
1672
|
+
}
|
|
1673
|
+
const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM metric_events`);
|
|
1674
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
1675
|
+
}
|
|
1676
|
+
async function getStreamHeadCursor3(db) {
|
|
1677
|
+
const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM metric_events`);
|
|
1678
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
1679
|
+
}
|
|
1459
1680
|
async function getMetricAggregate(db, args) {
|
|
1460
1681
|
const aggSql = getAggregationSql2(args.aggregation, "value", args.distinctColumn);
|
|
1461
1682
|
const { clause: nameClause, params: nameParams } = buildMetricNameFilter(args.name);
|
|
@@ -1734,11 +1955,42 @@ async function getMetricLabelValues(db, args) {
|
|
|
1734
1955
|
);
|
|
1735
1956
|
return { values: rows.map((r) => r.val) };
|
|
1736
1957
|
}
|
|
1958
|
+
var CURSOR_ID_TABLES = ["span_events", "metric_events", "log_events", "score_events", "feedback_events"];
|
|
1959
|
+
async function dropLegacyCursorIdDefaults(db) {
|
|
1960
|
+
const rows = await db.query(
|
|
1961
|
+
`SELECT table_name FROM information_schema.columns
|
|
1962
|
+
WHERE column_name = 'cursorId'
|
|
1963
|
+
AND column_default IS NOT NULL
|
|
1964
|
+
AND table_name IN (${CURSOR_ID_TABLES.map((t) => `'${t}'`).join(", ")})`
|
|
1965
|
+
);
|
|
1966
|
+
if (rows.length === 0) return;
|
|
1967
|
+
await db.executeBatch(rows.map((row) => `ALTER TABLE ${row.table_name} ALTER COLUMN cursorId DROP DEFAULT`));
|
|
1968
|
+
}
|
|
1737
1969
|
var SIGNAL_MIGRATIONS = [
|
|
1738
|
-
{
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1970
|
+
{
|
|
1971
|
+
table: "metric_events",
|
|
1972
|
+
createDDL: METRIC_EVENTS_DDL,
|
|
1973
|
+
idColumn: "metricId",
|
|
1974
|
+
cursorSequenceDDL: METRIC_EVENTS_CURSOR_SEQUENCE_DDL
|
|
1975
|
+
},
|
|
1976
|
+
{
|
|
1977
|
+
table: "log_events",
|
|
1978
|
+
createDDL: LOG_EVENTS_DDL,
|
|
1979
|
+
idColumn: "logId",
|
|
1980
|
+
cursorSequenceDDL: LOG_EVENTS_CURSOR_SEQUENCE_DDL
|
|
1981
|
+
},
|
|
1982
|
+
{
|
|
1983
|
+
table: "score_events",
|
|
1984
|
+
createDDL: SCORE_EVENTS_DDL,
|
|
1985
|
+
idColumn: "scoreId",
|
|
1986
|
+
cursorSequenceDDL: SCORE_EVENTS_CURSOR_SEQUENCE_DDL
|
|
1987
|
+
},
|
|
1988
|
+
{
|
|
1989
|
+
table: "feedback_events",
|
|
1990
|
+
createDDL: FEEDBACK_EVENTS_DDL,
|
|
1991
|
+
idColumn: "feedbackId",
|
|
1992
|
+
cursorSequenceDDL: FEEDBACK_EVENTS_CURSOR_SEQUENCE_DDL
|
|
1993
|
+
}
|
|
1742
1994
|
];
|
|
1743
1995
|
async function tableExists(db, table) {
|
|
1744
1996
|
const rows = await db.query(
|
|
@@ -1798,7 +2050,7 @@ async function checkSignalTablesMigrationStatus(db) {
|
|
|
1798
2050
|
};
|
|
1799
2051
|
}
|
|
1800
2052
|
async function migrateSignalTables(db, logger) {
|
|
1801
|
-
for (const { table, createDDL, idColumn } of SIGNAL_MIGRATIONS) {
|
|
2053
|
+
for (const { table, createDDL, idColumn, cursorSequenceDDL } of SIGNAL_MIGRATIONS) {
|
|
1802
2054
|
if (!await tableExists(db, table)) continue;
|
|
1803
2055
|
if (await hasPrimaryKey(db, table)) continue;
|
|
1804
2056
|
logger?.info?.(`Migrating ${table} to schema with ${idColumn} PRIMARY KEY`);
|
|
@@ -1807,6 +2059,7 @@ async function migrateSignalTables(db, logger) {
|
|
|
1807
2059
|
let originalRenamed = false;
|
|
1808
2060
|
let swapCompleted = false;
|
|
1809
2061
|
try {
|
|
2062
|
+
await db.execute(cursorSequenceDDL);
|
|
1810
2063
|
await db.execute(buildTemporaryTableDDL(createDDL, table, temp));
|
|
1811
2064
|
const newColumns = await getColumns(db, temp);
|
|
1812
2065
|
const currentColumns = new Set(await getColumns(db, table));
|
|
@@ -2036,7 +2289,7 @@ async function createScore(db, args) {
|
|
|
2036
2289
|
const scoreSource = s.scoreSource ?? s.source ?? null;
|
|
2037
2290
|
await db.execute(
|
|
2038
2291
|
`INSERT INTO score_events (
|
|
2039
|
-
scoreId, timestamp, traceId, spanId, experimentId, scoreTraceId,
|
|
2292
|
+
scoreId, timestamp, cursorId, traceId, spanId, experimentId, scoreTraceId,
|
|
2040
2293
|
entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
|
|
2041
2294
|
userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
|
|
2042
2295
|
scorerId, scorerVersion, scoreSource, score, reason, tags, metadata, scope
|
|
@@ -2044,6 +2297,7 @@ async function createScore(db, args) {
|
|
|
2044
2297
|
VALUES (${[
|
|
2045
2298
|
v(s.scoreId),
|
|
2046
2299
|
v(s.timestamp),
|
|
2300
|
+
"nextval('score_events_cursor_id_seq')",
|
|
2047
2301
|
v(s.traceId),
|
|
2048
2302
|
v(s.spanId ?? null),
|
|
2049
2303
|
v(s.experimentId ?? null),
|
|
@@ -2090,6 +2344,7 @@ async function batchCreateScores(db, args) {
|
|
|
2090
2344
|
return `(${[
|
|
2091
2345
|
v(legacyScore.scoreId),
|
|
2092
2346
|
v(legacyScore.timestamp),
|
|
2347
|
+
"nextval('score_events_cursor_id_seq')",
|
|
2093
2348
|
v(legacyScore.traceId),
|
|
2094
2349
|
v(legacyScore.spanId ?? null),
|
|
2095
2350
|
v(legacyScore.experimentId ?? null),
|
|
@@ -2128,7 +2383,7 @@ async function batchCreateScores(db, args) {
|
|
|
2128
2383
|
});
|
|
2129
2384
|
await db.execute(
|
|
2130
2385
|
`INSERT INTO score_events (
|
|
2131
|
-
scoreId, timestamp, traceId, spanId, experimentId, scoreTraceId,
|
|
2386
|
+
scoreId, timestamp, cursorId, traceId, spanId, experimentId, scoreTraceId,
|
|
2132
2387
|
entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
|
|
2133
2388
|
userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
|
|
2134
2389
|
scorerId, scorerVersion, scoreSource, score, reason, tags, metadata, scope
|
|
@@ -2138,15 +2393,41 @@ async function batchCreateScores(db, args) {
|
|
|
2138
2393
|
);
|
|
2139
2394
|
}
|
|
2140
2395
|
async function listScores(db, args) {
|
|
2141
|
-
const
|
|
2142
|
-
const page = Number(
|
|
2143
|
-
const perPage = Number(
|
|
2144
|
-
const orderBy = { field: args.orderBy?.field ?? "timestamp", direction: args.orderBy?.direction ?? "DESC" };
|
|
2396
|
+
const { mode, filters, pagination, orderBy, after, limit } = storage.listScoresArgsSchema.parse(args);
|
|
2397
|
+
const page = Number(pagination.page);
|
|
2398
|
+
const perPage = Number(pagination.perPage);
|
|
2145
2399
|
const { clause: filterClause, params: filterParams } = buildWhereClause(filters, {
|
|
2146
2400
|
source: "scoreSource"
|
|
2147
2401
|
});
|
|
2402
|
+
if (mode === "delta") {
|
|
2403
|
+
assertDeltaPollingEnabled();
|
|
2404
|
+
const streamHeadCursor = await getStreamHeadCursor4(db);
|
|
2405
|
+
if (after === void 0) {
|
|
2406
|
+
return {
|
|
2407
|
+
scores: [],
|
|
2408
|
+
delta: { limit, hasMore: false },
|
|
2409
|
+
deltaCursor: streamHeadCursor
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
const afterCursorId = validateCursorId(after);
|
|
2413
|
+
const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
|
|
2414
|
+
const rows2 = await db.query(
|
|
2415
|
+
`SELECT * FROM score_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
|
|
2416
|
+
[...filterParams, afterCursorId, limit + 1]
|
|
2417
|
+
);
|
|
2418
|
+
const visibleRows = rows2.slice(0, limit).map((row) => ({
|
|
2419
|
+
cursorId: row.cursorId,
|
|
2420
|
+
score: rowToScoreRecord(row)
|
|
2421
|
+
}));
|
|
2422
|
+
return {
|
|
2423
|
+
scores: visibleRows.map((row) => row.score),
|
|
2424
|
+
delta: { limit, hasMore: rows2.length > limit },
|
|
2425
|
+
deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2148
2428
|
const orderByClause = buildOrderByClause(orderBy);
|
|
2149
2429
|
const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
|
|
2430
|
+
const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor4(db, filterClause, filterParams) : void 0;
|
|
2150
2431
|
const countResult = await db.query(
|
|
2151
2432
|
`SELECT COUNT(*) as total FROM score_events ${filterClause}`,
|
|
2152
2433
|
filterParams
|
|
@@ -2158,9 +2439,26 @@ async function listScores(db, args) {
|
|
|
2158
2439
|
);
|
|
2159
2440
|
return {
|
|
2160
2441
|
pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
|
|
2161
|
-
scores: rows.map((row) => rowToScoreRecord(row))
|
|
2442
|
+
scores: rows.map((row) => rowToScoreRecord(row)),
|
|
2443
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
2162
2444
|
};
|
|
2163
2445
|
}
|
|
2446
|
+
async function getDeltaCursor4(db, filterClause, filterParams) {
|
|
2447
|
+
const rows = await db.query(
|
|
2448
|
+
`SELECT max(cursorId) AS cursorId FROM score_events ${filterClause}`,
|
|
2449
|
+
filterParams
|
|
2450
|
+
);
|
|
2451
|
+
const cursorId = rows[0]?.cursorId;
|
|
2452
|
+
if (cursorId !== null && cursorId !== void 0) {
|
|
2453
|
+
return encodeDeltaCursor(cursorId);
|
|
2454
|
+
}
|
|
2455
|
+
const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM score_events`);
|
|
2456
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
2457
|
+
}
|
|
2458
|
+
async function getStreamHeadCursor4(db) {
|
|
2459
|
+
const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM score_events`);
|
|
2460
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
2461
|
+
}
|
|
2164
2462
|
async function getScoreById(db, scoreId) {
|
|
2165
2463
|
const rows = await db.query(`SELECT * FROM score_events WHERE scoreId = ? LIMIT 1`, [
|
|
2166
2464
|
scoreId
|
|
@@ -2293,6 +2591,7 @@ async function getScorePercentiles(db, args) {
|
|
|
2293
2591
|
var COLUMNS2 = [
|
|
2294
2592
|
"eventType",
|
|
2295
2593
|
"timestamp",
|
|
2594
|
+
"cursorId",
|
|
2296
2595
|
"traceId",
|
|
2297
2596
|
"spanId",
|
|
2298
2597
|
"parentSpanId",
|
|
@@ -2525,6 +2824,7 @@ function toValuesTuple(row) {
|
|
|
2525
2824
|
return [
|
|
2526
2825
|
v(row.eventType),
|
|
2527
2826
|
v(row.timestamp),
|
|
2827
|
+
"nextval('span_events_cursor_id_seq')",
|
|
2528
2828
|
v(row.traceId),
|
|
2529
2829
|
v(row.spanId),
|
|
2530
2830
|
v(row.parentSpanId),
|
|
@@ -2779,13 +3079,152 @@ async function listTraceRows(db, args, reconstructSelect, mapRow, toSpans) {
|
|
|
2779
3079
|
};
|
|
2780
3080
|
}
|
|
2781
3081
|
async function listTraces(db, args) {
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
(
|
|
2788
|
-
|
|
3082
|
+
const { mode, filters, pagination, orderBy, after, limit } = storage.listTracesArgsSchema.parse(args);
|
|
3083
|
+
const filterRecord = filters ?? {};
|
|
3084
|
+
const page = Number(pagination.page);
|
|
3085
|
+
const perPage = Number(pagination.perPage);
|
|
3086
|
+
if (mode === "delta") {
|
|
3087
|
+
assertDeltaPollingEnabled();
|
|
3088
|
+
const streamHeadCursor = await getTraceStreamHeadCursor(db);
|
|
3089
|
+
if (after === void 0) {
|
|
3090
|
+
return {
|
|
3091
|
+
spans: [],
|
|
3092
|
+
delta: { limit, hasMore: false },
|
|
3093
|
+
deltaCursor: streamHeadCursor
|
|
3094
|
+
};
|
|
3095
|
+
}
|
|
3096
|
+
const afterCursorId = validateCursorId(after);
|
|
3097
|
+
const { prefilter: prefilter2, postAgg: postAgg2, hasChildError: hasChildError2 } = partitionAnchorFilters(filterRecord);
|
|
3098
|
+
const { clause: prefilterClause2, params: prefilterParams2 } = buildWhereClause(prefilter2);
|
|
3099
|
+
const prefilterParts2 = [
|
|
3100
|
+
`eventType = 'start'`,
|
|
3101
|
+
`parentSpanId IS NULL`,
|
|
3102
|
+
`cursorId IS NOT NULL`,
|
|
3103
|
+
`cursorId > CAST(? AS BIGINT)`
|
|
3104
|
+
];
|
|
3105
|
+
if (prefilterClause2) prefilterParts2.push(prefilterClause2.replace(/^WHERE\s+/i, ""));
|
|
3106
|
+
const prefilterWhere2 = `WHERE ${prefilterParts2.join(" AND ")}`;
|
|
3107
|
+
const { clause: postAggClause2, params: postAggParams2 } = buildWhereClause(postAgg2);
|
|
3108
|
+
const postAggParts2 = [];
|
|
3109
|
+
if (postAggClause2) postAggParts2.push(postAggClause2.replace(/^WHERE\s+/i, ""));
|
|
3110
|
+
const childErrorClause2 = buildHasChildErrorClause(hasChildError2, "root_spans");
|
|
3111
|
+
if (childErrorClause2) postAggParts2.push(childErrorClause2);
|
|
3112
|
+
const postAggWhere2 = postAggParts2.length > 0 ? `WHERE ${postAggParts2.join(" AND ")}` : "";
|
|
3113
|
+
const outerAlias2 = "outer_root";
|
|
3114
|
+
const dataSql2 = `
|
|
3115
|
+
WITH candidate_roots AS (
|
|
3116
|
+
SELECT traceId, spanId, cursorId
|
|
3117
|
+
FROM span_events AS ${outerAlias2}
|
|
3118
|
+
${prefilterWhere2}
|
|
3119
|
+
),
|
|
3120
|
+
root_spans AS (
|
|
3121
|
+
SELECT reconstructed.*, candidate_roots.cursorId AS anchorCursorId
|
|
3122
|
+
FROM (
|
|
3123
|
+
${SPAN_RECONSTRUCT_SELECT}
|
|
3124
|
+
WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
|
|
3125
|
+
GROUP BY traceId, spanId
|
|
3126
|
+
) AS reconstructed
|
|
3127
|
+
INNER JOIN candidate_roots USING (traceId, spanId)
|
|
3128
|
+
)
|
|
3129
|
+
SELECT * FROM root_spans ${postAggWhere2} ORDER BY anchorCursorId ASC LIMIT ?
|
|
3130
|
+
`;
|
|
3131
|
+
const rows2 = await db.query(dataSql2, [
|
|
3132
|
+
afterCursorId,
|
|
3133
|
+
...prefilterParams2,
|
|
3134
|
+
...postAggParams2,
|
|
3135
|
+
limit + 1
|
|
3136
|
+
]);
|
|
3137
|
+
const visibleRows = rows2.slice(0, limit).map((row) => ({
|
|
3138
|
+
cursorId: row.anchorCursorId,
|
|
3139
|
+
span: rowToSpanRecord(row)
|
|
3140
|
+
}));
|
|
3141
|
+
return {
|
|
3142
|
+
spans: storage.toTraceSpans(visibleRows.map((row) => row.span)),
|
|
3143
|
+
delta: { limit, hasMore: rows2.length > limit },
|
|
3144
|
+
deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
const { prefilter, postAgg, hasChildError } = partitionAnchorFilters(filterRecord);
|
|
3148
|
+
const { clause: prefilterClause, params: prefilterParams } = buildWhereClause(prefilter);
|
|
3149
|
+
const prefilterParts = [`eventType = 'start'`, `parentSpanId IS NULL`];
|
|
3150
|
+
if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
|
|
3151
|
+
const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
|
|
3152
|
+
const outerAlias = "outer_root";
|
|
3153
|
+
const orderDir = orderBy.direction.toUpperCase();
|
|
3154
|
+
if (orderDir !== "ASC" && orderDir !== "DESC") {
|
|
3155
|
+
throw new Error(`Invalid sort direction: ${orderBy.direction}`);
|
|
3156
|
+
}
|
|
3157
|
+
const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getTraceDeltaCursor(db, filters) : void 0;
|
|
3158
|
+
const canOrderInPrefilter = SAFE_PREFILTER_ORDER_FIELDS.has(orderBy.field);
|
|
3159
|
+
const hasPostAggFilters = Object.keys(postAgg).length > 0 || hasChildError !== void 0 || !canOrderInPrefilter;
|
|
3160
|
+
if (!hasPostAggFilters) {
|
|
3161
|
+
const prefilterOrderBy = `ORDER BY timestamp ${orderDir}`;
|
|
3162
|
+
const offset = page * perPage;
|
|
3163
|
+
const countSql2 = `
|
|
3164
|
+
SELECT COUNT(*) as total
|
|
3165
|
+
FROM span_events AS ${outerAlias}
|
|
3166
|
+
${prefilterWhere}
|
|
3167
|
+
`;
|
|
3168
|
+
const countResult2 = await db.query(countSql2, prefilterParams);
|
|
3169
|
+
const total2 = Number(countResult2[0]?.total ?? 0);
|
|
3170
|
+
const pageSql = `
|
|
3171
|
+
WITH page_roots AS (
|
|
3172
|
+
SELECT traceId, spanId
|
|
3173
|
+
FROM span_events AS ${outerAlias}
|
|
3174
|
+
${prefilterWhere}
|
|
3175
|
+
${prefilterOrderBy}
|
|
3176
|
+
LIMIT ? OFFSET ?
|
|
3177
|
+
)
|
|
3178
|
+
${SPAN_RECONSTRUCT_SELECT}
|
|
3179
|
+
WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM page_roots)
|
|
3180
|
+
GROUP BY traceId, spanId
|
|
3181
|
+
${buildOrderByClause(orderBy)}
|
|
3182
|
+
`;
|
|
3183
|
+
const rows2 = await db.query(pageSql, [...prefilterParams, perPage, offset]);
|
|
3184
|
+
const spans2 = rows2.map((row) => rowToSpanRecord(row));
|
|
3185
|
+
return {
|
|
3186
|
+
pagination: { total: total2, page, perPage, hasMore: (page + 1) * perPage < total2 },
|
|
3187
|
+
spans: storage.toTraceSpans(spans2),
|
|
3188
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
3189
|
+
};
|
|
3190
|
+
}
|
|
3191
|
+
const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
|
|
3192
|
+
const postAggParts = [];
|
|
3193
|
+
if (postAggClause) postAggParts.push(postAggClause.replace(/^WHERE\s+/i, ""));
|
|
3194
|
+
const childErrorClause = buildHasChildErrorClause(hasChildError, "root_spans");
|
|
3195
|
+
if (childErrorClause) postAggParts.push(childErrorClause);
|
|
3196
|
+
const postAggWhere = postAggParts.length > 0 ? `WHERE ${postAggParts.join(" AND ")}` : "";
|
|
3197
|
+
const cteSql = `
|
|
3198
|
+
WITH candidate_roots AS (
|
|
3199
|
+
SELECT traceId, spanId
|
|
3200
|
+
FROM span_events AS ${outerAlias}
|
|
3201
|
+
${prefilterWhere}
|
|
3202
|
+
),
|
|
3203
|
+
root_spans AS (
|
|
3204
|
+
${SPAN_RECONSTRUCT_SELECT}
|
|
3205
|
+
WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
|
|
3206
|
+
GROUP BY traceId, spanId
|
|
3207
|
+
)
|
|
3208
|
+
`;
|
|
3209
|
+
const orderByClause = buildOrderByClause(orderBy);
|
|
3210
|
+
const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
|
|
3211
|
+
const countSql = `
|
|
3212
|
+
${cteSql}
|
|
3213
|
+
SELECT COUNT(*) as total FROM root_spans ${postAggWhere}
|
|
3214
|
+
`;
|
|
3215
|
+
const countResult = await db.query(countSql, [...prefilterParams, ...postAggParams]);
|
|
3216
|
+
const total = Number(countResult[0]?.total ?? 0);
|
|
3217
|
+
const dataSql = `
|
|
3218
|
+
${cteSql}
|
|
3219
|
+
SELECT * FROM root_spans ${postAggWhere} ${orderByClause} ${paginationClause}
|
|
3220
|
+
`;
|
|
3221
|
+
const rows = await db.query(dataSql, [...prefilterParams, ...postAggParams, ...paginationParams]);
|
|
3222
|
+
const spans = rows.map((row) => rowToSpanRecord(row));
|
|
3223
|
+
return {
|
|
3224
|
+
pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
|
|
3225
|
+
spans: storage.toTraceSpans(spans),
|
|
3226
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
3227
|
+
};
|
|
2789
3228
|
}
|
|
2790
3229
|
async function listTracesLight(db, args) {
|
|
2791
3230
|
return listTraceRows(
|
|
@@ -2814,18 +3253,88 @@ async function getSpans(db, args) {
|
|
|
2814
3253
|
};
|
|
2815
3254
|
}
|
|
2816
3255
|
async function listBranches(db, args) {
|
|
2817
|
-
const
|
|
2818
|
-
const
|
|
2819
|
-
const
|
|
2820
|
-
const
|
|
2821
|
-
const userSpanType =
|
|
3256
|
+
const { mode, filters, pagination, orderBy, after, limit } = storage.listBranchesArgsSchema.parse(args);
|
|
3257
|
+
const filterRecord = filters ?? {};
|
|
3258
|
+
const page = Number(pagination.page);
|
|
3259
|
+
const perPage = Number(pagination.perPage);
|
|
3260
|
+
const userSpanType = filterRecord.spanType;
|
|
2822
3261
|
if (typeof userSpanType === "string" && !storage.BRANCH_SPAN_TYPES.includes(userSpanType)) {
|
|
3262
|
+
const currentDeltaCursor2 = deltaPollingFeatureEnabled() ? await getBranchDeltaCursor(db, filters) : void 0;
|
|
3263
|
+
if (mode === "delta") {
|
|
3264
|
+
assertDeltaPollingEnabled();
|
|
3265
|
+
return {
|
|
3266
|
+
branches: [],
|
|
3267
|
+
delta: { limit, hasMore: false },
|
|
3268
|
+
deltaCursor: currentDeltaCursor2
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
2823
3271
|
return {
|
|
2824
3272
|
pagination: { total: 0, page, perPage, hasMore: false },
|
|
2825
|
-
branches: []
|
|
3273
|
+
branches: [],
|
|
3274
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor2 } : {}
|
|
3275
|
+
};
|
|
3276
|
+
}
|
|
3277
|
+
if (mode === "delta") {
|
|
3278
|
+
assertDeltaPollingEnabled();
|
|
3279
|
+
const streamHeadCursor = await getBranchStreamHeadCursor(
|
|
3280
|
+
db,
|
|
3281
|
+
typeof userSpanType === "string" ? userSpanType : null
|
|
3282
|
+
);
|
|
3283
|
+
if (after === void 0) {
|
|
3284
|
+
return {
|
|
3285
|
+
branches: [],
|
|
3286
|
+
delta: { limit, hasMore: false },
|
|
3287
|
+
deltaCursor: streamHeadCursor
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
const afterCursorId = validateCursorId(after);
|
|
3291
|
+
const { spanType: _spanType2, ...rest2 } = filterRecord;
|
|
3292
|
+
const { prefilter: prefilter2, postAgg: postAgg2} = partitionAnchorFilters(rest2);
|
|
3293
|
+
const { clause: prefilterClause2, params: prefilterFilterParams2 } = buildWhereClause(prefilter2);
|
|
3294
|
+
const prefilterParts2 = [`eventType = 'start'`, `cursorId IS NOT NULL`, `cursorId > CAST(? AS BIGINT)`];
|
|
3295
|
+
let spanTypeParams2;
|
|
3296
|
+
if (typeof userSpanType === "string") {
|
|
3297
|
+
prefilterParts2.push(`spanType = ?`);
|
|
3298
|
+
spanTypeParams2 = [userSpanType];
|
|
3299
|
+
} else {
|
|
3300
|
+
prefilterParts2.push(`spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS})`);
|
|
3301
|
+
spanTypeParams2 = [...storage.BRANCH_SPAN_TYPES];
|
|
3302
|
+
}
|
|
3303
|
+
if (prefilterClause2) prefilterParts2.push(prefilterClause2.replace(/^WHERE\s+/i, ""));
|
|
3304
|
+
const prefilterWhere2 = `WHERE ${prefilterParts2.join(" AND ")}`;
|
|
3305
|
+
const prefilterParams2 = [afterCursorId, ...spanTypeParams2, ...prefilterFilterParams2];
|
|
3306
|
+
const { clause: postAggClause2, params: postAggParams2 } = buildWhereClause(postAgg2);
|
|
3307
|
+
const postAggWhere2 = postAggClause2 ? postAggClause2 : "";
|
|
3308
|
+
const outerAlias2 = "outer_anchor";
|
|
3309
|
+
const dataSql2 = `
|
|
3310
|
+
WITH candidate_anchors AS (
|
|
3311
|
+
SELECT traceId, spanId, cursorId
|
|
3312
|
+
FROM span_events AS ${outerAlias2}
|
|
3313
|
+
${prefilterWhere2}
|
|
3314
|
+
),
|
|
3315
|
+
branch_anchors AS (
|
|
3316
|
+
SELECT reconstructed.*, candidate_anchors.cursorId AS anchorCursorId
|
|
3317
|
+
FROM (
|
|
3318
|
+
${SPAN_RECONSTRUCT_SELECT}
|
|
3319
|
+
WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_anchors)
|
|
3320
|
+
GROUP BY traceId, spanId
|
|
3321
|
+
) AS reconstructed
|
|
3322
|
+
INNER JOIN candidate_anchors USING (traceId, spanId)
|
|
3323
|
+
)
|
|
3324
|
+
SELECT * FROM branch_anchors ${postAggWhere2} ORDER BY anchorCursorId ASC LIMIT ?
|
|
3325
|
+
`;
|
|
3326
|
+
const rows2 = await db.query(dataSql2, [...prefilterParams2, ...postAggParams2, limit + 1]);
|
|
3327
|
+
const visibleRows = rows2.slice(0, limit).map((row) => ({
|
|
3328
|
+
cursorId: row.anchorCursorId,
|
|
3329
|
+
branch: rowToSpanRecord(row)
|
|
3330
|
+
}));
|
|
3331
|
+
return {
|
|
3332
|
+
branches: storage.toTraceSpans(visibleRows.map((row) => row.branch)),
|
|
3333
|
+
delta: { limit, hasMore: rows2.length > limit },
|
|
3334
|
+
deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
|
|
2826
3335
|
};
|
|
2827
3336
|
}
|
|
2828
|
-
const { spanType: _spanType, ...rest } =
|
|
3337
|
+
const { spanType: _spanType, ...rest } = filterRecord;
|
|
2829
3338
|
const { prefilter, postAgg} = partitionAnchorFilters(rest);
|
|
2830
3339
|
const { clause: prefilterClause, params: prefilterFilterParams } = buildWhereClause(prefilter);
|
|
2831
3340
|
const prefilterParts = [`eventType = 'start'`];
|
|
@@ -2845,6 +3354,7 @@ async function listBranches(db, args) {
|
|
|
2845
3354
|
if (orderDir !== "ASC" && orderDir !== "DESC") {
|
|
2846
3355
|
throw new Error(`Invalid sort direction: ${orderBy.direction}`);
|
|
2847
3356
|
}
|
|
3357
|
+
const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getBranchDeltaCursor(db, filters) : void 0;
|
|
2848
3358
|
const canOrderInPrefilter = SAFE_PREFILTER_ORDER_FIELDS.has(orderBy.field);
|
|
2849
3359
|
const hasPostAggFilters = Object.keys(postAgg).length > 0 || !canOrderInPrefilter;
|
|
2850
3360
|
if (!hasPostAggFilters) {
|
|
@@ -2860,7 +3370,8 @@ async function listBranches(db, args) {
|
|
|
2860
3370
|
if (total2 === 0) {
|
|
2861
3371
|
return {
|
|
2862
3372
|
pagination: { total: 0, page, perPage, hasMore: false },
|
|
2863
|
-
branches: []
|
|
3373
|
+
branches: [],
|
|
3374
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
2864
3375
|
};
|
|
2865
3376
|
}
|
|
2866
3377
|
const pageSql = `
|
|
@@ -2880,7 +3391,8 @@ async function listBranches(db, args) {
|
|
|
2880
3391
|
const spans2 = rows2.map((row) => rowToSpanRecord(row));
|
|
2881
3392
|
return {
|
|
2882
3393
|
pagination: { total: total2, page, perPage, hasMore: (page + 1) * perPage < total2 },
|
|
2883
|
-
branches: storage.toTraceSpans(spans2)
|
|
3394
|
+
branches: storage.toTraceSpans(spans2),
|
|
3395
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
2884
3396
|
};
|
|
2885
3397
|
}
|
|
2886
3398
|
const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
|
|
@@ -2908,7 +3420,8 @@ async function listBranches(db, args) {
|
|
|
2908
3420
|
if (total === 0) {
|
|
2909
3421
|
return {
|
|
2910
3422
|
pagination: { total: 0, page, perPage, hasMore: false },
|
|
2911
|
-
branches: []
|
|
3423
|
+
branches: [],
|
|
3424
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
2912
3425
|
};
|
|
2913
3426
|
}
|
|
2914
3427
|
const dataSql = `
|
|
@@ -2919,9 +3432,151 @@ async function listBranches(db, args) {
|
|
|
2919
3432
|
const spans = rows.map((row) => rowToSpanRecord(row));
|
|
2920
3433
|
return {
|
|
2921
3434
|
pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
|
|
2922
|
-
branches: storage.toTraceSpans(spans)
|
|
3435
|
+
branches: storage.toTraceSpans(spans),
|
|
3436
|
+
...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
|
|
2923
3437
|
};
|
|
2924
3438
|
}
|
|
3439
|
+
async function getTraceDeltaCursor(db, filters) {
|
|
3440
|
+
const { prefilter, postAgg, hasChildError } = partitionAnchorFilters(filters ?? {});
|
|
3441
|
+
const { clause: prefilterClause, params: prefilterParams } = buildWhereClause(prefilter);
|
|
3442
|
+
const prefilterParts = [`eventType = 'start'`, `parentSpanId IS NULL`, `cursorId IS NOT NULL`];
|
|
3443
|
+
if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
|
|
3444
|
+
const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
|
|
3445
|
+
const outerAlias = "outer_root";
|
|
3446
|
+
const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
|
|
3447
|
+
const postAggParts = [];
|
|
3448
|
+
if (postAggClause) postAggParts.push(postAggClause.replace(/^WHERE\s+/i, ""));
|
|
3449
|
+
const childErrorClause = buildHasChildErrorClause(hasChildError, "root_spans");
|
|
3450
|
+
if (childErrorClause) postAggParts.push(childErrorClause);
|
|
3451
|
+
const postAggWhere = postAggParts.length > 0 ? `WHERE ${postAggParts.join(" AND ")}` : "";
|
|
3452
|
+
if (postAggWhere === "") {
|
|
3453
|
+
const rows2 = await db.query(
|
|
3454
|
+
`SELECT max(cursorId) AS cursorId FROM span_events AS ${outerAlias} ${prefilterWhere}`,
|
|
3455
|
+
prefilterParams
|
|
3456
|
+
);
|
|
3457
|
+
const cursorId2 = rows2[0]?.cursorId;
|
|
3458
|
+
if (cursorId2 !== null && cursorId2 !== void 0) {
|
|
3459
|
+
return encodeDeltaCursor(cursorId2);
|
|
3460
|
+
}
|
|
3461
|
+
const streamRows2 = await db.query(
|
|
3462
|
+
`SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND parentSpanId IS NULL AND cursorId IS NOT NULL`
|
|
3463
|
+
);
|
|
3464
|
+
return encodeDeltaCursor(streamRows2[0]?.cursorId);
|
|
3465
|
+
}
|
|
3466
|
+
const cteSql = `
|
|
3467
|
+
WITH candidate_roots AS (
|
|
3468
|
+
SELECT traceId, spanId, cursorId
|
|
3469
|
+
FROM span_events AS ${outerAlias}
|
|
3470
|
+
${prefilterWhere}
|
|
3471
|
+
),
|
|
3472
|
+
root_spans AS (
|
|
3473
|
+
SELECT reconstructed.*, candidate_roots.cursorId AS anchorCursorId
|
|
3474
|
+
FROM (
|
|
3475
|
+
${SPAN_RECONSTRUCT_SELECT}
|
|
3476
|
+
WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
|
|
3477
|
+
GROUP BY traceId, spanId
|
|
3478
|
+
) AS reconstructed
|
|
3479
|
+
INNER JOIN candidate_roots USING (traceId, spanId)
|
|
3480
|
+
)
|
|
3481
|
+
`;
|
|
3482
|
+
const rows = await db.query(
|
|
3483
|
+
`${cteSql} SELECT max(anchorCursorId) AS cursorId FROM root_spans ${postAggWhere}`,
|
|
3484
|
+
[...prefilterParams, ...postAggParams]
|
|
3485
|
+
);
|
|
3486
|
+
const cursorId = rows[0]?.cursorId;
|
|
3487
|
+
if (cursorId !== null && cursorId !== void 0) {
|
|
3488
|
+
return encodeDeltaCursor(cursorId);
|
|
3489
|
+
}
|
|
3490
|
+
const streamRows = await db.query(
|
|
3491
|
+
`SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND parentSpanId IS NULL AND cursorId IS NOT NULL`
|
|
3492
|
+
);
|
|
3493
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
3494
|
+
}
|
|
3495
|
+
async function getTraceStreamHeadCursor(db) {
|
|
3496
|
+
const streamRows = await db.query(
|
|
3497
|
+
`SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND parentSpanId IS NULL AND cursorId IS NOT NULL`
|
|
3498
|
+
);
|
|
3499
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
3500
|
+
}
|
|
3501
|
+
async function getBranchDeltaCursor(db, filters) {
|
|
3502
|
+
const filterRecord = filters ?? {};
|
|
3503
|
+
const userSpanType = filterRecord.spanType;
|
|
3504
|
+
const { spanType: _spanType, ...rest } = filterRecord;
|
|
3505
|
+
const { prefilter, postAgg } = partitionAnchorFilters(rest);
|
|
3506
|
+
const { clause: prefilterClause, params: prefilterFilterParams } = buildWhereClause(prefilter);
|
|
3507
|
+
const prefilterParts = [`eventType = 'start'`, `cursorId IS NOT NULL`];
|
|
3508
|
+
let spanTypeParams;
|
|
3509
|
+
if (typeof userSpanType === "string") {
|
|
3510
|
+
prefilterParts.push(`spanType = ?`);
|
|
3511
|
+
spanTypeParams = [userSpanType];
|
|
3512
|
+
} else {
|
|
3513
|
+
prefilterParts.push(`spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS})`);
|
|
3514
|
+
spanTypeParams = [...storage.BRANCH_SPAN_TYPES];
|
|
3515
|
+
}
|
|
3516
|
+
if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
|
|
3517
|
+
const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
|
|
3518
|
+
const prefilterParams = [...spanTypeParams, ...prefilterFilterParams];
|
|
3519
|
+
const outerAlias = "outer_anchor";
|
|
3520
|
+
const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
|
|
3521
|
+
if (!postAggClause) {
|
|
3522
|
+
const rows2 = await db.query(
|
|
3523
|
+
`SELECT max(cursorId) AS cursorId FROM span_events AS ${outerAlias} ${prefilterWhere}`,
|
|
3524
|
+
prefilterParams
|
|
3525
|
+
);
|
|
3526
|
+
const cursorId2 = rows2[0]?.cursorId;
|
|
3527
|
+
if (cursorId2 !== null && cursorId2 !== void 0) {
|
|
3528
|
+
return encodeDeltaCursor(cursorId2);
|
|
3529
|
+
}
|
|
3530
|
+
const streamRows2 = await db.query(
|
|
3531
|
+
`SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS}) AND cursorId IS NOT NULL`,
|
|
3532
|
+
[...storage.BRANCH_SPAN_TYPES]
|
|
3533
|
+
);
|
|
3534
|
+
return encodeDeltaCursor(streamRows2[0]?.cursorId);
|
|
3535
|
+
}
|
|
3536
|
+
const cteSql = `
|
|
3537
|
+
WITH candidate_anchors AS (
|
|
3538
|
+
SELECT traceId, spanId, cursorId
|
|
3539
|
+
FROM span_events AS ${outerAlias}
|
|
3540
|
+
${prefilterWhere}
|
|
3541
|
+
),
|
|
3542
|
+
branch_anchors AS (
|
|
3543
|
+
SELECT reconstructed.*, candidate_anchors.cursorId AS anchorCursorId
|
|
3544
|
+
FROM (
|
|
3545
|
+
${SPAN_RECONSTRUCT_SELECT}
|
|
3546
|
+
WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_anchors)
|
|
3547
|
+
GROUP BY traceId, spanId
|
|
3548
|
+
) AS reconstructed
|
|
3549
|
+
INNER JOIN candidate_anchors USING (traceId, spanId)
|
|
3550
|
+
)
|
|
3551
|
+
`;
|
|
3552
|
+
const rows = await db.query(
|
|
3553
|
+
`${cteSql} SELECT max(anchorCursorId) AS cursorId FROM branch_anchors ${postAggClause}`,
|
|
3554
|
+
[...prefilterParams, ...postAggParams]
|
|
3555
|
+
);
|
|
3556
|
+
const cursorId = rows[0]?.cursorId;
|
|
3557
|
+
if (cursorId !== null && cursorId !== void 0) {
|
|
3558
|
+
return encodeDeltaCursor(cursorId);
|
|
3559
|
+
}
|
|
3560
|
+
const streamRows = await db.query(
|
|
3561
|
+
`SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS}) AND cursorId IS NOT NULL`,
|
|
3562
|
+
[...storage.BRANCH_SPAN_TYPES]
|
|
3563
|
+
);
|
|
3564
|
+
return encodeDeltaCursor(streamRows[0]?.cursorId);
|
|
3565
|
+
}
|
|
3566
|
+
async function getBranchStreamHeadCursor(db, userSpanType) {
|
|
3567
|
+
if (userSpanType) {
|
|
3568
|
+
const rows2 = await db.query(
|
|
3569
|
+
`SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType = ? AND cursorId IS NOT NULL`,
|
|
3570
|
+
[userSpanType]
|
|
3571
|
+
);
|
|
3572
|
+
return encodeDeltaCursor(rows2[0]?.cursorId);
|
|
3573
|
+
}
|
|
3574
|
+
const rows = await db.query(
|
|
3575
|
+
`SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS}) AND cursorId IS NOT NULL`,
|
|
3576
|
+
[...storage.BRANCH_SPAN_TYPES]
|
|
3577
|
+
);
|
|
3578
|
+
return encodeDeltaCursor(rows[0]?.cursorId);
|
|
3579
|
+
}
|
|
2925
3580
|
|
|
2926
3581
|
// src/storage/domains/observability/index.ts
|
|
2927
3582
|
function buildSignalMigrationRequiredMessage(args) {
|
|
@@ -2970,6 +3625,7 @@ var ObservabilityStorageDuckDB = class extends storage.ObservabilityStorage {
|
|
|
2970
3625
|
});
|
|
2971
3626
|
}
|
|
2972
3627
|
await this.db.executeBatch([...ALL_DDL, ...ALL_MIGRATIONS]);
|
|
3628
|
+
await dropLegacyCursorIdDefaults(this.db);
|
|
2973
3629
|
}
|
|
2974
3630
|
/**
|
|
2975
3631
|
* Manually migrate legacy signal tables to the signal-ID primary-key schema.
|
|
@@ -3006,6 +3662,12 @@ var ObservabilityStorageDuckDB = class extends storage.ObservabilityStorage {
|
|
|
3006
3662
|
supported: ["event-sourced"]
|
|
3007
3663
|
};
|
|
3008
3664
|
}
|
|
3665
|
+
getFeatures() {
|
|
3666
|
+
if (!deltaPollingFeatureEnabled()) {
|
|
3667
|
+
return void 0;
|
|
3668
|
+
}
|
|
3669
|
+
return ["delta-polling"];
|
|
3670
|
+
}
|
|
3009
3671
|
// Tracing
|
|
3010
3672
|
async createSpan(args) {
|
|
3011
3673
|
return createSpan(this.db, args);
|
|
@@ -3142,5 +3804,5 @@ var ObservabilityStorageDuckDB = class extends storage.ObservabilityStorage {
|
|
|
3142
3804
|
};
|
|
3143
3805
|
|
|
3144
3806
|
exports.ObservabilityStorageDuckDB = ObservabilityStorageDuckDB;
|
|
3145
|
-
//# sourceMappingURL=observability-
|
|
3146
|
-
//# sourceMappingURL=observability-
|
|
3807
|
+
//# sourceMappingURL=observability-V2KYD7UF.cjs.map
|
|
3808
|
+
//# sourceMappingURL=observability-V2KYD7UF.cjs.map
|