@mastra/clickhouse 1.21.0 → 1.21.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/dist/index.js CHANGED
@@ -4856,29 +4856,16 @@ function feedbackNotFoundError(feedbackId) {
4856
4856
  details: { feedbackId }
4857
4857
  });
4858
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
4859
  /**
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.
4860
+ * Finds a deletion request covering this feedback. With `appliedOnly`, only
4861
+ * requests whose DELETE succeeded count; otherwise pending requests count too,
4862
+ * including a delete that is still running or one that failed.
4875
4863
  */
4876
- async function hasFeedbackDeletionRequest(client, feedbackId, organizationId, resourceId) {
4864
+ async function hasFeedbackDeletionRequest(client, feedbackId, organizationId, resourceId, { appliedOnly }) {
4877
4865
  return (await queryJson$2(client, `SELECT 1 AS found FROM ${TABLE_DELETION_REQUESTS} FINAL
4878
4866
  WHERE signal = 'feedback'
4879
4867
  AND predicateType = 'itemIds'
4880
- AND has(predicateValues, {feedbackId:String})
4881
- AND lastAppliedAt > toDateTime64(0, 3)
4868
+ AND has(predicateValues, {feedbackId:String})${appliedOnly ? "\n AND lastAppliedAt > toDateTime64(0, 3)" : ""}
4882
4869
  AND (organizationId = '' OR organizationId = {organizationId:String})
4883
4870
  AND (resourceId = '' OR resourceId = {resourceId:String})
4884
4871
  LIMIT 1`, {
@@ -4887,63 +4874,44 @@ async function hasFeedbackDeletionRequest(client, feedbackId, organizationId, re
4887
4874
  resourceId: resourceId ?? ""
4888
4875
  })).length > 0;
4889
4876
  }
4890
- async function updateFeedbackReviewStatus(client, args, strategy = null) {
4891
- const { feedbackId, reviewStatus } = parseUpdateFeedbackReviewStatusArgs(args);
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
4877
  /**
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.
4878
+ * Review updates insert a replacement row, so they need only `INSERT` and the
4879
+ * insert materialized view publishes the delta cursor.
4880
+ *
4881
+ * A replacement written after a concurrent DELETE is not covered by that
4882
+ * DELETE. The post-write check therefore looks for any request, pending or
4883
+ * applied, and re-runs the delete when it finds one. Only applied requests
4884
+ * block the update up front, so feedback whose delete failed stays editable.
4885
+ *
4886
+ * If the post-write check reads a replica that has not received the request
4887
+ * yet, or the check or its delete fails, the replacement stays visible. A row
4888
+ * that is still visible under an applied request can only be such a leftover,
4889
+ * so the next update deletes it again.
4903
4890
  */
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
4891
+ async function updateFeedbackReviewStatus(client, args, replication) {
4892
+ const { feedbackId, reviewStatus } = parseUpdateFeedbackReviewStatusArgs(args);
4893
+ const existingRow = (await queryJson$2(client, `SELECT * FROM ${TABLE_FEEDBACK_EVENTS} FINAL
4906
4894
  WHERE feedbackId = {feedbackId:String}
4907
4895
  ORDER BY writeVersion DESC, timestamp DESC
4908
4896
  LIMIT 1`, { feedbackId }))[0];
4909
4897
  if (!existingRow) throw feedbackNotFoundError(feedbackId);
4910
- if (await hasFeedbackDeletionRequest(client, feedbackId, existingRow.organizationId, existingRow.resourceId)) throw feedbackNotFoundError(feedbackId);
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,
4920
- reviewStatus
4898
+ const { organizationId, resourceId } = existingRow;
4899
+ const redelete = async () => {
4900
+ await deleteFeedback(client, {
4901
+ feedbackIds: [feedbackId],
4902
+ organizationId: organizationId ?? void 0,
4903
+ resourceId: resourceId ?? void 0
4904
+ }, replication);
4905
+ throw feedbackNotFoundError(feedbackId);
4921
4906
  };
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
- }
4929
- });
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
- }
4907
+ if (await hasFeedbackDeletionRequest(client, feedbackId, organizationId, resourceId, { appliedOnly: true })) return redelete();
4908
+ const updated = rowToFeedbackRecord({
4909
+ ...existingRow,
4910
+ reviewStatus
4943
4911
  });
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;
4912
+ await batchCreateFeedback(client, { feedbacks: [updated] });
4913
+ if (await hasFeedbackDeletionRequest(client, feedbackId, organizationId, resourceId, { appliedOnly: false })) return redelete();
4914
+ return updated;
4947
4915
  }
4948
4916
  async function listFeedback(client, args, strategy) {
4949
4917
  const parsed = listFeedbackArgsSchema.parse(args);
@@ -8642,21 +8610,35 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
8642
8610
  if (!deltaPollingSupported(this.#deltaCursorStrategy)) return [
8643
8611
  "metrics",
8644
8612
  "logs",
8613
+ "entity-type-discovery",
8614
+ "entity-name-discovery",
8615
+ "service-name-discovery",
8616
+ "environment-discovery",
8617
+ "tag-discovery",
8618
+ "metric-discovery",
8645
8619
  "trace-query",
8646
8620
  "trace-query-root-duration",
8647
8621
  "trace-query-discovery",
8648
8622
  "thread-query",
8649
- "trace-query-tenant-scope"
8623
+ "trace-query-tenant-scope",
8624
+ "feedback"
8650
8625
  ];
8651
8626
  return [
8652
8627
  "metrics",
8653
8628
  "logs",
8629
+ "entity-type-discovery",
8630
+ "entity-name-discovery",
8631
+ "service-name-discovery",
8632
+ "environment-discovery",
8633
+ "tag-discovery",
8634
+ "metric-discovery",
8654
8635
  "delta-polling",
8655
8636
  "trace-query",
8656
8637
  "trace-query-root-duration",
8657
8638
  "trace-query-discovery",
8658
8639
  "thread-query",
8659
- "trace-query-tenant-scope"
8640
+ "trace-query-tenant-scope",
8641
+ "feedback"
8660
8642
  ];
8661
8643
  }
8662
8644
  async createSpan(args) {
@@ -8996,7 +8978,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
8996
8978
  }
8997
8979
  async updateFeedbackReviewStatus(args) {
8998
8980
  try {
8999
- return await updateFeedbackReviewStatus(this.#client, args, deltaPollingSupported(this.#deltaCursorStrategy) ? this.#deltaCursorStrategy : null);
8981
+ return await updateFeedbackReviewStatus(this.#client, args, this.#replication);
9000
8982
  } catch (error) {
9001
8983
  if (error instanceof MastraError) throw error;
9002
8984
  throw new MastraError({