@mastra/clickhouse 1.20.0-alpha.2 → 1.20.0-alpha.3

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.
@@ -3,7 +3,7 @@ name: mastra-clickhouse
3
3
  description: Documentation for @mastra/clickhouse. Use when working with @mastra/clickhouse APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/clickhouse"
6
- version: "1.20.0-alpha.2"
6
+ version: "1.20.0-alpha.3"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.20.0-alpha.2",
2
+ "version": "1.20.0-alpha.3",
3
3
  "package": "@mastra/clickhouse",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -2569,6 +2569,8 @@ const TABLE_TRACE_BRANCHES_DELTA = "mastra_trace_branches_delta";
2569
2569
  const TABLE_METRIC_EVENTS = "mastra_metric_events";
2570
2570
  const TABLE_LOG_EVENTS = "mastra_log_events";
2571
2571
  const TABLE_SCORE_EVENTS = "mastra_score_events";
2572
+ const TABLE_SCORE_EVENTS_CURRENT = "mastra_score_events_current";
2573
+ const TABLE_SCORE_EVENTS_CURRENT_BACKFILL = "mastra_score_events_current_backfill";
2572
2574
  const TABLE_FEEDBACK_EVENTS = "mastra_feedback_events";
2573
2575
  const TABLE_DELETION_REQUESTS = "mastra_deletion_requests";
2574
2576
  const TABLE_METRIC_EVENTS_DELTA = "mastra_metric_events_delta";
@@ -2584,6 +2586,7 @@ const MV_TRACE_BRANCHES_DELTA = "mastra_mv_trace_branches_delta";
2584
2586
  const MV_METRIC_EVENTS_DELTA = "mastra_mv_metric_events_delta";
2585
2587
  const MV_LOG_EVENTS_DELTA = "mastra_mv_log_events_delta";
2586
2588
  const MV_SCORE_EVENTS_DELTA = "mastra_mv_score_events_delta";
2589
+ const MV_SCORE_EVENTS_CURRENT = "mastra_mv_score_events_current";
2587
2590
  const MV_FEEDBACK_EVENTS_DELTA = "mastra_mv_feedback_events_delta";
2588
2591
  const MV_DISCOVERY_VALUES = "mastra_mv_discovery_values";
2589
2592
  const MV_DISCOVERY_PAIRS = "mastra_mv_discovery_pairs";
@@ -3101,19 +3104,53 @@ FROM (
3101
3104
  )
3102
3105
  `;
3103
3106
  }
3104
- const SCORE_EVENTS_DDL = `
3105
- CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS} (
3106
- -- Timestamp
3107
+ const SCORE_EVENT_COLUMN_NAMES = [
3108
+ "timestamp",
3109
+ "scoreId",
3110
+ "writeVersion",
3111
+ "traceId",
3112
+ "spanId",
3113
+ "experimentId",
3114
+ "scoreTraceId",
3115
+ "entityType",
3116
+ "entityId",
3117
+ "entityName",
3118
+ "entityVersionId",
3119
+ "parentEntityVersionId",
3120
+ "parentEntityType",
3121
+ "parentEntityId",
3122
+ "parentEntityName",
3123
+ "rootEntityVersionId",
3124
+ "rootEntityType",
3125
+ "rootEntityId",
3126
+ "rootEntityName",
3127
+ "userId",
3128
+ "organizationId",
3129
+ "resourceId",
3130
+ "runId",
3131
+ "sessionId",
3132
+ "threadId",
3133
+ "requestId",
3134
+ "environment",
3135
+ "executionSource",
3136
+ "serviceName",
3137
+ "scorerId",
3138
+ "scorerVersion",
3139
+ "scoreSource",
3140
+ "score",
3141
+ "reason",
3142
+ "tags",
3143
+ "metadata",
3144
+ "scope"
3145
+ ];
3146
+ const SCORE_EVENT_COLUMNS_DDL = `
3107
3147
  timestamp DateTime64(3, 'UTC'),
3108
-
3109
- -- IDs
3110
3148
  scoreId String,
3149
+ writeVersion UInt64 DEFAULT 0,
3111
3150
  traceId Nullable(String),
3112
3151
  spanId Nullable(String),
3113
3152
  experimentId Nullable(String),
3114
3153
  scoreTraceId Nullable(String),
3115
-
3116
- -- Entity hierarchy
3117
3154
  entityType LowCardinality(Nullable(String)),
3118
3155
  entityId Nullable(String),
3119
3156
  entityName Nullable(String),
@@ -3126,8 +3163,6 @@ CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS} (
3126
3163
  rootEntityType LowCardinality(Nullable(String)),
3127
3164
  rootEntityId Nullable(String),
3128
3165
  rootEntityName Nullable(String),
3129
-
3130
- -- Context
3131
3166
  userId Nullable(String),
3132
3167
  organizationId Nullable(String),
3133
3168
  resourceId Nullable(String),
@@ -3138,30 +3173,48 @@ CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS} (
3138
3173
  environment LowCardinality(Nullable(String)),
3139
3174
  executionSource LowCardinality(Nullable(String)),
3140
3175
  serviceName LowCardinality(Nullable(String)),
3141
-
3142
- -- Scorer identity
3143
3176
  scorerId LowCardinality(String),
3144
3177
  scorerVersion LowCardinality(Nullable(String)),
3145
3178
  scoreSource LowCardinality(Nullable(String)),
3146
-
3147
- -- Score value
3148
3179
  score Float64,
3149
-
3150
- -- Information-only
3151
3180
  reason Nullable(String),
3152
-
3153
- -- Query-relevant flexible fields
3154
3181
  tags Array(LowCardinality(String)) DEFAULT [],
3155
-
3156
- -- Information-only JSON payloads
3157
3182
  metadata Nullable(String),
3158
3183
  scope Nullable(String)
3184
+ `;
3185
+ const SCORE_EVENTS_DDL = `
3186
+ CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS} (
3187
+ ${SCORE_EVENT_COLUMNS_DDL},
3188
+ INDEX idx_scoreId scoreId TYPE bloom_filter(0.01) GRANULARITY 1
3159
3189
  )
3160
3190
  ENGINE = ReplacingMergeTree
3161
3191
  PARTITION BY toDate(timestamp)
3162
3192
  ORDER BY (traceId, timestamp, scoreId)
3163
3193
  SETTINGS allow_nullable_key = 1
3164
3194
  `;
3195
+ const SCORE_EVENTS_CURRENT_DDL = `
3196
+ CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS_CURRENT} (
3197
+ ${SCORE_EVENT_COLUMNS_DDL}
3198
+ )
3199
+ ENGINE = ReplacingMergeTree(writeVersion)
3200
+ PARTITION BY cityHash64(scoreId) % 64
3201
+ ORDER BY scoreId
3202
+ `;
3203
+ const SCORE_EVENTS_CURRENT_BACKFILL_DDL = `
3204
+ CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS_CURRENT_BACKFILL} (
3205
+ marker String,
3206
+ completedAt DateTime64(3, 'UTC')
3207
+ )
3208
+ ENGINE = ReplacingMergeTree(completedAt)
3209
+ ORDER BY marker
3210
+ `;
3211
+ const SCORE_EVENTS_CURRENT_MV_DDL = `
3212
+ CREATE MATERIALIZED VIEW IF NOT EXISTS ${MV_SCORE_EVENTS_CURRENT}
3213
+ TO ${TABLE_SCORE_EVENTS_CURRENT}
3214
+ AS
3215
+ SELECT ${SCORE_EVENT_COLUMN_NAMES.join(", ")}
3216
+ FROM ${TABLE_SCORE_EVENTS}
3217
+ `;
3165
3218
  function buildScoreEventsDeltaDDL() {
3166
3219
  return `
3167
3220
  CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS_DELTA} (
@@ -3435,6 +3488,8 @@ const BASE_TABLE_DDL = [
3435
3488
  METRIC_EVENTS_DDL,
3436
3489
  LOG_EVENTS_DDL,
3437
3490
  SCORE_EVENTS_DDL,
3491
+ SCORE_EVENTS_CURRENT_DDL,
3492
+ SCORE_EVENTS_CURRENT_BACKFILL_DDL,
3438
3493
  FEEDBACK_EVENTS_DDL,
3439
3494
  DELETION_REQUESTS_DDL,
3440
3495
  DISCOVERY_VALUES_DDL,
@@ -3453,7 +3508,11 @@ function buildDeltaTableDDL() {
3453
3508
  function buildAllTableDDL() {
3454
3509
  return [...BASE_TABLE_DDL, ...buildDeltaTableDDL()];
3455
3510
  }
3456
- const BASE_MV_DDL = [TRACE_ROOTS_MV_DDL, TRACE_BRANCHES_MV_DDL];
3511
+ const BASE_MV_DDL = [
3512
+ TRACE_ROOTS_MV_DDL,
3513
+ TRACE_BRANCHES_MV_DDL,
3514
+ SCORE_EVENTS_CURRENT_MV_DDL
3515
+ ];
3457
3516
  function buildDeltaMvDDL(strategy) {
3458
3517
  return [
3459
3518
  buildTraceRootsDeltaMvDDL(strategy),
@@ -3475,11 +3534,11 @@ const addColumn = (table, name, type) => ({
3475
3534
  name,
3476
3535
  sql: `ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS ${name} ${type}`
3477
3536
  });
3478
- const addBloomIndex = (table, name, column) => ({
3537
+ const addBloomIndex = (table, name, column, granularity = 2) => ({
3479
3538
  kind: "index",
3480
3539
  table,
3481
3540
  name,
3482
- sql: `ALTER TABLE ${table} ADD INDEX IF NOT EXISTS ${name} ${column} TYPE bloom_filter(0.01) GRANULARITY 2`
3541
+ sql: `ALTER TABLE ${table} ADD INDEX IF NOT EXISTS ${name} ${column} TYPE bloom_filter(0.01) GRANULARITY ${granularity}`
3483
3542
  });
3484
3543
  const ALL_MIGRATIONS = [
3485
3544
  addColumn(TABLE_SPAN_EVENTS, "entityVersionId", "Nullable(String)"),
@@ -3494,9 +3553,11 @@ const ALL_MIGRATIONS = [
3494
3553
  addColumn(TABLE_LOG_EVENTS, "entityVersionId", "Nullable(String)"),
3495
3554
  addColumn(TABLE_LOG_EVENTS, "parentEntityVersionId", "Nullable(String)"),
3496
3555
  addColumn(TABLE_LOG_EVENTS, "rootEntityVersionId", "Nullable(String)"),
3556
+ addColumn(TABLE_SCORE_EVENTS, "writeVersion", "UInt64 DEFAULT 0"),
3497
3557
  addColumn(TABLE_SCORE_EVENTS, "entityVersionId", "Nullable(String)"),
3498
3558
  addColumn(TABLE_SCORE_EVENTS, "parentEntityVersionId", "Nullable(String)"),
3499
3559
  addColumn(TABLE_SCORE_EVENTS, "rootEntityVersionId", "Nullable(String)"),
3560
+ addBloomIndex(TABLE_SCORE_EVENTS, "idx_scoreId", "scoreId", 1),
3500
3561
  addColumn(TABLE_FEEDBACK_EVENTS, "writeVersion", "UInt64 DEFAULT 0"),
3501
3562
  addColumn(TABLE_FEEDBACK_EVENTS, "entityVersionId", "Nullable(String)"),
3502
3563
  addColumn(TABLE_FEEDBACK_EVENTS, "reviewStatus", "LowCardinality(String) DEFAULT 'needs-review'"),
@@ -3522,6 +3583,8 @@ const ALL_TABLE_NAMES = [
3522
3583
  TABLE_METRIC_EVENTS,
3523
3584
  TABLE_LOG_EVENTS,
3524
3585
  TABLE_SCORE_EVENTS,
3586
+ TABLE_SCORE_EVENTS_CURRENT,
3587
+ TABLE_SCORE_EVENTS_CURRENT_BACKFILL,
3525
3588
  TABLE_FEEDBACK_EVENTS,
3526
3589
  TABLE_DELETION_REQUESTS,
3527
3590
  TABLE_METRIC_EVENTS_DELTA,
@@ -3539,6 +3602,7 @@ const SIGNAL_TTL_COLUMNS = {
3539
3602
  [TABLE_METRIC_EVENTS]: "timestamp",
3540
3603
  [TABLE_LOG_EVENTS]: "timestamp",
3541
3604
  [TABLE_SCORE_EVENTS]: "timestamp",
3605
+ [TABLE_SCORE_EVENTS_CURRENT]: "timestamp",
3542
3606
  [TABLE_FEEDBACK_EVENTS]: "timestamp"
3543
3607
  };
3544
3608
  /** Maps each signal key to the table(s) it controls. */
@@ -3550,7 +3614,7 @@ const SIGNAL_TO_TABLES = {
3550
3614
  ],
3551
3615
  logs: [TABLE_LOG_EVENTS],
3552
3616
  metrics: [TABLE_METRIC_EVENTS],
3553
- scores: [TABLE_SCORE_EVENTS],
3617
+ scores: [TABLE_SCORE_EVENTS, TABLE_SCORE_EVENTS_CURRENT],
3554
3618
  feedback: [TABLE_FEEDBACK_EVENTS]
3555
3619
  };
3556
3620
  const RETENTION_MANAGED_TABLES = [...Object.keys(SIGNAL_TTL_COLUMNS), TABLE_DELETION_REQUESTS];
@@ -5970,6 +6034,36 @@ async function migrateSignalTables(client, logger) {
5970
6034
  }
5971
6035
  }
5972
6036
  //#endregion
6037
+ //#region src/storage/domains/observability/v-next/score-current.ts
6038
+ const SCORE_CURRENT_BACKFILL_MARKER = "v1";
6039
+ async function backfillCurrentScores(client) {
6040
+ const markerRows = await (await client.query({
6041
+ query: `SELECT count() AS count
6042
+ FROM ${TABLE_SCORE_EVENTS_CURRENT_BACKFILL} FINAL
6043
+ WHERE marker = {marker:String}`,
6044
+ query_params: { marker: SCORE_CURRENT_BACKFILL_MARKER },
6045
+ format: "JSONEachRow"
6046
+ })).json();
6047
+ if (Number(markerRows[0]?.count ?? 0) > 0) return;
6048
+ const columns = SCORE_EVENT_COLUMN_NAMES.join(", ");
6049
+ await client.command({
6050
+ query: `INSERT INTO ${TABLE_SCORE_EVENTS_CURRENT} (${columns})
6051
+ SELECT ${columns}
6052
+ FROM (
6053
+ SELECT *, cityHash64(tuple(*)) AS _currentScoreFingerprint
6054
+ FROM ${TABLE_SCORE_EVENTS} FINAL
6055
+ ORDER BY scoreId, writeVersion DESC, timestamp DESC, _currentScoreFingerprint DESC
6056
+ LIMIT 1 BY scoreId
6057
+ )`,
6058
+ clickhouse_settings: { max_bytes_ratio_before_external_sort: .5 }
6059
+ });
6060
+ await client.command({
6061
+ query: `INSERT INTO ${TABLE_SCORE_EVENTS_CURRENT_BACKFILL} (marker, completedAt)
6062
+ VALUES ({marker:String}, now64(3, 'UTC'))`,
6063
+ query_params: { marker: SCORE_CURRENT_BACKFILL_MARKER }
6064
+ });
6065
+ }
6066
+ //#endregion
5973
6067
  //#region src/storage/domains/observability/v-next/scores.ts
5974
6068
  const SCORE_TYPED_COLUMNS = /* @__PURE__ */ new Set([
5975
6069
  "timestamp",
@@ -6046,6 +6140,13 @@ function mergeFilters(...parts) {
6046
6140
  function toWhereClause(filter) {
6047
6141
  return filter.conditions.length ? `WHERE ${filter.conditions.join(" AND ")}` : "";
6048
6142
  }
6143
+ function currentScoresRelation(sourcePredicate) {
6144
+ return `(
6145
+ SELECT *
6146
+ FROM ${TABLE_SCORE_EVENTS_CURRENT} FINAL
6147
+ ${sourcePredicate ? `WHERE ${sourcePredicate}` : ""}
6148
+ )`;
6149
+ }
6049
6150
  function buildScoreIdentityFilter(args) {
6050
6151
  const conditions = ["scorerId = {olapScorerId:String}"];
6051
6152
  const params = { olapScorerId: args.scorerId };
@@ -6081,6 +6182,24 @@ async function queryJson(client, query, params) {
6081
6182
  clickhouse_settings: CH_SETTINGS
6082
6183
  })).json();
6083
6184
  }
6185
+ async function scoreRowsWithWriteVersions(client, scores) {
6186
+ const scoreIds = [...new Set(scores.flatMap((score) => score.scoreId ? [score.scoreId] : []))];
6187
+ const existingVersions = [];
6188
+ const lookupBatchSize = 1e3;
6189
+ for (let offset = 0; offset < scoreIds.length; offset += lookupBatchSize) existingVersions.push(...await queryJson(client, `SELECT scoreId, toString(max(writeVersion)) AS writeVersion
6190
+ FROM ${TABLE_SCORE_EVENTS}
6191
+ WHERE scoreId IN ({scoreIds:Array(String)})
6192
+ GROUP BY scoreId`, { scoreIds: scoreIds.slice(offset, offset + lookupBatchSize) }));
6193
+ const versions = new Map(existingVersions.map((row) => [row.scoreId, BigInt(row.writeVersion)]));
6194
+ return scores.map((score) => {
6195
+ const writeVersion = score.scoreId ? (versions.get(score.scoreId) ?? 0n) + 1n : 1n;
6196
+ if (score.scoreId) versions.set(score.scoreId, writeVersion);
6197
+ return {
6198
+ ...scoreRecordToRow(score),
6199
+ writeVersion: writeVersion.toString()
6200
+ };
6201
+ });
6202
+ }
6084
6203
  async function createScore(client, args) {
6085
6204
  await batchCreateScores(client, { scores: [args.score] });
6086
6205
  }
@@ -6088,7 +6207,7 @@ async function batchCreateScores(client, args) {
6088
6207
  if (args.scores.length === 0) return;
6089
6208
  await client.insert({
6090
6209
  table: TABLE_SCORE_EVENTS,
6091
- values: args.scores.map(scoreRecordToRow),
6210
+ values: await scoreRowsWithWriteVersions(client, args.scores),
6092
6211
  format: "JSONEachRow",
6093
6212
  clickhouse_settings: CH_INSERT_SETTINGS
6094
6213
  });
@@ -6131,10 +6250,11 @@ async function deleteScores(client, args, replication) {
6131
6250
  conditions.push("resourceId = {delResourceId:String}");
6132
6251
  params.delResourceId = args.resourceId;
6133
6252
  }
6134
- await client.command({
6135
- query: `DELETE FROM ${TABLE_SCORE_EVENTS} WHERE ${conditions.join(" AND ")}`,
6253
+ const clickhouse_settings = { lightweight_deletes_sync: isReplicationConfigured(replication) ? "2" : "1" };
6254
+ for (const table of [TABLE_SCORE_EVENTS_CURRENT, TABLE_SCORE_EVENTS]) await client.command({
6255
+ query: `DELETE FROM ${table} WHERE ${conditions.join(" AND ")}`,
6136
6256
  query_params: params,
6137
- clickhouse_settings: { lightweight_deletes_sync: isReplicationConfigured(replication) ? "2" : "1" }
6257
+ clickhouse_settings
6138
6258
  });
6139
6259
  }
6140
6260
  async function listScores(client, args, strategy) {
@@ -6168,8 +6288,9 @@ async function listScores(client, args, strategy) {
6168
6288
  };
6169
6289
  }
6170
6290
  const currentDeltaCursor = deltaCursorEnabled ? await getDeltaCursor$2(client, whereClause, filter.params) : void 0;
6171
- const countResult = await queryJson(client, `SELECT count() AS total FROM ${TABLE_SCORE_EVENTS} AS s ${whereClause}`, filter.params);
6172
- const rows = await queryJson(client, `SELECT * FROM ${TABLE_SCORE_EVENTS} AS s ${whereClause} ORDER BY ${orderBy} LIMIT {limit:UInt32} OFFSET {offset:UInt32}`, {
6291
+ const scoresRelation = currentScoresRelation();
6292
+ const countResult = await queryJson(client, `SELECT count() AS total FROM ${scoresRelation} AS s ${whereClause}`, filter.params);
6293
+ const rows = await queryJson(client, `SELECT * FROM ${scoresRelation} AS s ${whereClause} ORDER BY ${orderBy} LIMIT {limit:UInt32} OFFSET {offset:UInt32}`, {
6173
6294
  ...filter.params,
6174
6295
  limit: pagination.limit,
6175
6296
  offset: pagination.offset
@@ -6228,14 +6349,15 @@ function buildScoresCursor(row) {
6228
6349
  return row.cursorId ?? "0";
6229
6350
  }
6230
6351
  async function getScoreById(client, scoreId) {
6231
- const rows = await queryJson(client, `SELECT * FROM ${TABLE_SCORE_EVENTS} WHERE scoreId = {scoreId:String} LIMIT 1`, { scoreId });
6352
+ const rows = await queryJson(client, `SELECT * FROM ${currentScoresRelation("scoreId = {scoreId:String}")} LIMIT 1`, { scoreId });
6232
6353
  return rows[0] ? rowToScoreRecord(rows[0]) : null;
6233
6354
  }
6234
6355
  async function getScoreAggregate(client, args) {
6235
6356
  const aggSql = getAggregationSql(args.aggregation);
6236
6357
  const identity = buildScoreIdentityFilter(args);
6237
6358
  const combined = mergeFilters(identity, buildScoresFilterConditions(args.filters));
6238
- const result = await queryJson(client, `SELECT ${aggSql} AS value FROM ${TABLE_SCORE_EVENTS} ${toWhereClause(combined)}`, combined.params);
6359
+ const whereClause = toWhereClause(combined);
6360
+ const result = await queryJson(client, `SELECT ${aggSql} AS value FROM ${currentScoresRelation()} ${whereClause}`, combined.params);
6239
6361
  const value = result[0]?.value == null ? null : Number(result[0]?.value);
6240
6362
  if (args.comparePeriod && args.filters?.timestamp) {
6241
6363
  const ts = args.filters.timestamp;
@@ -6269,7 +6391,8 @@ async function getScoreAggregate(client, args) {
6269
6391
  endExclusive: ts.endExclusive
6270
6392
  }
6271
6393
  }));
6272
- const prevResult = await queryJson(client, `SELECT ${aggSql} AS value FROM ${TABLE_SCORE_EVENTS} ${toWhereClause(prevCombined)}`, prevCombined.params);
6394
+ const prevWhereClause = toWhereClause(prevCombined);
6395
+ const prevResult = await queryJson(client, `SELECT ${aggSql} AS value FROM ${currentScoresRelation()} ${prevWhereClause}`, prevCombined.params);
6273
6396
  const previousValue = prevResult[0]?.value == null ? null : Number(prevResult[0]?.value);
6274
6397
  let changePercent = null;
6275
6398
  if (previousValue !== null && previousValue !== 0 && value !== null) changePercent = (value - previousValue) / Math.abs(previousValue) * 100;
@@ -6287,7 +6410,7 @@ async function getScoreBreakdown(client, args) {
6287
6410
  const combined = mergeFilters(buildScoreIdentityFilter(args), buildScoresFilterConditions(args.filters));
6288
6411
  const whereClause = toWhereClause(combined);
6289
6412
  const resolved = resolveScoreGroupBy(args.groupBy);
6290
- return { groups: (await queryJson(client, `SELECT ${resolved.map((e) => e.selectSql).join(", ")}, ${aggSql} AS value FROM ${TABLE_SCORE_EVENTS} ${whereClause} GROUP BY ${resolved.map((e) => e.groupSql).join(", ")} ORDER BY value DESC`, combined.params)).map((row) => ({
6413
+ return { groups: (await queryJson(client, `SELECT ${resolved.map((e) => e.selectSql).join(", ")}, ${aggSql} AS value FROM ${currentScoresRelation()} ${whereClause} GROUP BY ${resolved.map((e) => e.groupSql).join(", ")} ORDER BY value DESC`, combined.params)).map((row) => ({
6291
6414
  dimensions: Object.fromEntries(resolved.map((entry, index) => {
6292
6415
  const v = row[`group_by_${index}`];
6293
6416
  return [entry.key, v == null ? null : String(v)];
@@ -6306,7 +6429,7 @@ async function getScoreTimeSeries(client, args) {
6306
6429
  SELECT toStartOfInterval(timestamp, ${intervalSql}) AS bucket,
6307
6430
  ${resolved.map((e) => e.selectSql).join(", ")},
6308
6431
  ${aggSql} AS value
6309
- FROM ${TABLE_SCORE_EVENTS} ${whereClause}
6432
+ FROM ${currentScoresRelation()} ${whereClause}
6310
6433
  GROUP BY bucket, ${resolved.map((e) => e.groupSql).join(", ")}
6311
6434
  ORDER BY bucket
6312
6435
  `, combined.params);
@@ -6328,7 +6451,7 @@ async function getScoreTimeSeries(client, args) {
6328
6451
  const rows = await queryJson(client, `
6329
6452
  SELECT toStartOfInterval(timestamp, ${intervalSql}) AS bucket,
6330
6453
  ${aggSql} AS value
6331
- FROM ${TABLE_SCORE_EVENTS} ${whereClause}
6454
+ FROM ${currentScoresRelation()} ${whereClause}
6332
6455
  GROUP BY bucket
6333
6456
  ORDER BY bucket
6334
6457
  `, combined.params);
@@ -6351,7 +6474,7 @@ async function getScorePercentiles(client, args) {
6351
6474
  const rows = await queryJson(client, `
6352
6475
  SELECT toStartOfInterval(timestamp, ${intervalSql}) AS bucket,
6353
6476
  quantile(${p})(score) AS pvalue
6354
- FROM ${TABLE_SCORE_EVENTS}
6477
+ FROM ${currentScoresRelation()}
6355
6478
  ${whereClause}
6356
6479
  GROUP BY bucket
6357
6480
  ORDER BY bucket
@@ -6720,11 +6843,9 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters)
6720
6843
  entityVersionId,
6721
6844
  parentEntityVersionId,
6722
6845
  rootEntityVersionId
6723
- FROM ${TABLE_SCORE_EVENTS}
6724
- WHERE isNotNull(traceId)
6725
- AND traceId IN (SELECT traceId FROM root_scope)
6726
- ORDER BY scoreId, timestamp DESC
6727
- LIMIT 1 BY scoreId
6846
+ FROM ${currentScoresRelation()} AS current
6847
+ WHERE isNotNull(current.traceId)
6848
+ AND current.traceId IN (SELECT traceId FROM root_scope)
6728
6849
  )`);
6729
6850
  if (relationCollections.has("feedback")) ctes.push(`current_feedback AS (
6730
6851
  SELECT
@@ -6773,6 +6894,46 @@ LIMIT ${parameters.add(plan.limit + 1, "UInt64")}`,
6773
6894
  };
6774
6895
  const orderField = resolveOrderField(plan.orderBy.field);
6775
6896
  const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
6897
+ if (plan.paginationMode === "page") return {
6898
+ query: `${candidates},
6899
+ page_rows AS (
6900
+ SELECT *, row_number() OVER (ORDER BY ${orderField} ${direction}, traceId ASC) AS __row_position
6901
+ FROM candidates
6902
+ ORDER BY ${orderField} ${direction}, traceId ASC
6903
+ LIMIT ${parameters.add(plan.perPage, "UInt64")} OFFSET ${parameters.add(plan.page * plan.perPage, "UInt64")}
6904
+ ),
6905
+ page_total AS (
6906
+ SELECT count() AS total
6907
+ FROM candidates
6908
+ )
6909
+ SELECT page_rows.*, page_total.total, 0 AS __metadata
6910
+ FROM page_rows
6911
+ CROSS JOIN page_total
6912
+ UNION ALL
6913
+ SELECT
6914
+ '' AS traceId,
6915
+ '' AS rootSpanId,
6916
+ '' AS name,
6917
+ CAST(NULL, 'Nullable(String)') AS entityId,
6918
+ CAST(NULL, 'Nullable(String)') AS parentSpanId,
6919
+ CAST(NULL, 'Nullable(String)') AS metadata,
6920
+ CAST(NULL, 'Nullable(String)') AS input,
6921
+ CAST(NULL, 'Nullable(String)') AS threadId,
6922
+ CAST(NULL, 'Nullable(String)') AS resourceId,
6923
+ toDateTime64(0, 3, 'UTC') AS startedAt,
6924
+ toDateTime64(0, 3, 'UTC') AS endedAt,
6925
+ CAST(NULL, 'Nullable(String)') AS entityName,
6926
+ CAST(NULL, 'Nullable(String)') AS entityType,
6927
+ CAST(NULL, 'Nullable(String)') AS environment,
6928
+ '' AS status,
6929
+ 0 AS __row_position,
6930
+ page_total.total AS total,
6931
+ 1 AS __metadata
6932
+ FROM page_total
6933
+ ORDER BY __metadata ASC, __row_position ASC`,
6934
+ query_params: parameters.params,
6935
+ sharedSnapshot: true
6936
+ };
6776
6937
  let pageCondition = "";
6777
6938
  if (plan.cursor) {
6778
6939
  const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
@@ -6919,7 +7080,8 @@ async function runWithClickHouseTraceQueryTimeout(client, limits, compiled, quer
6919
7080
  clickhouse_settings: {
6920
7081
  ...CH_SETTINGS,
6921
7082
  max_execution_time: resolvedTimeoutMs / 1e3,
6922
- ...limits.memoryLimitBytes === void 0 ? {} : { max_memory_usage: String(limits.memoryLimitBytes) }
7083
+ ...limits.memoryLimitBytes === void 0 ? {} : { max_memory_usage: String(limits.memoryLimitBytes) },
7084
+ ...compiled.sharedSnapshot ? { enable_shared_storage_snapshot_in_query: 1 } : {}
6923
7085
  }
6924
7086
  })).json();
6925
7087
  } catch (error) {
@@ -6950,6 +7112,37 @@ async function getTraceQueryValues(client, plan, limits) {
6950
7112
  });
6951
7113
  }
6952
7114
  async function queryTraces(client, plan, timeoutMs) {
7115
+ if (plan.paginationMode === "page") {
7116
+ const rows = await runWithClickHouseTraceQueryTimeout(client, { timeoutMs }, compileClickHouseTraceQuery(plan));
7117
+ const total = Number(rows.at(-1)?.total ?? 0);
7118
+ const traces = rows.filter((row) => Number(row.__metadata) === 0).map((row) => ({
7119
+ traceId: String(row.traceId),
7120
+ rootSpanId: String(row.rootSpanId),
7121
+ name: row.name,
7122
+ entityId: row.entityId ?? null,
7123
+ parentSpanId: row.parentSpanId ?? null,
7124
+ createdAt: asIsoTimestamp(row.startedAt),
7125
+ metadata: parseJson(row.metadata) ?? null,
7126
+ inputPreview: _mastra_core_storage.buildInputPreview(row.input) ?? null,
7127
+ threadId: row.threadId == null ? null : String(row.threadId),
7128
+ resourceId: row.resourceId == null ? null : String(row.resourceId),
7129
+ startedAt: asIsoTimestamp(row.startedAt),
7130
+ endedAt: asIsoTimestamp(row.endedAt),
7131
+ entityName: row.entityName == null ? null : String(row.entityName),
7132
+ entityType: row.entityType == null ? null : String(row.entityType),
7133
+ environment: row.environment == null ? null : String(row.environment),
7134
+ status: row.status
7135
+ }));
7136
+ return _mastra_core_storage.traceQueryResponseSchema.parse({
7137
+ traces,
7138
+ pagination: {
7139
+ total,
7140
+ page: plan.page,
7141
+ perPage: plan.perPage,
7142
+ hasMore: (plan.page + 1) * plan.perPage < total
7143
+ }
7144
+ });
7145
+ }
6953
7146
  const rows = await runWithClickHouseTraceQueryTimeout(client, { timeoutMs }, compileClickHouseTraceQuery(plan));
6954
7147
  const visibleRows = rows.slice(0, plan.limit);
6955
7148
  if (plan.result === "groups") {
@@ -7455,6 +7648,7 @@ async function batchDeleteTraces(client, args, replication) {
7455
7648
  TABLE_METRIC_EVENTS,
7456
7649
  TABLE_LOG_EVENTS,
7457
7650
  TABLE_SCORE_EVENTS,
7651
+ TABLE_SCORE_EVENTS_CURRENT,
7458
7652
  TABLE_FEEDBACK_EVENTS
7459
7653
  ];
7460
7654
  await Promise.all([...tracingTables.map((table) => client.command({
@@ -8134,10 +8328,13 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
8134
8328
  else if (existingStrategy) this.#deltaCursorStrategy = existingStrategy;
8135
8329
  else this.#deltaCursorStrategy = await detectDeltaCursorStrategy(this.#client, void 0, existingStrategy);
8136
8330
  await reconcileDiscoveryTables(this.#client, this.#replication);
8137
- const coreDdl = this.#deltaCursorStrategy === null ? [...BASE_TABLE_DDL, ...BASE_MV_DDL] : [...buildAllTableDDL(), ...buildAllMvDDL(this.#deltaCursorStrategy)];
8138
- for (const ddl of coreDdl) await this.#client.command({ query: applyReplicationToDDL(ddl, this.#replication) });
8331
+ const coreTableDdl = this.#deltaCursorStrategy === null ? BASE_TABLE_DDL : buildAllTableDDL();
8332
+ for (const ddl of coreTableDdl) await this.#client.command({ query: applyReplicationToDDL(ddl, this.#replication) });
8139
8333
  const pendingMigrations = await filterAppliedMigrations(this.#client, ALL_MIGRATIONS);
8140
8334
  for (const migration of pendingMigrations) await this.#client.command({ query: addOnClusterToDDL(migration.sql, this.#replication) });
8335
+ const coreMvDdl = this.#deltaCursorStrategy === null ? BASE_MV_DDL : buildAllMvDDL(this.#deltaCursorStrategy);
8336
+ for (const ddl of coreMvDdl) await this.#client.command({ query: applyReplicationToDDL(ddl, this.#replication) });
8337
+ await backfillCurrentScores(this.#client);
8141
8338
  if (this.#retention) await this.applyRetention();
8142
8339
  if (this.#deltaCursorStrategy === "serial") for (const counterName of DELTA_CURSOR_COUNTER_NAMES) await this.#client.query({
8143
8340
  query: `SELECT generateSerialID({counterName:String}) AS cursorId`,