@mastra/clickhouse 1.17.0-alpha.0 → 1.17.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/docs/references/integrations-databases-clickhouse.md +6 -0
- package/dist/index.cjs +183 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +182 -33
- package/dist/index.js.map +1 -1
- package/dist/storage/db/replication.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts +3 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/deletion-requests.d.ts +28 -0
- package/dist/storage/domains/observability/v-next/deletion-requests.d.ts.map +1 -0
- package/dist/storage/domains/observability/v-next/helpers.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +3 -0
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/tracing.d.ts +22 -7
- package/dist/storage/domains/observability/v-next/tracing.d.ts.map +1 -1
- package/dist/storage/index.d.ts +2 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/docs/SKILL.md
CHANGED
|
@@ -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.17.0
|
|
6
|
+
version: "1.17.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## When to use
|
|
@@ -87,6 +87,12 @@ export const mastra = new Mastra({
|
|
|
87
87
|
|
|
88
88
|
`MastraStorageExporter` automatically selects the `insert-only` strategy when ClickHouse is the observability backend, which gives the highest write throughput. See [tracing strategies](https://mastra.ai/docs/observability/integrations/exporters/mastra-storage) for details.
|
|
89
89
|
|
|
90
|
+
Trace deletion cascades to spans, trace roots and branches, metrics, logs, scores, and feedback linked by trace ID. Signals without a trace ID are preserved. Mastra records the deletion predicate, then waits for ClickHouse lightweight delete masks to be applied. Normal reads no longer return the rows matched by that operation when the call resolves.
|
|
91
|
+
|
|
92
|
+
Lightweight deletion is a hide-only operation that marks rows with ClickHouse's `_row_exists` mask. Physical removal depends on merges and deployment-configured retention TTLs. `ObservabilityStorageClickhouseVNext` applies retention only when you provide a `RetentionConfig`; Mastra OSS doesn't configure a default retention TTL.
|
|
93
|
+
|
|
94
|
+
Deletion requests aren't purged automatically in Mastra OSS. Automatic retirement will be introduced with future database-agnostic retention configuration.
|
|
95
|
+
|
|
90
96
|
### Observability with the legacy domain
|
|
91
97
|
|
|
92
98
|
`ObservabilityStorageClickhouse` is the original observability adapter and remains supported for projects that haven't migrated to the vNext schema. The configuration shape is the same as the vNext class.
|
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ let _mastra_core_agent = require("@mastra/core/agent");
|
|
|
29
29
|
let _mastra_core_base = require("@mastra/core/base");
|
|
30
30
|
let _mastra_core_utils = require("@mastra/core/utils");
|
|
31
31
|
let _mastra_core_features = require("@mastra/core/features");
|
|
32
|
+
let crypto$1 = require("crypto");
|
|
32
33
|
let _mastra_core_evals = require("@mastra/core/evals");
|
|
33
34
|
//#region src/storage/db/replication.ts
|
|
34
35
|
const DEFAULT_ZOOKEEPER_PATH = "/clickhouse/tables/{shard}/{database}/{table}";
|
|
@@ -94,12 +95,22 @@ function getEngineNameAndArgs(engine) {
|
|
|
94
95
|
}
|
|
95
96
|
function hasBalancedParens(s) {
|
|
96
97
|
let depth = 0;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
98
|
+
let quote;
|
|
99
|
+
for (let i = 0; i < s.length; i++) {
|
|
100
|
+
const c = s[i];
|
|
101
|
+
if (quote) {
|
|
102
|
+
if (c === "\\") i++;
|
|
103
|
+
else if (c === quote) quote = void 0;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (c === "`" || c === "\"" || c === "'") quote = c;
|
|
107
|
+
else if (c === "(") depth++;
|
|
108
|
+
else if (c === ")") {
|
|
109
|
+
depth--;
|
|
110
|
+
if (depth < 0) return false;
|
|
111
|
+
}
|
|
101
112
|
}
|
|
102
|
-
return depth === 0;
|
|
113
|
+
return depth === 0 && quote === void 0;
|
|
103
114
|
}
|
|
104
115
|
function buildReplicatedTableEngine(engine, replication) {
|
|
105
116
|
if (!replication) return engine;
|
|
@@ -171,20 +182,51 @@ function addOnClusterToDDL(sql, replication) {
|
|
|
171
182
|
return out;
|
|
172
183
|
}
|
|
173
184
|
function rewriteEngineClauses(sql, replication) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
185
|
+
const enginePattern = /ENGINE\s*=\s*(\w+)/gi;
|
|
186
|
+
let result = "";
|
|
187
|
+
let consumedUntil = 0;
|
|
188
|
+
for (let match = enginePattern.exec(sql); match; match = enginePattern.exec(sql)) {
|
|
189
|
+
const engineName = match[1];
|
|
190
|
+
if (!engineName) continue;
|
|
191
|
+
let engineEnd = enginePattern.lastIndex;
|
|
192
|
+
let argsStart = engineEnd;
|
|
193
|
+
while (/\s/.test(sql[argsStart] ?? "")) argsStart++;
|
|
194
|
+
let engine = engineName;
|
|
195
|
+
if (sql[argsStart] === "(") {
|
|
196
|
+
let depth = 0;
|
|
197
|
+
let closingParenEnd;
|
|
198
|
+
let quote;
|
|
199
|
+
for (let i = argsStart; i < sql.length; i++) {
|
|
200
|
+
const char = sql[i];
|
|
201
|
+
if (quote) {
|
|
202
|
+
if (char === "\\") i++;
|
|
203
|
+
else if (char === quote) quote = void 0;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (char === "`" || char === "\"" || char === "'") quote = char;
|
|
207
|
+
else if (char === "(") depth++;
|
|
208
|
+
else if (char === ")") {
|
|
209
|
+
depth--;
|
|
210
|
+
if (depth === 0) {
|
|
211
|
+
closingParenEnd = i + 1;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
184
215
|
}
|
|
216
|
+
if (closingParenEnd === void 0) {
|
|
217
|
+
result += sql.slice(consumedUntil, enginePattern.lastIndex);
|
|
218
|
+
consumedUntil = enginePattern.lastIndex;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
engineEnd = closingParenEnd;
|
|
222
|
+
engine = `${engineName}${sql.slice(argsStart, engineEnd)}`;
|
|
185
223
|
}
|
|
186
|
-
|
|
187
|
-
|
|
224
|
+
result += sql.slice(consumedUntil, match.index);
|
|
225
|
+
result += `ENGINE = ${buildReplicatedTableEngine(engine, replication)}`;
|
|
226
|
+
consumedUntil = engineEnd;
|
|
227
|
+
enginePattern.lastIndex = engineEnd;
|
|
228
|
+
}
|
|
229
|
+
return result + sql.slice(consumedUntil);
|
|
188
230
|
}
|
|
189
231
|
function applyReplicationToDDL(sql, replication) {
|
|
190
232
|
return addOnClusterToDDL(replication ? rewriteEngineClauses(sql, replication) : sql, replication);
|
|
@@ -2501,6 +2543,7 @@ var ObservabilityStorageClickhouse = class extends _mastra_core_storage.Observab
|
|
|
2501
2543
|
}
|
|
2502
2544
|
}
|
|
2503
2545
|
async batchDeleteTraces(args) {
|
|
2546
|
+
this.assertUnscopedBatchDeleteTraces(args);
|
|
2504
2547
|
try {
|
|
2505
2548
|
if (args.traceIds.length === 0) return;
|
|
2506
2549
|
await this.client.command({
|
|
@@ -2527,6 +2570,7 @@ const TABLE_METRIC_EVENTS = "mastra_metric_events";
|
|
|
2527
2570
|
const TABLE_LOG_EVENTS = "mastra_log_events";
|
|
2528
2571
|
const TABLE_SCORE_EVENTS = "mastra_score_events";
|
|
2529
2572
|
const TABLE_FEEDBACK_EVENTS = "mastra_feedback_events";
|
|
2573
|
+
const TABLE_DELETION_REQUESTS = "mastra_deletion_requests";
|
|
2530
2574
|
const TABLE_METRIC_EVENTS_DELTA = "mastra_metric_events_delta";
|
|
2531
2575
|
const TABLE_LOG_EVENTS_DELTA = "mastra_log_events_delta";
|
|
2532
2576
|
const TABLE_SCORE_EVENTS_DELTA = "mastra_score_events_delta";
|
|
@@ -3222,6 +3266,23 @@ PARTITION BY toDate(timestamp)
|
|
|
3222
3266
|
ORDER BY (traceId, timestamp, feedbackId)
|
|
3223
3267
|
SETTINGS allow_nullable_key = 1
|
|
3224
3268
|
`;
|
|
3269
|
+
const DELETION_REQUESTS_DDL = `
|
|
3270
|
+
CREATE TABLE IF NOT EXISTS ${TABLE_DELETION_REQUESTS} (
|
|
3271
|
+
requestId String,
|
|
3272
|
+
organizationId String DEFAULT '',
|
|
3273
|
+
resourceId String DEFAULT '',
|
|
3274
|
+
signal LowCardinality(String),
|
|
3275
|
+
predicateType LowCardinality(String),
|
|
3276
|
+
predicateValues Array(String),
|
|
3277
|
+
requestedAt DateTime64(3),
|
|
3278
|
+
requestedBy String DEFAULT '',
|
|
3279
|
+
lastAppliedAt DateTime64(3) DEFAULT 0,
|
|
3280
|
+
purgeVerifiedAt DateTime64(3) DEFAULT 0,
|
|
3281
|
+
updatedAt DateTime64(3)
|
|
3282
|
+
)
|
|
3283
|
+
ENGINE = ReplacingMergeTree(updatedAt)
|
|
3284
|
+
ORDER BY (organizationId, resourceId, requestId)
|
|
3285
|
+
`;
|
|
3225
3286
|
function buildFeedbackEventsDeltaDDL() {
|
|
3226
3287
|
return `
|
|
3227
3288
|
CREATE TABLE IF NOT EXISTS ${TABLE_FEEDBACK_EVENTS_DELTA} (
|
|
@@ -3371,6 +3432,7 @@ const BASE_TABLE_DDL = [
|
|
|
3371
3432
|
LOG_EVENTS_DDL,
|
|
3372
3433
|
SCORE_EVENTS_DDL,
|
|
3373
3434
|
FEEDBACK_EVENTS_DDL,
|
|
3435
|
+
DELETION_REQUESTS_DDL,
|
|
3374
3436
|
DISCOVERY_VALUES_DDL,
|
|
3375
3437
|
DISCOVERY_PAIRS_DDL
|
|
3376
3438
|
];
|
|
@@ -3455,6 +3517,7 @@ const ALL_TABLE_NAMES = [
|
|
|
3455
3517
|
TABLE_LOG_EVENTS,
|
|
3456
3518
|
TABLE_SCORE_EVENTS,
|
|
3457
3519
|
TABLE_FEEDBACK_EVENTS,
|
|
3520
|
+
TABLE_DELETION_REQUESTS,
|
|
3458
3521
|
TABLE_METRIC_EVENTS_DELTA,
|
|
3459
3522
|
TABLE_LOG_EVENTS_DELTA,
|
|
3460
3523
|
TABLE_SCORE_EVENTS_DELTA,
|
|
@@ -4112,6 +4175,35 @@ function feedbackRecordToRow(feedback) {
|
|
|
4112
4175
|
};
|
|
4113
4176
|
}
|
|
4114
4177
|
//#endregion
|
|
4178
|
+
//#region src/storage/domains/observability/v-next/deletion-requests.ts
|
|
4179
|
+
const EPOCH = "1970-01-01T00:00:00.000Z";
|
|
4180
|
+
async function recordDeletionRequest(client, args) {
|
|
4181
|
+
const row = {
|
|
4182
|
+
requestId: args.requestId,
|
|
4183
|
+
organizationId: args.organizationId ?? "",
|
|
4184
|
+
resourceId: args.resourceId ?? "",
|
|
4185
|
+
signal: args.signal,
|
|
4186
|
+
predicateType: args.predicateType,
|
|
4187
|
+
predicateValues: args.predicateValues,
|
|
4188
|
+
requestedAt: args.requestedAt,
|
|
4189
|
+
requestedBy: args.requestedBy ?? "",
|
|
4190
|
+
lastAppliedAt: EPOCH,
|
|
4191
|
+
purgeVerifiedAt: EPOCH,
|
|
4192
|
+
updatedAt: args.requestedAt
|
|
4193
|
+
};
|
|
4194
|
+
await client.insert({
|
|
4195
|
+
table: TABLE_DELETION_REQUESTS,
|
|
4196
|
+
values: [row],
|
|
4197
|
+
format: "JSONEachRow",
|
|
4198
|
+
clickhouse_settings: isReplicationConfigured(args.replication) ? {
|
|
4199
|
+
...CH_INSERT_SETTINGS,
|
|
4200
|
+
insert_quorum: "auto",
|
|
4201
|
+
insert_quorum_parallel: 1
|
|
4202
|
+
} : CH_INSERT_SETTINGS
|
|
4203
|
+
});
|
|
4204
|
+
return row;
|
|
4205
|
+
}
|
|
4206
|
+
//#endregion
|
|
4115
4207
|
//#region src/storage/domains/observability/v-next/discovery.ts
|
|
4116
4208
|
async function queryJson$3(client, query, params = {}) {
|
|
4117
4209
|
return await (await client.query({
|
|
@@ -6614,6 +6706,17 @@ function buildTraceCursor(row) {
|
|
|
6614
6706
|
}
|
|
6615
6707
|
//#endregion
|
|
6616
6708
|
//#region src/storage/domains/observability/v-next/tracing.ts
|
|
6709
|
+
/**
|
|
6710
|
+
* Tracing operations for ClickHouse v-next observability.
|
|
6711
|
+
*
|
|
6712
|
+
* Owns: batchCreateSpans, getSpan, getSpans, getTrace, getTraceLight,
|
|
6713
|
+
* listBranches, batchDeleteTraces, dangerouslyClearSpanEvents.
|
|
6714
|
+
* Delegates to trace-roots.ts: listTraces, getRootSpan.
|
|
6715
|
+
*
|
|
6716
|
+
* `listBranches` reads from the MV-fed `mastra_trace_branches` table (one row
|
|
6717
|
+
* per branch anchor span). It lives here -- alongside the other read paths
|
|
6718
|
+
* over the trace data -- since branches are conceptually a subset of traces.
|
|
6719
|
+
*/
|
|
6617
6720
|
const BRANCH_SPAN_TYPE_SQL_LIST = _mastra_core_storage.BRANCH_SPAN_TYPES.map((t) => `'${t}'`).join(", ");
|
|
6618
6721
|
/** Insert a single completed span. */
|
|
6619
6722
|
async function createSpan(client, args) {
|
|
@@ -6753,16 +6856,40 @@ async function getTraceLight(client, args) {
|
|
|
6753
6856
|
};
|
|
6754
6857
|
}
|
|
6755
6858
|
/**
|
|
6756
|
-
* Delete traces by traceId
|
|
6757
|
-
*
|
|
6859
|
+
* Delete traces by traceId, cascading to trace-derived tables and trace-linked
|
|
6860
|
+
* signal events (metrics, logs, scores, feedback). Signal rows with a NULL
|
|
6861
|
+
* traceId are never affected.
|
|
6862
|
+
*
|
|
6863
|
+
* On the tracing tables, rows are targeted by tracing identity: traceId +
|
|
6864
|
+
* dedupeKey (which starts with traceId). The dedupeKey condition is redundant
|
|
6865
|
+
* for correctness (dedupeKey = traceId:spanId) but satisfies the design-doc
|
|
6866
|
+
* requirement that trace deletes reference dedupeKey and helps the engine
|
|
6867
|
+
* narrow within the sorted ORDER BY key. Signal tables key on their own event
|
|
6868
|
+
* ids, so they are targeted by traceId alone.
|
|
6869
|
+
*
|
|
6870
|
+
* `trace_branches` must be deleted explicitly: its MV fires on insert only,
|
|
6871
|
+
* so span deletes never propagate to it. Delta tables self-expire via TTL and
|
|
6872
|
+
* discovery tables self-heal, so neither needs explicit deletes.
|
|
6758
6873
|
*
|
|
6759
|
-
*
|
|
6760
|
-
*
|
|
6761
|
-
*
|
|
6762
|
-
*
|
|
6874
|
+
* Records the predicate before using lightweight DELETE FROM on every table.
|
|
6875
|
+
* Lightweight deletes hide rows through ClickHouse's delete mask; physical
|
|
6876
|
+
* removal depends on the deployment's configured retention and merge policy.
|
|
6877
|
+
*
|
|
6878
|
+
* When the optional tenant scope (`organizationId` / `resourceId`) is set,
|
|
6879
|
+
* every DELETE additionally requires the row's tenant columns to match.
|
|
6763
6880
|
*/
|
|
6764
|
-
async function batchDeleteTraces(client, args) {
|
|
6881
|
+
async function batchDeleteTraces(client, args, replication) {
|
|
6765
6882
|
if (args.traceIds.length === 0) return;
|
|
6883
|
+
await recordDeletionRequest(client, {
|
|
6884
|
+
requestId: (0, crypto$1.randomUUID)(),
|
|
6885
|
+
organizationId: args.organizationId,
|
|
6886
|
+
resourceId: args.resourceId,
|
|
6887
|
+
signal: "traces",
|
|
6888
|
+
predicateType: "traceIds",
|
|
6889
|
+
predicateValues: [...args.traceIds],
|
|
6890
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6891
|
+
replication
|
|
6892
|
+
});
|
|
6766
6893
|
const params = {};
|
|
6767
6894
|
const traceInPlaceholders = [];
|
|
6768
6895
|
const dedupeOrParts = [];
|
|
@@ -6776,13 +6903,35 @@ async function batchDeleteTraces(client, args) {
|
|
|
6776
6903
|
}
|
|
6777
6904
|
const traceInList = traceInPlaceholders.join(", ");
|
|
6778
6905
|
const dedupeCondition = dedupeOrParts.length === 1 ? dedupeOrParts[0] : `(${dedupeOrParts.join(" OR ")})`;
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6906
|
+
let scopeCondition = "";
|
|
6907
|
+
if (args.organizationId !== void 0) {
|
|
6908
|
+
params.scope_org = args.organizationId;
|
|
6909
|
+
scopeCondition += ` AND organizationId = {scope_org:String}`;
|
|
6910
|
+
}
|
|
6911
|
+
if (args.resourceId !== void 0) {
|
|
6912
|
+
params.scope_res = args.resourceId;
|
|
6913
|
+
scopeCondition += ` AND resourceId = {scope_res:String}`;
|
|
6914
|
+
}
|
|
6915
|
+
const tracingTables = [
|
|
6916
|
+
TABLE_SPAN_EVENTS,
|
|
6917
|
+
TABLE_TRACE_ROOTS,
|
|
6918
|
+
TABLE_TRACE_BRANCHES
|
|
6919
|
+
];
|
|
6920
|
+
const signalTables = [
|
|
6921
|
+
TABLE_METRIC_EVENTS,
|
|
6922
|
+
TABLE_LOG_EVENTS,
|
|
6923
|
+
TABLE_SCORE_EVENTS,
|
|
6924
|
+
TABLE_FEEDBACK_EVENTS
|
|
6925
|
+
];
|
|
6926
|
+
await Promise.all([...tracingTables.map((table) => client.command({
|
|
6927
|
+
query: `DELETE FROM ${table} WHERE traceId IN (${traceInList}) AND ${dedupeCondition}${scopeCondition}`,
|
|
6928
|
+
query_params: params,
|
|
6929
|
+
clickhouse_settings: { lightweight_deletes_sync: "2" }
|
|
6930
|
+
})), ...signalTables.map((table) => client.command({
|
|
6931
|
+
query: `DELETE FROM ${table} WHERE traceId IN (${traceInList})${scopeCondition}`,
|
|
6932
|
+
query_params: params,
|
|
6933
|
+
clickhouse_settings: { lightweight_deletes_sync: "2" }
|
|
6934
|
+
}))]);
|
|
6786
6935
|
}
|
|
6787
6936
|
/**
|
|
6788
6937
|
* List trace branches with optional filtering, pagination, and ordering.
|
|
@@ -7985,7 +8134,7 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
|
|
|
7985
8134
|
}
|
|
7986
8135
|
async batchDeleteTraces(args) {
|
|
7987
8136
|
try {
|
|
7988
|
-
await batchDeleteTraces(this.#client, args);
|
|
8137
|
+
await batchDeleteTraces(this.#client, args, this.#replication);
|
|
7989
8138
|
} catch (error) {
|
|
7990
8139
|
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
7991
8140
|
throw new _mastra_core_error.MastraError({
|
|
@@ -8862,7 +9011,9 @@ exports.MemoryStorageClickhouse = MemoryStorageClickhouse;
|
|
|
8862
9011
|
exports.ObservabilityStorageClickhouse = ObservabilityStorageClickhouse;
|
|
8863
9012
|
exports.ObservabilityStorageClickhouseVNext = ObservabilityStorageClickhouseVNext;
|
|
8864
9013
|
exports.ScoresStorageClickhouse = ScoresStorageClickhouse;
|
|
9014
|
+
exports.TABLE_DELETION_REQUESTS = TABLE_DELETION_REQUESTS;
|
|
8865
9015
|
exports.TABLE_ENGINES = TABLE_ENGINES;
|
|
8866
9016
|
exports.WorkflowsStorageClickhouse = WorkflowsStorageClickhouse;
|
|
9017
|
+
exports.recordDeletionRequest = recordDeletionRequest;
|
|
8867
9018
|
|
|
8868
9019
|
//# sourceMappingURL=index.cjs.map
|