@mastra/pg 1.22.3 → 1.23.0-alpha.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/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-deployment-workers.md +3 -0
- package/dist/docs/references/docs-storage.md +1 -0
- package/dist/docs/references/reference-rag-metadata-filters.md +1 -0
- package/dist/index.cjs +358 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +358 -11
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +5 -2
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts +10 -0
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -0
- package/dist/storage/domains/observability/v-next/tracing.d.ts +7 -0
- package/dist/storage/domains/observability/v-next/tracing.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/docs/SKILL.md
CHANGED
|
@@ -285,6 +285,8 @@ curl http://localhost:4111/api/agents
|
|
|
285
285
|
|
|
286
286
|
### Kubernetes
|
|
287
287
|
|
|
288
|
+
> **Helm:** On Kubernetes, the [`mastra-projects` Helm chart](https://mastra.ai/integrations/deploy/kubernetes-helm) renders these deployments for you, with the workers on the same image and environment as the server. It also refuses to install combinations that would run every worker twice. The manifests below are for writing them by hand.
|
|
289
|
+
|
|
288
290
|
Create separate Deployments for the API, orchestration worker, scheduler worker, and background task worker. Use the same image and Secret for each Deployment. Set only the role-specific environment variables directly on each container.
|
|
289
291
|
|
|
290
292
|
The orchestration worker Deployment has the following shape:
|
|
@@ -391,4 +393,5 @@ If the API crashes while a step is executing, that work can be lost and the work
|
|
|
391
393
|
- [Workers reference](https://mastra.ai/reference/workers/overview): Details about worker environment variables and types, with a list of supported storage backends
|
|
392
394
|
- [CLI reference](https://mastra.ai/reference/cli/mastra): `mastra worker build` and `mastra worker start`
|
|
393
395
|
- [PubSub](https://mastra.ai/docs/server/pubsub): Event delivery backends
|
|
396
|
+
- [Kubernetes (Helm)](https://mastra.ai/integrations/deploy/kubernetes-helm): Deploy worker roles with the official chart
|
|
394
397
|
- [Scheduled workflows](https://mastra.ai/docs/workflows/scheduled-workflows): Declare cron schedules on workflows
|
|
@@ -205,6 +205,7 @@ Each provider page includes installation instructions, configuration parameters,
|
|
|
205
205
|
- [libSQL](https://mastra.ai/integrations/databases/libsql)
|
|
206
206
|
- [Mastra](https://mastra.ai/docs/mastra-platform/database)
|
|
207
207
|
- [MongoDB](https://mastra.ai/integrations/databases/mongodb)
|
|
208
|
+
- [MySQL](https://mastra.ai/integrations/databases/mysql)
|
|
208
209
|
- [MSSQL](https://mastra.ai/integrations/databases/mssql)
|
|
209
210
|
- [Neon Postgres](https://mastra.ai/integrations/databases/neon)
|
|
210
211
|
- [OracleDB](https://mastra.ai/integrations/databases/oracledb)
|
|
@@ -176,6 +176,7 @@ const results = await store.query({
|
|
|
176
176
|
- Special handling for null and empty values
|
|
177
177
|
- Vector-specific filtering capabilities
|
|
178
178
|
- Datetime values must be in RFC 3339 format
|
|
179
|
+
- `$regex` is translated to Qdrant's full-text match, which matches whole tokens rather than arbitrary regular expressions or substrings (e.g. `{ name: { $regex: "item" } }` doesn't match `"item1"`). On unindexed payload fields, Qdrant versions before v1.19.1 fall back to substring matching
|
|
179
180
|
|
|
180
181
|
### Upstash
|
|
181
182
|
|
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
//#endregion
|
|
24
24
|
let _mastra_core_error = require("@mastra/core/error");
|
|
25
25
|
let _mastra_core_storage = require("@mastra/core/storage");
|
|
26
|
+
_mastra_core_storage = __toESM(_mastra_core_storage, 1);
|
|
26
27
|
let _mastra_core_utils = require("@mastra/core/utils");
|
|
27
28
|
let _mastra_core_vector = require("@mastra/core/vector");
|
|
28
29
|
let async_mutex = require("async-mutex");
|
|
@@ -12169,12 +12170,20 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
12169
12170
|
};
|
|
12170
12171
|
const lastBufferedAtTime = input.lastBufferedAtTime ? input.lastBufferedAtTime.toISOString() : null;
|
|
12171
12172
|
if ((await this.#db.client.query(`UPDATE ${tableName} SET
|
|
12172
|
-
"bufferedObservationChunks" =
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
|
|
12173
|
+
"bufferedObservationChunks" = CASE
|
|
12174
|
+
WHEN EXISTS (
|
|
12175
|
+
SELECT 1
|
|
12176
|
+
FROM jsonb_array_elements(COALESCE("bufferedObservationChunks", '[]'::jsonb)) AS chunk
|
|
12177
|
+
WHERE chunk->>'cycleId' = $2
|
|
12178
|
+
) THEN COALESCE("bufferedObservationChunks", '[]'::jsonb)
|
|
12179
|
+
ELSE COALESCE("bufferedObservationChunks", '[]'::jsonb) || $1::jsonb
|
|
12180
|
+
END,
|
|
12181
|
+
"lastBufferedAtTime" = COALESCE($3, "lastBufferedAtTime"),
|
|
12182
|
+
"updatedAt" = $4,
|
|
12183
|
+
"updatedAtZ" = $5
|
|
12184
|
+
WHERE id = $6`, [
|
|
12177
12185
|
JSON.stringify([newChunk]),
|
|
12186
|
+
input.chunk.cycleId,
|
|
12178
12187
|
lastBufferedAtTime,
|
|
12179
12188
|
nowStr,
|
|
12180
12189
|
nowStr,
|
|
@@ -13504,6 +13513,7 @@ var ObservabilityPG = class ObservabilityPG extends _mastra_core_storage.Observa
|
|
|
13504
13513
|
}
|
|
13505
13514
|
}
|
|
13506
13515
|
async batchDeleteTraces(args) {
|
|
13516
|
+
this.assertUnscopedBatchDeleteTraces(args);
|
|
13507
13517
|
try {
|
|
13508
13518
|
const tableName = getTableName$5({
|
|
13509
13519
|
indexName: _mastra_core_storage.TABLE_SPANS,
|
|
@@ -16739,6 +16749,304 @@ async function getScorePercentiles(client, schema, args) {
|
|
|
16739
16749
|
return { series: percentileSeriesFromRows(await client.manyOrNone(sql, acc.params), args.percentiles) };
|
|
16740
16750
|
}
|
|
16741
16751
|
//#endregion
|
|
16752
|
+
//#region src/storage/domains/observability/v-next/trace-query.ts
|
|
16753
|
+
const TRACE_STATUS_SQL = `CASE WHEN r."error" IS NOT NULL THEN 'error' ELSE 'success' END`;
|
|
16754
|
+
const TRACE_FIELDS = {
|
|
16755
|
+
traceId: "r.\"traceId\"",
|
|
16756
|
+
threadId: "r.\"threadId\"",
|
|
16757
|
+
resourceId: "r.\"resourceId\"",
|
|
16758
|
+
startedAt: "r.\"startedAt\"",
|
|
16759
|
+
endedAt: "r.\"endedAt\"",
|
|
16760
|
+
entityName: "r.\"entityName\"",
|
|
16761
|
+
entityType: "r.\"entityType\"",
|
|
16762
|
+
environment: "r.\"environment\"",
|
|
16763
|
+
status: TRACE_STATUS_SQL
|
|
16764
|
+
};
|
|
16765
|
+
const SPAN_FIELDS = {
|
|
16766
|
+
spanType: "s.\"spanType\"",
|
|
16767
|
+
error: "s.\"error\""
|
|
16768
|
+
};
|
|
16769
|
+
const SCORE_FIELDS = {
|
|
16770
|
+
scorerId: "s.\"scorerId\"",
|
|
16771
|
+
score: "s.\"score\""
|
|
16772
|
+
};
|
|
16773
|
+
const TRACE_SELECT = `
|
|
16774
|
+
r."traceId" AS "traceId",
|
|
16775
|
+
r."spanId" AS "rootSpanId",
|
|
16776
|
+
r."threadId" AS "threadId",
|
|
16777
|
+
r."resourceId" AS "resourceId",
|
|
16778
|
+
r."startedAt" AS "startedAt",
|
|
16779
|
+
r."endedAt" AS "endedAt",
|
|
16780
|
+
r."entityName" AS "entityName",
|
|
16781
|
+
r."entityType" AS "entityType",
|
|
16782
|
+
r."environment" AS "environment",
|
|
16783
|
+
${TRACE_STATUS_SQL} AS "status"`;
|
|
16784
|
+
function fieldSql(registry, field) {
|
|
16785
|
+
const sql = registry[field];
|
|
16786
|
+
if (sql === void 0) throw new Error(`Unsupported trusted trace-query field: ${field}`);
|
|
16787
|
+
return sql;
|
|
16788
|
+
}
|
|
16789
|
+
function placeholders(values, offset) {
|
|
16790
|
+
return values.map((_, index) => `$${offset + index}`).join(", ");
|
|
16791
|
+
}
|
|
16792
|
+
function compileScalarPredicate(predicate, registry, parameterOffset) {
|
|
16793
|
+
if (predicate.type === "boolean") {
|
|
16794
|
+
const values = [];
|
|
16795
|
+
return {
|
|
16796
|
+
sql: predicate.args.map((arg) => {
|
|
16797
|
+
const compiled = compileScalarPredicate(arg, registry, parameterOffset + values.length);
|
|
16798
|
+
values.push(...compiled.values);
|
|
16799
|
+
return `(${compiled.sql})`;
|
|
16800
|
+
}).join(predicate.operator === "and" ? " AND " : " OR "),
|
|
16801
|
+
values
|
|
16802
|
+
};
|
|
16803
|
+
}
|
|
16804
|
+
if (predicate.type === "not") {
|
|
16805
|
+
const compiled = compileScalarPredicate(predicate.arg, registry, parameterOffset);
|
|
16806
|
+
return {
|
|
16807
|
+
sql: `NOT (${compiled.sql})`,
|
|
16808
|
+
values: compiled.values
|
|
16809
|
+
};
|
|
16810
|
+
}
|
|
16811
|
+
const field = fieldSql(registry, predicate.field);
|
|
16812
|
+
if (predicate.type === "presence") return {
|
|
16813
|
+
sql: `${field} IS ${predicate.operator === "exists" ? "NOT " : ""}NULL`,
|
|
16814
|
+
values: []
|
|
16815
|
+
};
|
|
16816
|
+
if (predicate.type === "membership") {
|
|
16817
|
+
const list = placeholders(predicate.values, parameterOffset);
|
|
16818
|
+
if (predicate.operator === "in") return {
|
|
16819
|
+
sql: `${field} IS NOT NULL AND ${field} IN (${list})`,
|
|
16820
|
+
values: predicate.values
|
|
16821
|
+
};
|
|
16822
|
+
return {
|
|
16823
|
+
sql: `${field} IS NULL OR ${field} NOT IN (${list})`,
|
|
16824
|
+
values: predicate.values
|
|
16825
|
+
};
|
|
16826
|
+
}
|
|
16827
|
+
const parameter = `$${parameterOffset}`;
|
|
16828
|
+
const operators = {
|
|
16829
|
+
lt: "<",
|
|
16830
|
+
lte: "<=",
|
|
16831
|
+
gt: ">",
|
|
16832
|
+
gte: ">="
|
|
16833
|
+
};
|
|
16834
|
+
if (predicate.operator === "eq") return {
|
|
16835
|
+
sql: `${field} IS NOT DISTINCT FROM ${parameter}`,
|
|
16836
|
+
values: [predicate.value]
|
|
16837
|
+
};
|
|
16838
|
+
if (predicate.operator === "ne") return {
|
|
16839
|
+
sql: `${field} IS DISTINCT FROM ${parameter}`,
|
|
16840
|
+
values: [predicate.value]
|
|
16841
|
+
};
|
|
16842
|
+
const operator = operators[predicate.operator];
|
|
16843
|
+
if (operator === void 0) throw new Error(`Unsupported trusted trace-query operator: ${predicate.operator}`);
|
|
16844
|
+
return {
|
|
16845
|
+
sql: `${field} IS NOT NULL AND ${field} ${operator} ${parameter}`,
|
|
16846
|
+
values: [predicate.value]
|
|
16847
|
+
};
|
|
16848
|
+
}
|
|
16849
|
+
function latestRootPredicate$1(spanTable) {
|
|
16850
|
+
return `NOT EXISTS (
|
|
16851
|
+
SELECT 1 FROM ${spanTable} newer
|
|
16852
|
+
WHERE newer."traceId" = r."traceId"
|
|
16853
|
+
AND newer."parentSpanId" IS NULL
|
|
16854
|
+
AND newer."cursorId" > r."cursorId"
|
|
16855
|
+
)`;
|
|
16856
|
+
}
|
|
16857
|
+
function latestSpanPredicate$1(spanTable) {
|
|
16858
|
+
return `NOT EXISTS (
|
|
16859
|
+
SELECT 1 FROM ${spanTable} newer
|
|
16860
|
+
WHERE newer."traceId" = s."traceId"
|
|
16861
|
+
AND newer."spanId" = s."spanId"
|
|
16862
|
+
AND (newer."isPending" < s."isPending" OR (newer."isPending" = s."isPending" AND newer."cursorId" > s."cursorId"))
|
|
16863
|
+
)`;
|
|
16864
|
+
}
|
|
16865
|
+
function latestScorePredicate(scoreTable) {
|
|
16866
|
+
return `NOT EXISTS (
|
|
16867
|
+
SELECT 1 FROM ${scoreTable} newer
|
|
16868
|
+
WHERE newer."scoreId" = s."scoreId"
|
|
16869
|
+
AND newer."cursorId" > s."cursorId"
|
|
16870
|
+
)`;
|
|
16871
|
+
}
|
|
16872
|
+
function collectRelationCollections(predicate, collections = /* @__PURE__ */ new Set()) {
|
|
16873
|
+
if (!predicate) return collections;
|
|
16874
|
+
if (predicate.type === "relation") collections.add(predicate.collection);
|
|
16875
|
+
else if (predicate.type === "boolean") for (const arg of predicate.args) collectRelationCollections(arg, collections);
|
|
16876
|
+
else if (predicate.type === "not") collectRelationCollections(predicate.arg, collections);
|
|
16877
|
+
return collections;
|
|
16878
|
+
}
|
|
16879
|
+
function compilePredicate(predicate, parameterOffset) {
|
|
16880
|
+
if (predicate.type === "relation") {
|
|
16881
|
+
const registry = predicate.collection === "spans" ? SPAN_FIELDS : SCORE_FIELDS;
|
|
16882
|
+
const compiled = compileScalarPredicate(predicate.predicate, registry, parameterOffset);
|
|
16883
|
+
const existence = `EXISTS (
|
|
16884
|
+
SELECT 1 FROM ${predicate.collection === "spans" ? "current_spans" : "current_scores"} s
|
|
16885
|
+
WHERE s."traceId" = r."traceId"
|
|
16886
|
+
AND (${compiled.sql})
|
|
16887
|
+
)`;
|
|
16888
|
+
return {
|
|
16889
|
+
sql: predicate.quantifier === "some" ? existence : `NOT ${existence}`,
|
|
16890
|
+
values: compiled.values
|
|
16891
|
+
};
|
|
16892
|
+
}
|
|
16893
|
+
if (predicate.type === "boolean") {
|
|
16894
|
+
const values = [];
|
|
16895
|
+
return {
|
|
16896
|
+
sql: predicate.args.map((arg) => {
|
|
16897
|
+
const compiled = compilePredicate(arg, parameterOffset + values.length);
|
|
16898
|
+
values.push(...compiled.values);
|
|
16899
|
+
return `(${compiled.sql})`;
|
|
16900
|
+
}).join(predicate.operator === "and" ? " AND " : " OR "),
|
|
16901
|
+
values
|
|
16902
|
+
};
|
|
16903
|
+
}
|
|
16904
|
+
if (predicate.type === "not") {
|
|
16905
|
+
const compiled = compilePredicate(predicate.arg, parameterOffset);
|
|
16906
|
+
return {
|
|
16907
|
+
sql: `NOT (${compiled.sql})`,
|
|
16908
|
+
values: compiled.values
|
|
16909
|
+
};
|
|
16910
|
+
}
|
|
16911
|
+
return compileScalarPredicate(predicate, TRACE_FIELDS, parameterOffset);
|
|
16912
|
+
}
|
|
16913
|
+
function compilePostgresTraceQuery(schema, plan) {
|
|
16914
|
+
const spanTable = qualifiedTable(schema, TABLE_SPAN_EVENTS);
|
|
16915
|
+
const scoreTable = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
16916
|
+
const values = [plan.timeRange.from, plan.timeRange.to];
|
|
16917
|
+
const relationCollections = collectRelationCollections(plan.where);
|
|
16918
|
+
const ctes = [`root_scope AS MATERIALIZED (
|
|
16919
|
+
SELECT *
|
|
16920
|
+
FROM ${spanTable} r
|
|
16921
|
+
WHERE ${[
|
|
16922
|
+
`r."parentSpanId" IS NULL`,
|
|
16923
|
+
latestRootPredicate$1(spanTable),
|
|
16924
|
+
`NOT r."isPending"`,
|
|
16925
|
+
`r."endedAt" IS NOT NULL`,
|
|
16926
|
+
`r."startedAt" >= $1`,
|
|
16927
|
+
`r."startedAt" < $2`
|
|
16928
|
+
].join("\n AND ")}
|
|
16929
|
+
)`];
|
|
16930
|
+
if (relationCollections.has("spans")) ctes.push(`current_spans AS MATERIALIZED (
|
|
16931
|
+
SELECT s."traceId", s."spanType", s."error"
|
|
16932
|
+
FROM ${spanTable} s
|
|
16933
|
+
WHERE s."traceId" IS NOT NULL
|
|
16934
|
+
AND s."traceId" IN (SELECT "traceId" FROM root_scope)
|
|
16935
|
+
AND ${latestSpanPredicate$1(spanTable)}
|
|
16936
|
+
)`);
|
|
16937
|
+
if (relationCollections.has("scores")) ctes.push(`current_scores AS MATERIALIZED (
|
|
16938
|
+
SELECT s."traceId", s."scorerId", s."score"
|
|
16939
|
+
FROM ${scoreTable} s
|
|
16940
|
+
WHERE s."traceId" IS NOT NULL
|
|
16941
|
+
AND s."traceId" IN (SELECT "traceId" FROM root_scope)
|
|
16942
|
+
AND ${latestScorePredicate(scoreTable)}
|
|
16943
|
+
)`);
|
|
16944
|
+
let predicateSql = "TRUE";
|
|
16945
|
+
if (plan.where) {
|
|
16946
|
+
const predicate = compilePredicate(plan.where, values.length + 1);
|
|
16947
|
+
predicateSql = predicate.sql;
|
|
16948
|
+
values.push(...predicate.values);
|
|
16949
|
+
}
|
|
16950
|
+
ctes.push(`candidates AS (
|
|
16951
|
+
SELECT ${TRACE_SELECT}
|
|
16952
|
+
FROM root_scope r
|
|
16953
|
+
WHERE ${predicateSql}
|
|
16954
|
+
)`);
|
|
16955
|
+
const candidates = `WITH ${ctes.join(",\n")}`;
|
|
16956
|
+
if (plan.result === "groups") {
|
|
16957
|
+
const pageCondition = plan.cursor ? `AND "threadId" > $${values.length + 1}` : "";
|
|
16958
|
+
if (plan.cursor) values.push(plan.cursor.threadId);
|
|
16959
|
+
values.push(plan.limit + 1);
|
|
16960
|
+
return {
|
|
16961
|
+
text: `${candidates}
|
|
16962
|
+
SELECT "threadId"
|
|
16963
|
+
FROM candidates
|
|
16964
|
+
WHERE "threadId" IS NOT NULL ${pageCondition}
|
|
16965
|
+
GROUP BY "threadId"
|
|
16966
|
+
ORDER BY "threadId" ASC
|
|
16967
|
+
LIMIT $${values.length}`,
|
|
16968
|
+
values
|
|
16969
|
+
};
|
|
16970
|
+
}
|
|
16971
|
+
const orderField = plan.orderBy.field === "startedAt" ? "\"startedAt\"" : "\"endedAt\"";
|
|
16972
|
+
const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
|
|
16973
|
+
let pageCondition = "";
|
|
16974
|
+
if (plan.cursor) {
|
|
16975
|
+
const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
|
|
16976
|
+
const sortParameter = `$${values.length + 1}`;
|
|
16977
|
+
pageCondition = `WHERE (${orderField} ${comparison} ${sortParameter} OR (${orderField} = ${sortParameter} AND "traceId" > ${`$${values.length + 2}`}))`;
|
|
16978
|
+
values.push(plan.cursor.sortValue, plan.cursor.traceId);
|
|
16979
|
+
}
|
|
16980
|
+
values.push(plan.limit + 1);
|
|
16981
|
+
return {
|
|
16982
|
+
text: `${candidates}
|
|
16983
|
+
SELECT *
|
|
16984
|
+
FROM candidates
|
|
16985
|
+
${pageCondition}
|
|
16986
|
+
ORDER BY ${orderField} ${direction}, "traceId" ASC
|
|
16987
|
+
LIMIT $${values.length}`,
|
|
16988
|
+
values
|
|
16989
|
+
};
|
|
16990
|
+
}
|
|
16991
|
+
function asIsoTimestamp$1(value) {
|
|
16992
|
+
if (value === null || value === void 0) throw new Error("Trace query returned a null timestamp");
|
|
16993
|
+
return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
|
|
16994
|
+
}
|
|
16995
|
+
function isPostgresStatementTimeout(error) {
|
|
16996
|
+
if (!error || typeof error !== "object") return false;
|
|
16997
|
+
const candidate = error;
|
|
16998
|
+
return candidate.code === "57014" && String(candidate.message ?? "").includes("statement timeout");
|
|
16999
|
+
}
|
|
17000
|
+
async function runWithPostgresTraceQueryTimeout(client, timeoutMs, execute) {
|
|
17001
|
+
const resolvedTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
17002
|
+
try {
|
|
17003
|
+
return await client.tx(async (transaction) => {
|
|
17004
|
+
await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${resolvedTimeoutMs}ms`]);
|
|
17005
|
+
return execute(transaction);
|
|
17006
|
+
});
|
|
17007
|
+
} catch (error) {
|
|
17008
|
+
if (isPostgresStatementTimeout(error)) throw new _mastra_core_storage.TraceQueryExecutionError();
|
|
17009
|
+
throw error;
|
|
17010
|
+
}
|
|
17011
|
+
}
|
|
17012
|
+
async function queryTraces(client, schema, plan, timeoutMs) {
|
|
17013
|
+
const query = compilePostgresTraceQuery(schema, plan);
|
|
17014
|
+
const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
|
|
17015
|
+
const visibleRows = rows.slice(0, plan.limit);
|
|
17016
|
+
if (plan.result === "groups") {
|
|
17017
|
+
const groups = visibleRows.map((row) => ({ threadId: String(row.threadId) }));
|
|
17018
|
+
const last = groups.at(-1);
|
|
17019
|
+
return _mastra_core_storage.traceQueryResponseSchema.parse({
|
|
17020
|
+
groups,
|
|
17021
|
+
page: { next: rows.length > plan.limit && last ? _mastra_core_storage.encodeTraceQueryCursor(plan, {
|
|
17022
|
+
result: "groups",
|
|
17023
|
+
threadId: last.threadId
|
|
17024
|
+
}) : null }
|
|
17025
|
+
});
|
|
17026
|
+
}
|
|
17027
|
+
const traces = visibleRows.map((row) => ({
|
|
17028
|
+
traceId: String(row.traceId),
|
|
17029
|
+
rootSpanId: String(row.rootSpanId),
|
|
17030
|
+
threadId: row.threadId == null ? null : String(row.threadId),
|
|
17031
|
+
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
17032
|
+
startedAt: asIsoTimestamp$1(row.startedAt),
|
|
17033
|
+
endedAt: asIsoTimestamp$1(row.endedAt),
|
|
17034
|
+
entityName: row.entityName == null ? null : String(row.entityName),
|
|
17035
|
+
entityType: row.entityType == null ? null : String(row.entityType),
|
|
17036
|
+
environment: row.environment == null ? null : String(row.environment),
|
|
17037
|
+
status: row.status
|
|
17038
|
+
}));
|
|
17039
|
+
const last = traces.at(-1);
|
|
17040
|
+
return _mastra_core_storage.traceQueryResponseSchema.parse({
|
|
17041
|
+
traces,
|
|
17042
|
+
page: { next: rows.length > plan.limit && last ? _mastra_core_storage.encodeTraceQueryCursor(plan, {
|
|
17043
|
+
result: "traces",
|
|
17044
|
+
sortValue: last[plan.orderBy.field],
|
|
17045
|
+
traceId: last.traceId
|
|
17046
|
+
}) : null }
|
|
17047
|
+
});
|
|
17048
|
+
}
|
|
17049
|
+
//#endregion
|
|
16742
17050
|
//#region src/storage/domains/observability/v-next/traces.ts
|
|
16743
17051
|
/**
|
|
16744
17052
|
* Trace / branch list reads for the v-next Postgres observability domain.
|
|
@@ -17388,11 +17696,39 @@ async function getTraceLight(client, schema, args) {
|
|
|
17388
17696
|
spans: rows.map(rowToLightSpanRecord)
|
|
17389
17697
|
};
|
|
17390
17698
|
}
|
|
17699
|
+
/**
|
|
17700
|
+
* Delete traces by traceId, cascading to trace-linked signal events
|
|
17701
|
+
* (metrics, logs, scores, feedback). Signal rows with a NULL traceId are
|
|
17702
|
+
* never affected. When the optional tenant scope (`organizationId` /
|
|
17703
|
+
* `resourceId`) is set, every DELETE additionally requires the row's tenant
|
|
17704
|
+
* columns to match.
|
|
17705
|
+
*/
|
|
17391
17706
|
async function batchDeleteTraces(client, schema, args) {
|
|
17392
17707
|
if (args.traceIds.length === 0) return;
|
|
17393
|
-
const
|
|
17708
|
+
const params = [...args.traceIds];
|
|
17394
17709
|
const placeholders = args.traceIds.map((_, i) => `$${i + 1}`).join(", ");
|
|
17395
|
-
|
|
17710
|
+
let scopeCondition = "";
|
|
17711
|
+
if (args.organizationId !== void 0) {
|
|
17712
|
+
params.push(args.organizationId);
|
|
17713
|
+
scopeCondition += ` AND "organizationId" = $${params.length}`;
|
|
17714
|
+
}
|
|
17715
|
+
if (args.resourceId !== void 0) {
|
|
17716
|
+
params.push(args.resourceId);
|
|
17717
|
+
scopeCondition += ` AND "resourceId" = $${params.length}`;
|
|
17718
|
+
}
|
|
17719
|
+
const tables = [
|
|
17720
|
+
TABLE_SPAN_EVENTS,
|
|
17721
|
+
TABLE_METRIC_EVENTS,
|
|
17722
|
+
TABLE_LOG_EVENTS,
|
|
17723
|
+
TABLE_SCORE_EVENTS,
|
|
17724
|
+
TABLE_FEEDBACK_EVENTS
|
|
17725
|
+
];
|
|
17726
|
+
await client.tx(async (t) => {
|
|
17727
|
+
for (const tableName of tables) {
|
|
17728
|
+
const table = qualifiedTable(schema, tableName);
|
|
17729
|
+
await t.query(`DELETE FROM ${table} WHERE "traceId" IN (${placeholders})${scopeCondition}`, params);
|
|
17730
|
+
}
|
|
17731
|
+
});
|
|
17396
17732
|
}
|
|
17397
17733
|
/** Truncate the span_events table. */
|
|
17398
17734
|
async function dangerouslyClearTracing(client, schema) {
|
|
@@ -17425,7 +17761,7 @@ async function dangerouslyClearTracing(client, schema) {
|
|
|
17425
17761
|
* Use it through `MastraCompositeStore` with a dedicated Postgres connection.
|
|
17426
17762
|
*/
|
|
17427
17763
|
function wrapError(op, error, details) {
|
|
17428
|
-
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
17764
|
+
if (error instanceof _mastra_core_error.MastraError || error instanceof _mastra_core_storage.TraceQueryExecutionError) throw error;
|
|
17429
17765
|
throw new _mastra_core_error.MastraError({
|
|
17430
17766
|
id: (0, _mastra_core_storage.createStorageErrorId)("PG", op, "FAILED"),
|
|
17431
17767
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -17438,6 +17774,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
17438
17774
|
#schema;
|
|
17439
17775
|
#partitioning;
|
|
17440
17776
|
#discoveryConfig;
|
|
17777
|
+
#traceQueryTimeoutMs;
|
|
17441
17778
|
#partitionMode;
|
|
17442
17779
|
constructor(config) {
|
|
17443
17780
|
super();
|
|
@@ -17446,6 +17783,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
17446
17783
|
this.#schema = schemaName ?? "public";
|
|
17447
17784
|
this.#partitioning = config.partitioning ?? {};
|
|
17448
17785
|
this.#discoveryConfig = config.discovery ?? {};
|
|
17786
|
+
this.#traceQueryTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(config.traceQueryTimeoutMs);
|
|
17449
17787
|
}
|
|
17450
17788
|
/**
|
|
17451
17789
|
* Build the discovery config used at each call site, with the framework
|
|
@@ -17597,11 +17935,16 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
17597
17935
|
};
|
|
17598
17936
|
}
|
|
17599
17937
|
getFeatures() {
|
|
17600
|
-
if (!deltaPollingFeatureEnabled()) return [
|
|
17938
|
+
if (!deltaPollingFeatureEnabled()) return [
|
|
17939
|
+
"metrics",
|
|
17940
|
+
"logs",
|
|
17941
|
+
"trace-query"
|
|
17942
|
+
];
|
|
17601
17943
|
return [
|
|
17602
17944
|
"metrics",
|
|
17603
17945
|
"logs",
|
|
17604
|
-
"delta-polling"
|
|
17946
|
+
"delta-polling",
|
|
17947
|
+
"trace-query"
|
|
17605
17948
|
];
|
|
17606
17949
|
}
|
|
17607
17950
|
async #run(op, fn, details) {
|
|
@@ -17644,6 +17987,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
17644
17987
|
async listTraces(args) {
|
|
17645
17988
|
return this.#run("LIST_TRACES", () => listTraces(this.#client, this.#schema, args));
|
|
17646
17989
|
}
|
|
17990
|
+
async queryTraces(plan) {
|
|
17991
|
+
return this.#run("QUERY_TRACES", () => queryTraces(this.#client, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
17992
|
+
}
|
|
17647
17993
|
async listBranches(args) {
|
|
17648
17994
|
return this.#run("LIST_BRANCHES", () => listBranches(this.#client, this.#schema, args));
|
|
17649
17995
|
}
|
|
@@ -22843,7 +23189,8 @@ var PostgresStoreVNext = class extends PostgresStore {
|
|
|
22843
23189
|
client: observabilityClient,
|
|
22844
23190
|
schemaName: obsConfig.schemaName ?? config.schemaName,
|
|
22845
23191
|
partitioning: obsConfig.partitioning,
|
|
22846
|
-
discovery: obsConfig.discovery
|
|
23192
|
+
discovery: obsConfig.discovery,
|
|
23193
|
+
traceQueryTimeoutMs: obsConfig.traceQueryTimeoutMs
|
|
22847
23194
|
});
|
|
22848
23195
|
this.stores = {
|
|
22849
23196
|
...this.stores,
|