@mastra/pg 1.26.0-alpha.1 → 1.26.0-alpha.3
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/reference-rag-vector-databases.md +34 -0
- package/dist/docs/references/reference-storage-retention.md +56 -4
- package/dist/index.cjs +194 -161
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +195 -162
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +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 +1 -0
- package/dist/storage/domains/observability/v-next/scores.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts +4 -2
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/dist/storage/retention.d.ts +5 -58
- package/dist/storage/retention.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -4927,137 +4927,31 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
|
|
|
4927
4927
|
};
|
|
4928
4928
|
//#endregion
|
|
4929
4929
|
//#region src/storage/retention.ts
|
|
4930
|
-
const DEFAULT_BATCH_SIZE = 1e3;
|
|
4931
|
-
async function sleep(ms, signal) {
|
|
4932
|
-
if (ms <= 0 || signal?.aborted) return;
|
|
4933
|
-
await new Promise((resolve) => {
|
|
4934
|
-
const timer = setTimeout(() => {
|
|
4935
|
-
signal?.removeEventListener("abort", onAbort);
|
|
4936
|
-
resolve();
|
|
4937
|
-
}, ms);
|
|
4938
|
-
const onAbort = () => {
|
|
4939
|
-
clearTimeout(timer);
|
|
4940
|
-
resolve();
|
|
4941
|
-
};
|
|
4942
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4943
|
-
});
|
|
4944
|
-
}
|
|
4945
|
-
/**
|
|
4946
|
-
* Convert a policy's `maxAge` into a cutoff bound matching the anchor's storage
|
|
4947
|
-
* type: a `Date` for `timestamptz` columns (pg compares timezone-aware), or a
|
|
4948
|
-
* raw millisecond number for `bigint` epoch-ms columns.
|
|
4949
|
-
*/
|
|
4950
4930
|
function cutoffFor(policy, anchorType, now = Date.now()) {
|
|
4951
|
-
const cutoffMs =
|
|
4931
|
+
const cutoffMs = (0, _mastra_core_storage.retentionCutoffMs)(policy, now);
|
|
4952
4932
|
return anchorType === "epoch-ms" ? cutoffMs : new Date(cutoffMs);
|
|
4953
4933
|
}
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
};
|
|
4969
|
-
if (options?.maxBatches !== void 0 && batches >= options.maxBatches) return {
|
|
4970
|
-
deleted,
|
|
4971
|
-
done: false
|
|
4972
|
-
};
|
|
4973
|
-
let limit = batchSize;
|
|
4974
|
-
if (options?.maxRows !== void 0) {
|
|
4975
|
-
const remaining = options.maxRows - deleted;
|
|
4976
|
-
if (remaining <= 0) return {
|
|
4977
|
-
deleted,
|
|
4978
|
-
done: false
|
|
4979
|
-
};
|
|
4980
|
-
limit = Math.min(limit, remaining);
|
|
4981
|
-
}
|
|
4982
|
-
const affected = await deleteBatch(limit);
|
|
4983
|
-
deleted += affected;
|
|
4984
|
-
batches += 1;
|
|
4985
|
-
if (affected < limit) return {
|
|
4986
|
-
deleted,
|
|
4987
|
-
done: true
|
|
4988
|
-
};
|
|
4989
|
-
if (options?.pauseMs) await sleep(options.pauseMs, options.signal);
|
|
4990
|
-
}
|
|
4991
|
-
}
|
|
4992
|
-
/**
|
|
4993
|
-
* Runs the bounded, batched, cancellable delete loop for a set of tables in the
|
|
4994
|
-
* given order (callers pass children before parents for cascade-safe pruning),
|
|
4995
|
-
* and returns one {@link PruneResult} per table.
|
|
4996
|
-
*
|
|
4997
|
-
* The loop:
|
|
4998
|
-
* - deletes in chunks of `batchSize` (default 1000), each its own statement;
|
|
4999
|
-
* - stops a table's loop when a batch deletes fewer rows than requested (drained),
|
|
5000
|
-
* or when `maxBatches`/`maxRows` is hit, or the `signal` aborts — the latter
|
|
5001
|
-
* three leave `done: false` so the caller can resume;
|
|
5002
|
-
* - pauses `pauseMs` between batches when set, to avoid starving live traffic.
|
|
5003
|
-
*
|
|
5004
|
-
* `prune()` only deletes rows; it never reclaims disk. PostgreSQL reuses freed
|
|
5005
|
-
* space (dead tuples) via autovacuum on subsequent writes, so tables stop
|
|
5006
|
-
* growing. Returning disk to the OS (e.g. `VACUUM FULL`) is left to the operator.
|
|
5007
|
-
*/
|
|
5008
|
-
async function runPrune({ db, domain, targets, options }) {
|
|
5009
|
-
const results = [];
|
|
5010
|
-
const now = Date.now();
|
|
5011
|
-
for (const target of targets) {
|
|
5012
|
-
if (options?.signal?.aborted) {
|
|
5013
|
-
results.push({
|
|
5014
|
-
domain,
|
|
5015
|
-
table: target.table,
|
|
5016
|
-
deleted: 0,
|
|
5017
|
-
done: false
|
|
5018
|
-
});
|
|
5019
|
-
continue;
|
|
5020
|
-
}
|
|
5021
|
-
const cutoff = cutoffFor(target.policy, target.anchorType, now);
|
|
5022
|
-
const { deleted, done } = await runBatchedDelete({
|
|
5023
|
-
deleteBatch: (limit) => db.pruneBatch({
|
|
5024
|
-
tableName: target.table,
|
|
5025
|
-
column: target.column,
|
|
5026
|
-
cutoff,
|
|
5027
|
-
limit
|
|
5028
|
-
}),
|
|
5029
|
-
batchSize: target.policy.batchSize ?? DEFAULT_BATCH_SIZE,
|
|
5030
|
-
options
|
|
5031
|
-
});
|
|
5032
|
-
results.push({
|
|
5033
|
-
domain,
|
|
5034
|
-
table: target.table,
|
|
5035
|
-
deleted,
|
|
5036
|
-
done
|
|
5037
|
-
});
|
|
5038
|
-
}
|
|
5039
|
-
return results;
|
|
4934
|
+
const runBatchedDelete = _mastra_core_storage.runRetentionBatches;
|
|
4935
|
+
function runPrune({ db, domain, targets, options }) {
|
|
4936
|
+
return (0, _mastra_core_storage.executeRetentionPrune)({
|
|
4937
|
+
domain,
|
|
4938
|
+
targets,
|
|
4939
|
+
options,
|
|
4940
|
+
cutoffFor: (target, now) => cutoffFor(target.policy, target.anchorType ?? "timestamp", now),
|
|
4941
|
+
deleteBatch: (target, cutoff, limit) => db.pruneBatch({
|
|
4942
|
+
tableName: target.table,
|
|
4943
|
+
column: target.column,
|
|
4944
|
+
cutoff,
|
|
4945
|
+
limit
|
|
4946
|
+
})
|
|
4947
|
+
});
|
|
5040
4948
|
}
|
|
5041
|
-
/**
|
|
5042
|
-
* Resolve a domain's `{ tableKey: policy }` map plus its descriptor into an
|
|
5043
|
-
* ordered list of {@link PruneTarget}s. `order` lists table keys children-first
|
|
5044
|
-
* so cascade-dependent rows are removed before their parents. Table keys not in
|
|
5045
|
-
* `policies` are skipped (unset = keep forever).
|
|
5046
|
-
*/
|
|
5047
4949
|
function resolveTargets({ policies, descriptor, order }) {
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
targets.push({
|
|
5054
|
-
table: entry.table,
|
|
5055
|
-
column: entry.column,
|
|
5056
|
-
anchorType: entry.anchorType ?? "timestamp",
|
|
5057
|
-
policy
|
|
5058
|
-
});
|
|
5059
|
-
}
|
|
5060
|
-
return targets;
|
|
4950
|
+
return (0, _mastra_core_storage.resolveRetentionTargets)({
|
|
4951
|
+
policies,
|
|
4952
|
+
descriptor,
|
|
4953
|
+
order
|
|
4954
|
+
});
|
|
5061
4955
|
}
|
|
5062
4956
|
//#endregion
|
|
5063
4957
|
//#region src/storage/domains/background-tasks/index.ts
|
|
@@ -10888,7 +10782,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
10888
10782
|
hasMore: false
|
|
10889
10783
|
};
|
|
10890
10784
|
const limitValue = perPageInput === false ? total : perPage;
|
|
10891
|
-
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ" ${baseQuery} ORDER BY COALESCE("${field}Z", "${field}") ${direction} LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
|
|
10785
|
+
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ" ${baseQuery} ORDER BY COALESCE("${field}Z", "${field}") ${direction}, "id" ${direction} LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
|
|
10892
10786
|
return {
|
|
10893
10787
|
threads: (await this.#db.readClient.manyOrNone(dataQuery, [
|
|
10894
10788
|
...queryParams,
|
|
@@ -11081,10 +10975,11 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
11081
10975
|
return messages.sort((a, b) => {
|
|
11082
10976
|
const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
11083
10977
|
const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
11084
|
-
|
|
10978
|
+
const idOrder = direction === "ASC" ? a.id.localeCompare(b.id) : b.id.localeCompare(a.id);
|
|
10979
|
+
if (aValue == null && bValue == null) return idOrder;
|
|
11085
10980
|
if (aValue == null) return 1;
|
|
11086
10981
|
if (bValue == null) return -1;
|
|
11087
|
-
if (aValue === bValue) return
|
|
10982
|
+
if (aValue === bValue) return idOrder;
|
|
11088
10983
|
if (typeof aValue === "number" && typeof bValue === "number") return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
11089
10984
|
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
11090
10985
|
});
|
|
@@ -11287,7 +11182,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
11287
11182
|
const metadataFilter = (0, _mastra_core_storage.validateStorageMetadataFilter)(filter?.metadata);
|
|
11288
11183
|
try {
|
|
11289
11184
|
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
11290
|
-
const orderByStatement = `ORDER BY "${field}" ${direction}`;
|
|
11185
|
+
const orderByStatement = `ORDER BY "${field}" ${direction}, "id" ${direction}`;
|
|
11291
11186
|
const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
|
|
11292
11187
|
const tableName = getTableName$3({
|
|
11293
11188
|
indexName: _mastra_core_storage.TABLE_MESSAGES,
|
|
@@ -11442,7 +11337,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
11442
11337
|
const metadataFilter = (0, _mastra_core_storage.validateStorageMetadataFilter)(filter?.metadata);
|
|
11443
11338
|
try {
|
|
11444
11339
|
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
11445
|
-
const orderByStatement = `ORDER BY "${field}" ${direction}`;
|
|
11340
|
+
const orderByStatement = `ORDER BY "${field}" ${direction}, "id" ${direction}`;
|
|
11446
11341
|
const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
|
|
11447
11342
|
const tableName = getTableName$3({
|
|
11448
11343
|
indexName: _mastra_core_storage.TABLE_MESSAGES,
|
|
@@ -14937,6 +14832,11 @@ function tableIndexes() {
|
|
|
14937
14832
|
columns: "(\"tags\")",
|
|
14938
14833
|
using: "gin"
|
|
14939
14834
|
},
|
|
14835
|
+
{
|
|
14836
|
+
name: "mastra_score_events_scoreid_cursor_idx",
|
|
14837
|
+
table: TABLE_SCORE_EVENTS,
|
|
14838
|
+
columns: "(\"scoreId\", \"cursorId\" DESC)"
|
|
14839
|
+
},
|
|
14940
14840
|
{
|
|
14941
14841
|
name: "mastra_score_events_cursor_idx",
|
|
14942
14842
|
table: TABLE_SCORE_EVENTS,
|
|
@@ -17069,13 +16969,32 @@ function pushScoreIdentity(acc, scorerId, scoreSource) {
|
|
|
17069
16969
|
acc.params.push(scoreSource);
|
|
17070
16970
|
}
|
|
17071
16971
|
}
|
|
16972
|
+
function scoreRewriteConflict(row) {
|
|
16973
|
+
return `ON CONFLICT ("scoreId", "timestamp") DO UPDATE SET ${[
|
|
16974
|
+
...Object.keys(row).filter((column) => column !== "scoreId" && column !== "timestamp").map((column) => `"${column}" = EXCLUDED."${column}"`),
|
|
16975
|
+
"\"cursorId\" = EXCLUDED.\"cursorId\"",
|
|
16976
|
+
"\"xactId\" = EXCLUDED.\"xactId\""
|
|
16977
|
+
].join(", ")}`;
|
|
16978
|
+
}
|
|
16979
|
+
function collapseExactScoreConflicts(rows) {
|
|
16980
|
+
const records = /* @__PURE__ */ new Map();
|
|
16981
|
+
for (const row of rows) {
|
|
16982
|
+
const timestamp = new Date(row.timestamp).toISOString();
|
|
16983
|
+
const key = `${String(row.scoreId)}\u0000${timestamp}`;
|
|
16984
|
+
records.delete(key);
|
|
16985
|
+
records.set(key, row);
|
|
16986
|
+
}
|
|
16987
|
+
return [...records.values()];
|
|
16988
|
+
}
|
|
17072
16989
|
async function createScore(client, schema, args) {
|
|
17073
|
-
const
|
|
16990
|
+
const row = scoreRecordToRow(args.score);
|
|
16991
|
+
const insert = buildInsert(schema, TABLE_SCORE_EVENTS, [row], scoreRewriteConflict(row));
|
|
17074
16992
|
if (insert) await client.query(insert.text, insert.values);
|
|
17075
16993
|
}
|
|
17076
16994
|
async function batchCreateScores(client, schema, args) {
|
|
17077
16995
|
if (args.scores.length === 0) return;
|
|
17078
|
-
const
|
|
16996
|
+
const rows = collapseExactScoreConflicts(args.scores.map(scoreRecordToRow));
|
|
16997
|
+
const insert = buildInsert(schema, TABLE_SCORE_EVENTS, rows, scoreRewriteConflict(rows[0]));
|
|
17079
16998
|
if (insert) await client.query(insert.text, insert.values);
|
|
17080
16999
|
}
|
|
17081
17000
|
/**
|
|
@@ -17098,6 +17017,16 @@ async function deleteScores(client, schema, args) {
|
|
|
17098
17017
|
}
|
|
17099
17018
|
await client.query(`DELETE FROM ${table} WHERE ${conditions.join(" AND ")}`, values);
|
|
17100
17019
|
}
|
|
17020
|
+
function latestScorePredicate(table, alias = "s") {
|
|
17021
|
+
return `NOT EXISTS (
|
|
17022
|
+
SELECT 1 FROM ${table} newer
|
|
17023
|
+
WHERE newer."scoreId" = ${alias}."scoreId"
|
|
17024
|
+
AND newer."cursorId" > ${alias}."cursorId"
|
|
17025
|
+
)`;
|
|
17026
|
+
}
|
|
17027
|
+
function applyLatestScorePredicate(acc, table) {
|
|
17028
|
+
acc.conditions.push(latestScorePredicate(table));
|
|
17029
|
+
}
|
|
17101
17030
|
async function listScores(client, schema, args) {
|
|
17102
17031
|
const { mode, filters, pagination, orderBy, after, limit } = _mastra_core_storage.listScoresArgsSchema.parse(args);
|
|
17103
17032
|
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
@@ -17108,28 +17037,50 @@ async function listScores(client, schema, args) {
|
|
|
17108
17037
|
return listScoresPage(client, table, filters, pagination.page, pagination.perPage, orderBy.field, orderBy.direction);
|
|
17109
17038
|
}
|
|
17110
17039
|
async function getScoreById(client, schema, scoreId) {
|
|
17040
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17111
17041
|
const row = await client.oneOrNone(`SELECT ${SCORE_SELECT_COLUMNS}
|
|
17112
|
-
FROM ${
|
|
17042
|
+
FROM ${table}
|
|
17113
17043
|
WHERE "scoreId" = $1
|
|
17114
|
-
ORDER BY "
|
|
17044
|
+
ORDER BY "cursorId" DESC
|
|
17115
17045
|
LIMIT 1`, [scoreId]);
|
|
17116
17046
|
return row ? rowToScoreRecord(row) : null;
|
|
17117
17047
|
}
|
|
17118
17048
|
async function listScoresPage(client, table, filters, page, perPage, orderField, orderDir) {
|
|
17119
|
-
|
|
17049
|
+
const acc = newFilterAccumulator();
|
|
17050
|
+
applyScoreFilters(acc, filters);
|
|
17051
|
+
applyLatestScorePredicate(acc, table);
|
|
17052
|
+
const whereClause = whereOrEmpty(acc);
|
|
17053
|
+
const countRow = await client.oneOrNone(`SELECT COUNT(*)::text AS count FROM ${table} s ${whereClause}`, acc.params);
|
|
17054
|
+
const total = Number(countRow?.count ?? 0);
|
|
17055
|
+
let scores = [];
|
|
17056
|
+
if (total > 0) {
|
|
17057
|
+
const safeOrderField = (0, _mastra_core_utils.parseSqlIdentifier)(orderField, "order field");
|
|
17058
|
+
scores = (await client.manyOrNone(`SELECT ${SCORE_SELECT_COLUMNS}
|
|
17059
|
+
FROM ${table} s
|
|
17060
|
+
${whereClause}
|
|
17061
|
+
ORDER BY "${safeOrderField}" ${orderDir}, "cursorId" ${orderDir}
|
|
17062
|
+
LIMIT $${acc.next++} OFFSET $${acc.next++}`, [
|
|
17063
|
+
...acc.params,
|
|
17064
|
+
perPage,
|
|
17065
|
+
page * perPage
|
|
17066
|
+
])).map(rowToScoreRecord);
|
|
17067
|
+
}
|
|
17068
|
+
const deltaCursor = deltaPollingFeatureEnabled() ? await readSignalStreamHeadCursor({
|
|
17120
17069
|
client,
|
|
17121
17070
|
table,
|
|
17122
17071
|
filters,
|
|
17123
|
-
|
|
17124
|
-
|
|
17125
|
-
|
|
17126
|
-
|
|
17127
|
-
|
|
17128
|
-
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17072
|
+
applyFilters: applyScoreFilters
|
|
17073
|
+
}) : void 0;
|
|
17074
|
+
return {
|
|
17075
|
+
scores,
|
|
17076
|
+
pagination: {
|
|
17077
|
+
total,
|
|
17078
|
+
page,
|
|
17079
|
+
perPage,
|
|
17080
|
+
hasMore: (page + 1) * perPage < total
|
|
17081
|
+
},
|
|
17082
|
+
...deltaCursor !== void 0 ? { deltaCursor } : {}
|
|
17083
|
+
};
|
|
17133
17084
|
}
|
|
17134
17085
|
async function listScoresDelta(client, table, filters, after, limit) {
|
|
17135
17086
|
return listSignalDelta({
|
|
@@ -17145,12 +17096,14 @@ async function listScoresDelta(client, table, filters, after, limit) {
|
|
|
17145
17096
|
});
|
|
17146
17097
|
}
|
|
17147
17098
|
async function runScoreAggregateQuery(client, schema, args, filters) {
|
|
17099
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17148
17100
|
const acc = newFilterAccumulator();
|
|
17149
17101
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17150
17102
|
applyScoreFilters(acc, filters);
|
|
17103
|
+
applyLatestScorePredicate(acc, table);
|
|
17151
17104
|
const sql = `
|
|
17152
17105
|
SELECT ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
|
|
17153
|
-
FROM ${
|
|
17106
|
+
FROM ${table} s
|
|
17154
17107
|
${whereOrEmpty(acc)}
|
|
17155
17108
|
`;
|
|
17156
17109
|
const row = await client.oneOrNone(sql, acc.params);
|
|
@@ -17182,10 +17135,12 @@ async function getScoreBreakdown(client, schema, args) {
|
|
|
17182
17135
|
});
|
|
17183
17136
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17184
17137
|
applyScoreFilters(acc, args.filters);
|
|
17138
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17139
|
+
applyLatestScorePredicate(acc, table);
|
|
17185
17140
|
const sql = `
|
|
17186
17141
|
SELECT ${resolved.map((e) => e.selectSql).join(", ")},
|
|
17187
17142
|
${aggregationSql(args.aggregation, "\"score\"")} AS "value"
|
|
17188
|
-
FROM ${
|
|
17143
|
+
FROM ${table} s
|
|
17189
17144
|
${whereOrEmpty(acc)}
|
|
17190
17145
|
GROUP BY ${resolved.map((e) => e.alias).join(", ")}
|
|
17191
17146
|
ORDER BY "value" DESC NULLS LAST
|
|
@@ -17205,11 +17160,13 @@ async function getScoreTimeSeries(client, schema, args) {
|
|
|
17205
17160
|
});
|
|
17206
17161
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17207
17162
|
applyScoreFilters(acc, args.filters);
|
|
17163
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17164
|
+
applyLatestScorePredicate(acc, table);
|
|
17208
17165
|
const sql = `
|
|
17209
17166
|
SELECT ${bucket} AS bucket,
|
|
17210
17167
|
${resolved.map((e) => e.selectSql).join(", ")},
|
|
17211
17168
|
${aggregationSql(args.aggregation, "\"score\"")} AS "value"
|
|
17212
|
-
FROM ${
|
|
17169
|
+
FROM ${table} s
|
|
17213
17170
|
${whereOrEmpty(acc)}
|
|
17214
17171
|
GROUP BY bucket, ${resolved.map((e) => e.alias).join(", ")}
|
|
17215
17172
|
ORDER BY bucket
|
|
@@ -17227,10 +17184,12 @@ async function getScoreTimeSeries(client, schema, args) {
|
|
|
17227
17184
|
const acc = newFilterAccumulator();
|
|
17228
17185
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17229
17186
|
applyScoreFilters(acc, args.filters);
|
|
17187
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17188
|
+
applyLatestScorePredicate(acc, table);
|
|
17230
17189
|
const sql = `
|
|
17231
17190
|
SELECT ${bucket} AS bucket,
|
|
17232
17191
|
${aggregationSql(args.aggregation, "\"score\"")} AS "value"
|
|
17233
|
-
FROM ${
|
|
17192
|
+
FROM ${table} s
|
|
17234
17193
|
${whereOrEmpty(acc)}
|
|
17235
17194
|
GROUP BY bucket
|
|
17236
17195
|
ORDER BY bucket
|
|
@@ -17250,9 +17209,11 @@ async function getScorePercentiles(client, schema, args) {
|
|
|
17250
17209
|
const acc = newFilterAccumulator();
|
|
17251
17210
|
pushScoreIdentity(acc, args.scorerId, args.scoreSource);
|
|
17252
17211
|
applyScoreFilters(acc, args.filters);
|
|
17212
|
+
const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
|
|
17213
|
+
applyLatestScorePredicate(acc, table);
|
|
17253
17214
|
const sql = `
|
|
17254
17215
|
SELECT ${bucket} AS bucket, ${percentileSelectSql(args.percentiles, "\"score\"")}
|
|
17255
|
-
FROM ${
|
|
17216
|
+
FROM ${table} s
|
|
17256
17217
|
${whereOrEmpty(acc)}
|
|
17257
17218
|
GROUP BY bucket
|
|
17258
17219
|
ORDER BY bucket
|
|
@@ -17315,6 +17276,11 @@ const FEEDBACK_FIELDS = {
|
|
|
17315
17276
|
const TRACE_SELECT = `
|
|
17316
17277
|
r."traceId" AS "traceId",
|
|
17317
17278
|
r."spanId" AS "rootSpanId",
|
|
17279
|
+
r."name" AS "name",
|
|
17280
|
+
r."entityId" AS "entityId",
|
|
17281
|
+
r."parentSpanId" AS "parentSpanId",
|
|
17282
|
+
r."metadata" AS "metadata",
|
|
17283
|
+
r."input" AS "input",
|
|
17318
17284
|
r."threadId" AS "threadId",
|
|
17319
17285
|
r."resourceId" AS "resourceId",
|
|
17320
17286
|
r."startedAt" AS "startedAt",
|
|
@@ -17446,13 +17412,6 @@ function latestSpanPredicate$1(spanTable) {
|
|
|
17446
17412
|
AND (newer."isPending" < s."isPending" OR (newer."isPending" = s."isPending" AND newer."cursorId" > s."cursorId"))
|
|
17447
17413
|
)`;
|
|
17448
17414
|
}
|
|
17449
|
-
function latestScorePredicate(scoreTable) {
|
|
17450
|
-
return `NOT EXISTS (
|
|
17451
|
-
SELECT 1 FROM ${scoreTable} newer
|
|
17452
|
-
WHERE newer."scoreId" = s."scoreId"
|
|
17453
|
-
AND newer."cursorId" > s."cursorId"
|
|
17454
|
-
)`;
|
|
17455
|
-
}
|
|
17456
17415
|
function latestFeedbackPredicate(feedbackTable) {
|
|
17457
17416
|
return `NOT EXISTS (
|
|
17458
17417
|
SELECT 1 FROM ${feedbackTable} newer
|
|
@@ -17622,7 +17581,7 @@ function compilePostgresTraceScope(schema, selection, relationCollections) {
|
|
|
17622
17581
|
values
|
|
17623
17582
|
};
|
|
17624
17583
|
}
|
|
17625
|
-
function compilePostgresTraceQuery(schema, plan) {
|
|
17584
|
+
function compilePostgresTraceQuery(schema, plan, mode = "data") {
|
|
17626
17585
|
const { ctes, values } = compilePostgresTraceScope(schema, plan, collectRelationCollections(plan.where));
|
|
17627
17586
|
let predicateSql = "TRUE";
|
|
17628
17587
|
if (plan.where) {
|
|
@@ -17636,6 +17595,12 @@ function compilePostgresTraceQuery(schema, plan) {
|
|
|
17636
17595
|
WHERE ${predicateSql}
|
|
17637
17596
|
)`);
|
|
17638
17597
|
const candidates = `WITH ${ctes.join(",\n")}`;
|
|
17598
|
+
if (plan.paginationMode === "page" && mode === "count") return {
|
|
17599
|
+
text: `${candidates}
|
|
17600
|
+
SELECT COUNT(*)::text AS count
|
|
17601
|
+
FROM candidates`,
|
|
17602
|
+
values
|
|
17603
|
+
};
|
|
17639
17604
|
if (plan.result === "groups") {
|
|
17640
17605
|
const pageCondition = plan.cursor ? `AND "threadId" > $${values.length + 1}` : "";
|
|
17641
17606
|
if (plan.cursor) values.push(plan.cursor.threadId);
|
|
@@ -17653,6 +17618,17 @@ LIMIT $${values.length}`,
|
|
|
17653
17618
|
}
|
|
17654
17619
|
const orderField = plan.orderBy.field === "startedAt" ? "\"startedAt\"" : "\"endedAt\"";
|
|
17655
17620
|
const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
|
|
17621
|
+
if (plan.paginationMode === "page") {
|
|
17622
|
+
values.push(plan.perPage, plan.page * plan.perPage);
|
|
17623
|
+
return {
|
|
17624
|
+
text: `${candidates}
|
|
17625
|
+
SELECT *
|
|
17626
|
+
FROM candidates
|
|
17627
|
+
ORDER BY ${orderField} ${direction}, "traceId" ASC
|
|
17628
|
+
LIMIT $${values.length - 1} OFFSET $${values.length}`,
|
|
17629
|
+
values
|
|
17630
|
+
};
|
|
17631
|
+
}
|
|
17656
17632
|
let pageCondition = "";
|
|
17657
17633
|
if (plan.cursor) {
|
|
17658
17634
|
const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
|
|
@@ -17794,15 +17770,22 @@ function isPostgresStatementTimeout(error) {
|
|
|
17794
17770
|
const candidate = error;
|
|
17795
17771
|
return candidate.code === "57014" && String(candidate.message ?? "").includes("statement timeout");
|
|
17796
17772
|
}
|
|
17797
|
-
|
|
17773
|
+
function isPostgresResourceLimit(error) {
|
|
17774
|
+
if (!error || typeof error !== "object") return false;
|
|
17775
|
+
const candidate = error;
|
|
17776
|
+
return candidate.code === "53200" || candidate.code === "53400";
|
|
17777
|
+
}
|
|
17778
|
+
async function runWithPostgresTraceQueryTimeout(client, timeoutMs, execute, options = {}) {
|
|
17798
17779
|
const resolvedTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
17799
17780
|
try {
|
|
17800
17781
|
return await client.tx(async (transaction) => {
|
|
17782
|
+
if (options.repeatableRead) await transaction.query("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");
|
|
17801
17783
|
await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${resolvedTimeoutMs}ms`]);
|
|
17802
17784
|
return execute(transaction);
|
|
17803
17785
|
});
|
|
17804
17786
|
} catch (error) {
|
|
17805
17787
|
if (isPostgresStatementTimeout(error)) throw new _mastra_core_storage.TraceQueryExecutionError();
|
|
17788
|
+
if (isPostgresResourceLimit(error)) throw new _mastra_core_storage.TraceQueryResourceLimitError();
|
|
17806
17789
|
throw error;
|
|
17807
17790
|
}
|
|
17808
17791
|
}
|
|
@@ -17830,6 +17813,50 @@ async function getTraceQueryValues(client, schema, plan, timeoutMs) {
|
|
|
17830
17813
|
});
|
|
17831
17814
|
}
|
|
17832
17815
|
async function queryTraces(client, schema, plan, timeoutMs) {
|
|
17816
|
+
if (plan.paginationMode === "page") {
|
|
17817
|
+
const resolvedTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
17818
|
+
const deadline = performance.now() + resolvedTimeoutMs;
|
|
17819
|
+
const countQuery = compilePostgresTraceQuery(schema, plan, "count");
|
|
17820
|
+
const dataQuery = compilePostgresTraceQuery(schema, plan);
|
|
17821
|
+
const { total, rows } = await runWithPostgresTraceQueryTimeout(client, resolvedTimeoutMs, async (transaction) => {
|
|
17822
|
+
const countRows = await transaction.any(countQuery.text, countQuery.values);
|
|
17823
|
+
const remainingTimeoutMs = Math.floor(deadline - performance.now());
|
|
17824
|
+
if (remainingTimeoutMs <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
|
|
17825
|
+
await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${remainingTimeoutMs}ms`]);
|
|
17826
|
+
const rows = await transaction.any(dataQuery.text, dataQuery.values);
|
|
17827
|
+
return {
|
|
17828
|
+
total: Number(countRows[0]?.count ?? 0),
|
|
17829
|
+
rows
|
|
17830
|
+
};
|
|
17831
|
+
}, { repeatableRead: true });
|
|
17832
|
+
const traces = rows.map((row) => ({
|
|
17833
|
+
traceId: String(row.traceId),
|
|
17834
|
+
rootSpanId: String(row.rootSpanId),
|
|
17835
|
+
name: row.name,
|
|
17836
|
+
entityId: row.entityId ?? null,
|
|
17837
|
+
parentSpanId: row.parentSpanId ?? null,
|
|
17838
|
+
createdAt: asIsoTimestamp$1(row.startedAt),
|
|
17839
|
+
metadata: row.metadata ?? null,
|
|
17840
|
+
inputPreview: _mastra_core_storage.buildInputPreview(row.input) ?? null,
|
|
17841
|
+
threadId: row.threadId == null ? null : String(row.threadId),
|
|
17842
|
+
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
17843
|
+
startedAt: asIsoTimestamp$1(row.startedAt),
|
|
17844
|
+
endedAt: asIsoTimestamp$1(row.endedAt),
|
|
17845
|
+
entityName: row.entityName == null ? null : String(row.entityName),
|
|
17846
|
+
entityType: row.entityType == null ? null : String(row.entityType),
|
|
17847
|
+
environment: row.environment == null ? null : String(row.environment),
|
|
17848
|
+
status: row.status
|
|
17849
|
+
}));
|
|
17850
|
+
return _mastra_core_storage.traceQueryResponseSchema.parse({
|
|
17851
|
+
traces,
|
|
17852
|
+
pagination: {
|
|
17853
|
+
total,
|
|
17854
|
+
page: plan.page,
|
|
17855
|
+
perPage: plan.perPage,
|
|
17856
|
+
hasMore: (plan.page + 1) * plan.perPage < total
|
|
17857
|
+
}
|
|
17858
|
+
});
|
|
17859
|
+
}
|
|
17833
17860
|
const query = compilePostgresTraceQuery(schema, plan);
|
|
17834
17861
|
const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
|
|
17835
17862
|
const visibleRows = rows.slice(0, plan.limit);
|
|
@@ -17847,6 +17874,12 @@ async function queryTraces(client, schema, plan, timeoutMs) {
|
|
|
17847
17874
|
const traces = visibleRows.map((row) => ({
|
|
17848
17875
|
traceId: String(row.traceId),
|
|
17849
17876
|
rootSpanId: String(row.rootSpanId),
|
|
17877
|
+
name: row.name,
|
|
17878
|
+
entityId: row.entityId ?? null,
|
|
17879
|
+
parentSpanId: row.parentSpanId ?? null,
|
|
17880
|
+
createdAt: asIsoTimestamp$1(row.startedAt),
|
|
17881
|
+
metadata: row.metadata ?? null,
|
|
17882
|
+
inputPreview: _mastra_core_storage.buildInputPreview(row.input) ?? null,
|
|
17850
17883
|
threadId: row.threadId == null ? null : String(row.threadId),
|
|
17851
17884
|
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
17852
17885
|
startedAt: asIsoTimestamp$1(row.startedAt),
|
|
@@ -18594,7 +18627,7 @@ async function dangerouslyClearTracing(client, schema) {
|
|
|
18594
18627
|
* Use it through `MastraCompositeStore` with a dedicated Postgres connection.
|
|
18595
18628
|
*/
|
|
18596
18629
|
function wrapError(op, error, details) {
|
|
18597
|
-
if (error instanceof _mastra_core_error.MastraError || error instanceof _mastra_core_storage.TraceQueryExecutionError) throw error;
|
|
18630
|
+
if (error instanceof _mastra_core_error.MastraError || error instanceof _mastra_core_storage.TraceQueryExecutionError || error instanceof _mastra_core_storage.TraceQueryResourceLimitError) throw error;
|
|
18598
18631
|
throw new _mastra_core_error.MastraError({
|
|
18599
18632
|
id: (0, _mastra_core_storage.createStorageErrorId)("PG", op, "FAILED"),
|
|
18600
18633
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|