@mastra/clickhouse 1.20.0 → 1.20.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +72 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +72 -17
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts +1 -0
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +15 -0
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/scores.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -3200,7 +3200,8 @@ CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS_DELTA} (
|
|
|
3200
3200
|
ingestedAt ${DELTA_INGESTED_AT_TYPE},
|
|
3201
3201
|
traceId Nullable(String),
|
|
3202
3202
|
timestamp DateTime64(3, 'UTC'),
|
|
3203
|
-
scoreId String
|
|
3203
|
+
scoreId String,
|
|
3204
|
+
INDEX idx_scoreId scoreId TYPE bloom_filter(0.01) GRANULARITY 1
|
|
3204
3205
|
)
|
|
3205
3206
|
ENGINE = MergeTree
|
|
3206
3207
|
PARTITION BY toDate(ingestedAt)
|
|
@@ -3209,12 +3210,8 @@ TTL ingestedAt + toIntervalDay(2)
|
|
|
3209
3210
|
SETTINGS allow_nullable_key = 1
|
|
3210
3211
|
`;
|
|
3211
3212
|
}
|
|
3212
|
-
function
|
|
3213
|
-
return `
|
|
3214
|
-
CREATE MATERIALIZED VIEW IF NOT EXISTS ${MV_SCORE_EVENTS_DELTA}
|
|
3215
|
-
TO ${TABLE_SCORE_EVENTS_DELTA}
|
|
3216
|
-
AS
|
|
3217
|
-
SELECT
|
|
3213
|
+
function buildScoreEventsDeltaMvQuery(strategy) {
|
|
3214
|
+
return `SELECT
|
|
3218
3215
|
${buildDeltaCursorExpr(strategy, "mastra_score_events_delta_cursor", "scoreId")} AS cursorId,
|
|
3219
3216
|
ingestedAt,
|
|
3220
3217
|
traceId,
|
|
@@ -3227,7 +3224,19 @@ FROM (
|
|
|
3227
3224
|
timestamp,
|
|
3228
3225
|
scoreId
|
|
3229
3226
|
FROM ${TABLE_SCORE_EVENTS}
|
|
3230
|
-
|
|
3227
|
+
WHERE scoreId NOT IN (
|
|
3228
|
+
SELECT scoreId FROM ${TABLE_SCORE_EVENTS_DELTA}
|
|
3229
|
+
WHERE scoreId IN (SELECT scoreId FROM ${TABLE_SCORE_EVENTS})
|
|
3230
|
+
)
|
|
3231
|
+
LIMIT 1 BY scoreId
|
|
3232
|
+
)`;
|
|
3233
|
+
}
|
|
3234
|
+
function buildScoreEventsDeltaMvDDL(strategy) {
|
|
3235
|
+
return `
|
|
3236
|
+
CREATE MATERIALIZED VIEW IF NOT EXISTS ${MV_SCORE_EVENTS_DELTA}
|
|
3237
|
+
TO ${TABLE_SCORE_EVENTS_DELTA}
|
|
3238
|
+
AS
|
|
3239
|
+
${buildScoreEventsDeltaMvQuery(strategy)}
|
|
3231
3240
|
`;
|
|
3232
3241
|
}
|
|
3233
3242
|
const FEEDBACK_EVENTS_DDL = `
|
|
@@ -3536,6 +3545,7 @@ const ALL_MIGRATIONS = [
|
|
|
3536
3545
|
addColumn(TABLE_SCORE_EVENTS, "parentEntityVersionId", "Nullable(String)"),
|
|
3537
3546
|
addColumn(TABLE_SCORE_EVENTS, "rootEntityVersionId", "Nullable(String)"),
|
|
3538
3547
|
addBloomIndex(TABLE_SCORE_EVENTS, "idx_scoreId", "scoreId", 1),
|
|
3548
|
+
addBloomIndex(TABLE_SCORE_EVENTS_DELTA, "idx_scoreId", "scoreId", 1),
|
|
3539
3549
|
addColumn(TABLE_FEEDBACK_EVENTS, "writeVersion", "UInt64 DEFAULT 0"),
|
|
3540
3550
|
addColumn(TABLE_FEEDBACK_EVENTS, "entityVersionId", "Nullable(String)"),
|
|
3541
3551
|
addColumn(TABLE_FEEDBACK_EVENTS, "reviewStatus", "LowCardinality(String) DEFAULT 'needs-review'"),
|
|
@@ -6285,6 +6295,17 @@ async function listScores(client, args, strategy) {
|
|
|
6285
6295
|
...deltaCursorEnabled ? { deltaCursor: currentDeltaCursor } : {}
|
|
6286
6296
|
};
|
|
6287
6297
|
}
|
|
6298
|
+
/**
|
|
6299
|
+
* Delta reads join the delta stream to the current-state table by scoreId, so
|
|
6300
|
+
* a poll or replay always returns the latest write of a score (a rewrite may
|
|
6301
|
+
* change traceId or timestamp, which the delta row does not track).
|
|
6302
|
+
*
|
|
6303
|
+
* A score's first delta row is its only cursor: rows after `afterCursor` are
|
|
6304
|
+
* dropped when the same scoreId already has a row at or before it, and
|
|
6305
|
+
* `LIMIT 1 BY` keeps the lowest cursor within the page. Duplicate delta rows
|
|
6306
|
+
* (a concurrent-insert race past the MV check) therefore never surface, in
|
|
6307
|
+
* this poll or a later one.
|
|
6308
|
+
*/
|
|
6288
6309
|
async function queryScoresAfterCursor(client, whereClause, params, limit, cursorId) {
|
|
6289
6310
|
return await queryJson(client, `
|
|
6290
6311
|
SELECT
|
|
@@ -6294,12 +6315,16 @@ async function queryScoresAfterCursor(client, whereClause, params, limit, cursor
|
|
|
6294
6315
|
s.scoreId AS scoreId,
|
|
6295
6316
|
toString(d.cursorId) AS cursorId
|
|
6296
6317
|
FROM ${TABLE_SCORE_EVENTS_DELTA} d
|
|
6297
|
-
INNER JOIN ${
|
|
6298
|
-
ON
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6318
|
+
INNER JOIN ${TABLE_SCORE_EVENTS_CURRENT} s FINAL
|
|
6319
|
+
ON s.scoreId = d.scoreId
|
|
6320
|
+
${whereClause ? `${whereClause} AND` : "WHERE"} d.cursorId > {afterCursor:UInt64}
|
|
6321
|
+
AND d.scoreId NOT IN (
|
|
6322
|
+
SELECT scoreId FROM ${TABLE_SCORE_EVENTS_DELTA}
|
|
6323
|
+
WHERE cursorId <= {afterCursor:UInt64}
|
|
6324
|
+
AND scoreId IN (SELECT scoreId FROM ${TABLE_SCORE_EVENTS_DELTA} WHERE cursorId > {afterCursor:UInt64})
|
|
6325
|
+
)
|
|
6302
6326
|
ORDER BY d.cursorId ASC
|
|
6327
|
+
LIMIT 1 BY s.scoreId
|
|
6303
6328
|
LIMIT {fetchLimit:UInt32}
|
|
6304
6329
|
`, {
|
|
6305
6330
|
...params,
|
|
@@ -6311,10 +6336,8 @@ async function getDeltaCursor$2(client, whereClause, params) {
|
|
|
6311
6336
|
const cursorId = (await queryJson(client, `
|
|
6312
6337
|
SELECT toString(max(d.cursorId)) AS cursorId
|
|
6313
6338
|
FROM mastra_score_events_delta d
|
|
6314
|
-
INNER JOIN
|
|
6315
|
-
ON
|
|
6316
|
-
AND s.timestamp = d.timestamp
|
|
6317
|
-
AND s.scoreId = d.scoreId
|
|
6339
|
+
INNER JOIN mastra_score_events_current s FINAL
|
|
6340
|
+
ON s.scoreId = d.scoreId
|
|
6318
6341
|
${whereClause}
|
|
6319
6342
|
`, params))[0]?.cursorId ?? null;
|
|
6320
6343
|
if (cursorId) return cursorId;
|
|
@@ -8284,6 +8307,37 @@ async function queryNamesByTable(client, query, tables) {
|
|
|
8284
8307
|
}
|
|
8285
8308
|
return out;
|
|
8286
8309
|
}
|
|
8310
|
+
/**
|
|
8311
|
+
* Upgrades a score delta MV created before per-scoreId deduplication with
|
|
8312
|
+
* `ALTER TABLE ... MODIFY QUERY`. The view is changed in place, so there is no
|
|
8313
|
+
* window without a view in which score writes would miss their delta row (a
|
|
8314
|
+
* drop-and-recreate would leave such writes out of delta polling for good).
|
|
8315
|
+
*
|
|
8316
|
+
* With a cluster configured the definition is read from every replica via
|
|
8317
|
+
* `clusterAllReplicas`, since each host stores its own copy of the view; a
|
|
8318
|
+
* single legacy copy is enough to alter `ON CLUSTER`.
|
|
8319
|
+
*
|
|
8320
|
+
* Introspection failures are logged and skipped, like the other schema checks
|
|
8321
|
+
* in init(): the existing view keeps working and the next boot retries.
|
|
8322
|
+
*/
|
|
8323
|
+
async function reconcileScoreDeltaMv(client, replication, strategy, logger) {
|
|
8324
|
+
const cluster = replication?.cluster?.trim();
|
|
8325
|
+
let createQueries;
|
|
8326
|
+
try {
|
|
8327
|
+
createQueries = (await (await client.query({
|
|
8328
|
+
query: cluster ? `SELECT create_table_query FROM clusterAllReplicas({cluster:String}, system.tables) WHERE database = currentDatabase() AND name = {name:String}` : `SELECT create_table_query FROM system.tables WHERE database = currentDatabase() AND name = {name:String}`,
|
|
8329
|
+
query_params: cluster ? {
|
|
8330
|
+
cluster,
|
|
8331
|
+
name: MV_SCORE_EVENTS_DELTA
|
|
8332
|
+
} : { name: MV_SCORE_EVENTS_DELTA },
|
|
8333
|
+
format: "JSONEachRow"
|
|
8334
|
+
})).json()).map((row) => row.create_table_query ?? "");
|
|
8335
|
+
} catch (error) {
|
|
8336
|
+
logger?.warn?.(`Could not verify the ${MV_SCORE_EVENTS_DELTA} definition; leaving the existing view in place: ${error instanceof Error ? error.message : String(error)}`);
|
|
8337
|
+
return;
|
|
8338
|
+
}
|
|
8339
|
+
if (createQueries.some((createQuery) => createQuery.length > 0 && !/NOT IN/i.test(createQuery))) await client.command({ query: addOnClusterToDDL(`ALTER TABLE ${MV_SCORE_EVENTS_DELTA} MODIFY QUERY ${buildScoreEventsDeltaMvQuery(strategy)}`, replication) });
|
|
8340
|
+
}
|
|
8287
8341
|
async function detectDeltaCursorStrategy(client, override, existingStrategy) {
|
|
8288
8342
|
if (override) return override;
|
|
8289
8343
|
if (existingStrategy && existingStrategy !== "mixed") return existingStrategy;
|
|
@@ -8393,6 +8447,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
8393
8447
|
for (const migration of pendingMigrations) await this.#client.command({ query: addOnClusterToDDL(migration.sql, this.#replication) });
|
|
8394
8448
|
const coreMvDdl = this.#deltaCursorStrategy === null ? BASE_MV_DDL : buildAllMvDDL(this.#deltaCursorStrategy);
|
|
8395
8449
|
for (const ddl of coreMvDdl) await this.#client.command({ query: applyReplicationToDDL(ddl, this.#replication) });
|
|
8450
|
+
if (this.#deltaCursorStrategy !== null) await reconcileScoreDeltaMv(this.#client, this.#replication, this.#deltaCursorStrategy, this.logger);
|
|
8396
8451
|
await backfillCurrentScores(this.#client);
|
|
8397
8452
|
if (this.#retention) await this.applyRetention();
|
|
8398
8453
|
if (this.#deltaCursorStrategy === "serial") for (const counterName of DELTA_CURSOR_COUNTER_NAMES) await this.#client.query({
|