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