@mastra/clickhouse 1.21.0-alpha.2 → 1.21.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 +14 -1
- package/dist/index.cjs +121 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +121 -28
- 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/deletion-requests.d.ts +9 -0
- package/dist/storage/domains/observability/v-next/deletion-requests.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/feedback.d.ts +8 -4
- package/dist/storage/domains/observability/v-next/feedback.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/scores.d.ts +5 -3
- package/dist/storage/domains/observability/v-next/scores.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/tracing.d.ts +3 -1
- package/dist/storage/domains/observability/v-next/tracing.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -4272,17 +4272,46 @@ async function recordDeletionRequest(client, args) {
|
|
|
4272
4272
|
purgeVerifiedAt: EPOCH,
|
|
4273
4273
|
updatedAt: args.requestedAt
|
|
4274
4274
|
};
|
|
4275
|
+
await insertDeletionRequest(client, row, args.replication);
|
|
4276
|
+
return row;
|
|
4277
|
+
}
|
|
4278
|
+
/**
|
|
4279
|
+
* Marks a recorded deletion request as applied after its lightweight DELETEs
|
|
4280
|
+
* succeeded. The table is `ReplacingMergeTree(updatedAt)`, so re-inserting the
|
|
4281
|
+
* row with a newer `updatedAt` supersedes the pending version on merge and
|
|
4282
|
+
* under `FINAL`. Requests whose DELETE failed keep `lastAppliedAt` at the
|
|
4283
|
+
* epoch; mutation guards ignore them, and re-invoking the delete API records a
|
|
4284
|
+
* new request and converges.
|
|
4285
|
+
*/
|
|
4286
|
+
async function markDeletionRequestApplied(client, row, replication) {
|
|
4287
|
+
const pendingAt = Date.parse(row.updatedAt);
|
|
4288
|
+
const appliedAt = new Date(Number.isFinite(pendingAt) ? Math.max(Date.now(), pendingAt + 1) : Date.now()).toISOString();
|
|
4289
|
+
const applied = {
|
|
4290
|
+
...row,
|
|
4291
|
+
lastAppliedAt: appliedAt,
|
|
4292
|
+
updatedAt: appliedAt
|
|
4293
|
+
};
|
|
4294
|
+
await insertDeletionRequest(client, applied, replication);
|
|
4295
|
+
return applied;
|
|
4296
|
+
}
|
|
4297
|
+
/**
|
|
4298
|
+
* Replicated clusters insert with parallel quorum, the same as every other
|
|
4299
|
+
* deletion-request write on main, so concurrent deletes never reject each other.
|
|
4300
|
+
* The guard that reads these rows is advisory: review updates mutate feedback
|
|
4301
|
+
* in place and preserve the delete mask, so a replica that has not yet received
|
|
4302
|
+
* a marker can only mis-report a status, never bring deleted feedback back.
|
|
4303
|
+
*/
|
|
4304
|
+
async function insertDeletionRequest(client, row, replication) {
|
|
4275
4305
|
await client.insert({
|
|
4276
4306
|
table: TABLE_DELETION_REQUESTS,
|
|
4277
4307
|
values: [row],
|
|
4278
4308
|
format: "JSONEachRow",
|
|
4279
|
-
clickhouse_settings: isReplicationConfigured(
|
|
4309
|
+
clickhouse_settings: isReplicationConfigured(replication) ? {
|
|
4280
4310
|
...CH_INSERT_SETTINGS,
|
|
4281
4311
|
insert_quorum: "auto",
|
|
4282
4312
|
insert_quorum_parallel: 1
|
|
4283
4313
|
} : CH_INSERT_SETTINGS
|
|
4284
4314
|
});
|
|
4285
|
-
return row;
|
|
4286
4315
|
}
|
|
4287
4316
|
//#endregion
|
|
4288
4317
|
//#region src/storage/domains/observability/v-next/discovery.ts
|
|
@@ -4774,14 +4803,18 @@ async function batchCreateFeedback(client, args) {
|
|
|
4774
4803
|
* `organizationId` and `resourceId` values are ANDed into the predicate to
|
|
4775
4804
|
* restrict deletion to records with matching scope fields.
|
|
4776
4805
|
*
|
|
4777
|
-
* A durable deletion request is recorded before the lightweight delete
|
|
4778
|
-
*
|
|
4779
|
-
*
|
|
4806
|
+
* A durable deletion request is recorded before the lightweight delete and
|
|
4807
|
+
* marked applied once the delete succeeds. If the delete fails, the request
|
|
4808
|
+
* stays unapplied and does not block updates to the still-visible rows; retry
|
|
4809
|
+
* by calling this function again.
|
|
4810
|
+
*
|
|
4811
|
+
* The delete is immediately visible to subsequent reads; physical purge depends
|
|
4812
|
+
* on the table's configured retention TTL. The delta table is intentionally not
|
|
4780
4813
|
* touched and expires through its fixed two-day TTL.
|
|
4781
4814
|
*/
|
|
4782
4815
|
async function deleteFeedback(client, args, replication) {
|
|
4783
4816
|
if (args.feedbackIds.length === 0) return;
|
|
4784
|
-
await recordDeletionRequest(client, {
|
|
4817
|
+
const request = await recordDeletionRequest(client, {
|
|
4785
4818
|
requestId: randomUUID(),
|
|
4786
4819
|
organizationId: args.organizationId,
|
|
4787
4820
|
resourceId: args.resourceId,
|
|
@@ -4812,6 +4845,7 @@ async function deleteFeedback(client, args, replication) {
|
|
|
4812
4845
|
query_params: params,
|
|
4813
4846
|
clickhouse_settings: { lightweight_deletes_sync: isReplicationConfigured(replication) ? "2" : "1" }
|
|
4814
4847
|
});
|
|
4848
|
+
await markDeletionRequestApplied(client, request, replication);
|
|
4815
4849
|
}
|
|
4816
4850
|
function feedbackNotFoundError(feedbackId) {
|
|
4817
4851
|
return new MastraError({
|
|
@@ -4822,11 +4856,29 @@ function feedbackNotFoundError(feedbackId) {
|
|
|
4822
4856
|
details: { feedbackId }
|
|
4823
4857
|
});
|
|
4824
4858
|
}
|
|
4859
|
+
function feedbackConflictError(feedbackId) {
|
|
4860
|
+
return new MastraError({
|
|
4861
|
+
id: "OBSERVABILITY_UPDATE_FEEDBACK_REVIEW_STATUS_CONFLICT",
|
|
4862
|
+
domain: ErrorDomain.MASTRA_OBSERVABILITY,
|
|
4863
|
+
category: ErrorCategory.USER,
|
|
4864
|
+
text: "Feedback record changed while its review status was being updated; retry the update",
|
|
4865
|
+
details: { feedbackId }
|
|
4866
|
+
});
|
|
4867
|
+
}
|
|
4868
|
+
/** Re-reads before giving up when ingestion keeps superseding the observed row. */
|
|
4869
|
+
const REVIEW_UPDATE_ATTEMPTS = 3;
|
|
4870
|
+
/**
|
|
4871
|
+
* Only applied requests block a review update. The guard decides the reply,
|
|
4872
|
+
* not data safety: the update mutates the row in place and preserves the
|
|
4873
|
+
* delete mask, so a replica that has not received a marker yet can only
|
|
4874
|
+
* mis-report a status, never bring deleted feedback back.
|
|
4875
|
+
*/
|
|
4825
4876
|
async function hasFeedbackDeletionRequest(client, feedbackId, organizationId, resourceId) {
|
|
4826
4877
|
return (await queryJson$2(client, `SELECT 1 AS found FROM ${TABLE_DELETION_REQUESTS} FINAL
|
|
4827
4878
|
WHERE signal = 'feedback'
|
|
4828
4879
|
AND predicateType = 'itemIds'
|
|
4829
4880
|
AND has(predicateValues, {feedbackId:String})
|
|
4881
|
+
AND lastAppliedAt > toDateTime64(0, 3)
|
|
4830
4882
|
AND (organizationId = '' OR organizationId = {organizationId:String})
|
|
4831
4883
|
AND (resourceId = '' OR resourceId = {resourceId:String})
|
|
4832
4884
|
LIMIT 1`, {
|
|
@@ -4835,28 +4887,63 @@ async function hasFeedbackDeletionRequest(client, feedbackId, organizationId, re
|
|
|
4835
4887
|
resourceId: resourceId ?? ""
|
|
4836
4888
|
})).length > 0;
|
|
4837
4889
|
}
|
|
4838
|
-
async function updateFeedbackReviewStatus(client, args,
|
|
4890
|
+
async function updateFeedbackReviewStatus(client, args, strategy = null) {
|
|
4839
4891
|
const { feedbackId, reviewStatus } = parseUpdateFeedbackReviewStatusArgs(args);
|
|
4840
|
-
|
|
4892
|
+
for (let attempt = 1;; attempt++) {
|
|
4893
|
+
const updated = await applyReviewStatus(client, feedbackId, reviewStatus, strategy);
|
|
4894
|
+
if (updated) return updated;
|
|
4895
|
+
if (attempt >= REVIEW_UPDATE_ATTEMPTS) throw feedbackConflictError(feedbackId);
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
/**
|
|
4899
|
+
* One read-guard-mutate-verify pass. Returns `null` when the read-back does not
|
|
4900
|
+
* show the new status: a newer version superseded the observed row, a delete
|
|
4901
|
+
* hid it after the guard read (the next pass then reports not found), or the
|
|
4902
|
+
* mutation left the row untouched.
|
|
4903
|
+
*/
|
|
4904
|
+
async function applyReviewStatus(client, feedbackId, reviewStatus, strategy) {
|
|
4905
|
+
const existingRow = (await queryJson$2(client, `SELECT *, toString(writeVersion) AS reviewWriteVersion FROM ${TABLE_FEEDBACK_EVENTS} FINAL
|
|
4841
4906
|
WHERE feedbackId = {feedbackId:String}
|
|
4842
4907
|
ORDER BY writeVersion DESC, timestamp DESC
|
|
4843
4908
|
LIMIT 1`, { feedbackId }))[0];
|
|
4844
4909
|
if (!existingRow) throw feedbackNotFoundError(feedbackId);
|
|
4845
4910
|
if (await hasFeedbackDeletionRequest(client, feedbackId, existingRow.organizationId, existingRow.resourceId)) throw feedbackNotFoundError(feedbackId);
|
|
4846
|
-
const
|
|
4847
|
-
|
|
4911
|
+
const identity = `feedbackId = {feedbackId:String}
|
|
4912
|
+
AND timestamp = parseDateTime64BestEffort({timestamp:String}, 3, 'UTC')
|
|
4913
|
+
AND (traceId = {traceId:Nullable(String)} OR (isNull(traceId) AND isNull({traceId:Nullable(String)})))
|
|
4914
|
+
AND writeVersion = {writeVersion:UInt64}`;
|
|
4915
|
+
const params = {
|
|
4916
|
+
feedbackId,
|
|
4917
|
+
timestamp: existingRow.timestamp,
|
|
4918
|
+
traceId: existingRow.traceId,
|
|
4919
|
+
writeVersion: existingRow.reviewWriteVersion,
|
|
4848
4920
|
reviewStatus
|
|
4921
|
+
};
|
|
4922
|
+
await client.command({
|
|
4923
|
+
query: `ALTER TABLE ${TABLE_FEEDBACK_EVENTS} UPDATE reviewStatus = {reviewStatus:String} WHERE ${identity}`,
|
|
4924
|
+
query_params: params,
|
|
4925
|
+
clickhouse_settings: {
|
|
4926
|
+
...CH_SETTINGS,
|
|
4927
|
+
mutations_sync: "1"
|
|
4928
|
+
}
|
|
4849
4929
|
});
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4930
|
+
if (strategy !== null) await client.command({
|
|
4931
|
+
query: `INSERT INTO ${TABLE_FEEDBACK_EVENTS_DELTA}
|
|
4932
|
+
SELECT ${buildDeltaCursorExpr(strategy, "mastra_feedback_events_delta_cursor", "feedbackId")} AS cursorId,
|
|
4933
|
+
ingestedAt, traceId, timestamp, feedbackId
|
|
4934
|
+
FROM (
|
|
4935
|
+
SELECT now64(9, 'UTC') AS ingestedAt, traceId, timestamp, feedbackId
|
|
4936
|
+
FROM ${TABLE_FEEDBACK_EVENTS} FINAL WHERE ${identity}
|
|
4937
|
+
)`,
|
|
4938
|
+
query_params: params,
|
|
4939
|
+
clickhouse_settings: {
|
|
4940
|
+
...CH_INSERT_SETTINGS,
|
|
4941
|
+
async_insert: 0
|
|
4942
|
+
}
|
|
4943
|
+
});
|
|
4944
|
+
if (await hasFeedbackDeletionRequest(client, feedbackId, existingRow.organizationId, existingRow.resourceId)) throw feedbackNotFoundError(feedbackId);
|
|
4945
|
+
const current = await queryJson$2(client, `SELECT * FROM ${TABLE_FEEDBACK_EVENTS} FINAL WHERE ${identity} LIMIT 1`, params);
|
|
4946
|
+
return current[0]?.reviewStatus === reviewStatus ? rowToFeedbackRecord(current[0]) : null;
|
|
4860
4947
|
}
|
|
4861
4948
|
async function listFeedback(client, args, strategy) {
|
|
4862
4949
|
const parsed = listFeedbackArgsSchema.parse(args);
|
|
@@ -6205,14 +6292,16 @@ async function batchCreateScores(client, args) {
|
|
|
6205
6292
|
* `organizationId` and `resourceId` values are ANDed into the predicate to
|
|
6206
6293
|
* restrict deletion to records with matching scope fields.
|
|
6207
6294
|
*
|
|
6208
|
-
* A durable deletion request is recorded before the lightweight delete
|
|
6209
|
-
*
|
|
6210
|
-
*
|
|
6295
|
+
* A durable deletion request is recorded before the lightweight delete and
|
|
6296
|
+
* marked applied once the delete succeeds. If the delete fails, the request
|
|
6297
|
+
* stays unapplied; retry by calling this function again. The delete is
|
|
6298
|
+
* immediately visible to subsequent reads; physical purge depends on the
|
|
6299
|
+
* table's configured retention TTL. The delta table is intentionally not
|
|
6211
6300
|
* touched and expires through its fixed two-day TTL.
|
|
6212
6301
|
*/
|
|
6213
6302
|
async function deleteScores(client, args, replication) {
|
|
6214
6303
|
if (args.scoreIds.length === 0) return;
|
|
6215
|
-
await recordDeletionRequest(client, {
|
|
6304
|
+
const request = await recordDeletionRequest(client, {
|
|
6216
6305
|
requestId: randomUUID(),
|
|
6217
6306
|
organizationId: args.organizationId,
|
|
6218
6307
|
resourceId: args.resourceId,
|
|
@@ -6244,6 +6333,7 @@ async function deleteScores(client, args, replication) {
|
|
|
6244
6333
|
query_params: params,
|
|
6245
6334
|
clickhouse_settings
|
|
6246
6335
|
});
|
|
6336
|
+
await markDeletionRequestApplied(client, request, replication);
|
|
6247
6337
|
}
|
|
6248
6338
|
async function listScores(client, args, strategy) {
|
|
6249
6339
|
const parsed = listScoresArgsSchema.parse(args);
|
|
@@ -7709,7 +7799,9 @@ async function getTraceLight(client, args) {
|
|
|
7709
7799
|
* so span deletes never propagate to it. Delta tables self-expire via TTL and
|
|
7710
7800
|
* discovery tables self-heal, so neither needs explicit deletes.
|
|
7711
7801
|
*
|
|
7712
|
-
* Records the predicate before using lightweight DELETE FROM on every table
|
|
7802
|
+
* Records the predicate before using lightweight DELETE FROM on every table
|
|
7803
|
+
* and marks the request applied once every delete succeeds. If any delete
|
|
7804
|
+
* fails, the request stays unapplied; retry by calling this function again.
|
|
7713
7805
|
* Lightweight deletes hide rows through ClickHouse's delete mask; physical
|
|
7714
7806
|
* removal depends on the deployment's configured retention and merge policy.
|
|
7715
7807
|
*
|
|
@@ -7718,7 +7810,7 @@ async function getTraceLight(client, args) {
|
|
|
7718
7810
|
*/
|
|
7719
7811
|
async function batchDeleteTraces(client, args, replication) {
|
|
7720
7812
|
if (args.traceIds.length === 0) return;
|
|
7721
|
-
await recordDeletionRequest(client, {
|
|
7813
|
+
const request = await recordDeletionRequest(client, {
|
|
7722
7814
|
requestId: randomUUID(),
|
|
7723
7815
|
organizationId: args.organizationId,
|
|
7724
7816
|
resourceId: args.resourceId,
|
|
@@ -7771,6 +7863,7 @@ async function batchDeleteTraces(client, args, replication) {
|
|
|
7771
7863
|
query_params: params,
|
|
7772
7864
|
clickhouse_settings: { lightweight_deletes_sync: "2" }
|
|
7773
7865
|
}))]);
|
|
7866
|
+
await markDeletionRequestApplied(client, request, replication);
|
|
7774
7867
|
}
|
|
7775
7868
|
/**
|
|
7776
7869
|
* List trace branches with optional filtering, pagination, and ordering.
|
|
@@ -8903,7 +8996,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
8903
8996
|
}
|
|
8904
8997
|
async updateFeedbackReviewStatus(args) {
|
|
8905
8998
|
try {
|
|
8906
|
-
return await updateFeedbackReviewStatus(this.#client, args, this.#
|
|
8999
|
+
return await updateFeedbackReviewStatus(this.#client, args, deltaPollingSupported(this.#deltaCursorStrategy) ? this.#deltaCursorStrategy : null);
|
|
8907
9000
|
} catch (error) {
|
|
8908
9001
|
if (error instanceof MastraError) throw error;
|
|
8909
9002
|
throw new MastraError({
|
|
@@ -10042,6 +10135,6 @@ var ClickhouseStoreVNext = class extends ClickhouseStore {
|
|
|
10042
10135
|
}
|
|
10043
10136
|
};
|
|
10044
10137
|
//#endregion
|
|
10045
|
-
export { COLUMN_TYPES, ClickhouseStore, ClickhouseStoreVNext, MemoryStorageClickhouse, ObservabilityStorageClickhouse, ObservabilityStorageClickhouseVNext, ScoresStorageClickhouse, TABLE_DELETION_REQUESTS, TABLE_ENGINES, WorkflowsStorageClickhouse, applyClickHouseRetention, recordDeletionRequest };
|
|
10138
|
+
export { COLUMN_TYPES, ClickhouseStore, ClickhouseStoreVNext, MemoryStorageClickhouse, ObservabilityStorageClickhouse, ObservabilityStorageClickhouseVNext, ScoresStorageClickhouse, TABLE_DELETION_REQUESTS, TABLE_ENGINES, WorkflowsStorageClickhouse, applyClickHouseRetention, markDeletionRequestApplied, recordDeletionRequest };
|
|
10046
10139
|
|
|
10047
10140
|
//# sourceMappingURL=index.js.map
|