@mastra/oracledb 0.5.2-alpha.0 → 0.5.2
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/index.cjs +334 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +335 -61
- package/dist/index.js.map +1 -1
- package/dist/storage/db/run-fencing.d.ts +32 -0
- package/dist/storage/db/run-fencing.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +18 -8
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/messages.d.ts +6 -4
- package/dist/storage/domains/memory/messages.d.ts.map +1 -1
- package/dist/storage/domains/memory/observational-buffering.d.ts +5 -5
- package/dist/storage/domains/memory/observational-buffering.d.ts.map +1 -1
- package/dist/storage/domains/memory/observational.d.ts +3 -3
- package/dist/storage/domains/memory/observational.d.ts.map +1 -1
- package/dist/storage/domains/memory/resources.d.ts +3 -2
- package/dist/storage/domains/memory/resources.d.ts.map +1 -1
- package/dist/storage/domains/memory/threads.d.ts +5 -3
- package/dist/storage/domains/memory/threads.d.ts.map +1 -1
- package/dist/storage/domains/memory/utils.d.ts +7 -1
- package/dist/storage/domains/memory/utils.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +19 -6
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { MastraVector, validateTopK, validateUpsertInput } from "@mastra/core/ve
|
|
|
2
2
|
import oracledb from "oracledb";
|
|
3
3
|
import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
|
|
4
4
|
import { createHash } from "crypto";
|
|
5
|
-
import { AgentsStorage, MCPClientsStorage, MastraCompositeStore, MemoryStorage, ObservabilityStorage, ScorerDefinitionsStorage, ScoresStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MESSAGES, TABLE_OBSERVATIONAL_MEMORY, TABLE_RESOURCES, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_WORKFLOW_SNAPSHOT, TraceStatus, WorkflowsStorage, calculatePagination, createStorageErrorId, createVectorErrorId, executeRetentionPrune, listLogsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, matchesExpectedWorkflowStatus, normalizePerPage, resolveRetentionTargets, retentionCutoffMs, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
|
|
5
|
+
import { AgentsStorage, MCPClientsStorage, MastraCompositeStore, MemoryStorage, ObservabilityStorage, RUN_FENCING_TABLE_SCHEMAS, RunFenceConflictError, ScorerDefinitionsStorage, ScoresStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MEMORY_RUN_FENCES, TABLE_MESSAGES, TABLE_OBSERVATIONAL_MEMORY, TABLE_RESOURCES, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_WORKFLOW_RUN_OWNERS, TABLE_WORKFLOW_SNAPSHOT, TraceStatus, WorkflowsStorage, calculatePagination, createStorageErrorId, createVectorErrorId, executeRetentionPrune, isRunFenceConflictError, listLogsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, matchesExpectedWorkflowStatus, matchesRunFence, normalizePerPage, resolveRetentionTargets, resolveRunFence, retentionCutoffMs, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
|
|
6
6
|
import { MessageList } from "@mastra/core/agent";
|
|
7
7
|
import { saveScorePayloadSchema } from "@mastra/core/evals";
|
|
8
8
|
//#region src/shared/connection.ts
|
|
@@ -3410,6 +3410,59 @@ function isAppendOnlySince(storedObservations, snapshotObservations) {
|
|
|
3410
3410
|
return stored === snapshot || stored.startsWith(snapshot);
|
|
3411
3411
|
}
|
|
3412
3412
|
//#endregion
|
|
3413
|
+
//#region src/storage/db/run-fencing.ts
|
|
3414
|
+
const SINCE_EPOCH = `(SYS_EXTRACT_UTC(SYSTIMESTAMP) - TIMESTAMP '1970-01-01 00:00:00')`;
|
|
3415
|
+
/**
|
|
3416
|
+
* Epoch milliseconds on the database clock. Oracle evaluates `SYSTIMESTAMP`
|
|
3417
|
+
* once per statement, so every reference here reads the same instant, and a
|
|
3418
|
+
* statement that waits on a lock compares against the time it started. That
|
|
3419
|
+
* error only makes an existing lease look longer and a new lease shorter.
|
|
3420
|
+
*/
|
|
3421
|
+
const DB_NOW_MS = `(EXTRACT(DAY FROM ${SINCE_EPOCH}) * 86400000 + EXTRACT(HOUR FROM ${SINCE_EPOCH}) * 3600000 + EXTRACT(MINUTE FROM ${SINCE_EPOCH}) * 60000 + FLOOR(EXTRACT(SECOND FROM ${SINCE_EPOCH}) * 1000))`;
|
|
3422
|
+
const ORA_UNIQUE_CONSTRAINT = -1;
|
|
3423
|
+
const ORA_DEADLOCK = -60;
|
|
3424
|
+
const MAX_CLAIM_ATTEMPTS = 5;
|
|
3425
|
+
/**
|
|
3426
|
+
* The check half of a fenced write; call it first inside the write's
|
|
3427
|
+
* transaction. Oracle has no shared row lock, so this takes the claim row
|
|
3428
|
+
* `FOR UPDATE`: a claim that updates the row waits for the write to commit,
|
|
3429
|
+
* and a write queued behind a claim re-reads the row once the claim commits
|
|
3430
|
+
* and sees the new generation. Writes from one run serialize on its row,
|
|
3431
|
+
* which they mostly do anyway.
|
|
3432
|
+
*/
|
|
3433
|
+
async function assertRunFence(client, claimsTable, fence, operation) {
|
|
3434
|
+
const row = await client.oneOrNone(`SELECT generation AS "generation", "ownerId" FROM ${claimsTable} WHERE "runId" = :runId FOR UPDATE`, { runId: fence.runId });
|
|
3435
|
+
if (!matchesRunFence(row ? {
|
|
3436
|
+
generation: Number(row.generation),
|
|
3437
|
+
ownerId: String(row.ownerId)
|
|
3438
|
+
} : null, fence)) throw new RunFenceConflictError(fence, operation);
|
|
3439
|
+
}
|
|
3440
|
+
/**
|
|
3441
|
+
* Runs `write` in a transaction, behind the fence check when it carries a
|
|
3442
|
+
* fence.
|
|
3443
|
+
*/
|
|
3444
|
+
function withRunFence(db, claimsTable, fence, operation, write) {
|
|
3445
|
+
return db.tx(async (client) => {
|
|
3446
|
+
if (fence) await assertRunFence(client, claimsTable, fence, operation);
|
|
3447
|
+
return write(client);
|
|
3448
|
+
});
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Runs a transaction that creates or updates a run's claim row, retrying when
|
|
3452
|
+
* two first claims race to insert it. `FOR UPDATE` locks nothing while the row
|
|
3453
|
+
* is missing, so racing first claims both insert; the second waits on the
|
|
3454
|
+
* primary key and fails once the first commits. On retry the row exists and
|
|
3455
|
+
* the claim serializes behind its row lock. A deadlock is retried too.
|
|
3456
|
+
*/
|
|
3457
|
+
async function claimTransaction(db, fn) {
|
|
3458
|
+
for (let attempt = 1;; attempt++) try {
|
|
3459
|
+
return await db.tx((client) => fn(client));
|
|
3460
|
+
} catch (error) {
|
|
3461
|
+
const retryable = isOracleErrorCode(error, [ORA_UNIQUE_CONSTRAINT, ORA_DEADLOCK]);
|
|
3462
|
+
if (attempt >= MAX_CLAIM_ATTEMPTS || !retryable) throw error;
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
//#endregion
|
|
3413
3466
|
//#region src/storage/domains/memory/utils.ts
|
|
3414
3467
|
const STORE_NAME$5 = "ORACLEDB";
|
|
3415
3468
|
const EMPTY_STRING_SENTINEL = "__MASTRA_ORACLE_EMPTY_STRING__";
|
|
@@ -3417,6 +3470,14 @@ const STRING_SENTINEL_ESCAPE_PREFIX = "__MASTRA_ORACLE_ESCAPED__";
|
|
|
3417
3470
|
function table(ctx, tableName) {
|
|
3418
3471
|
return qualifyName(tableName, ctx.schemaName);
|
|
3419
3472
|
}
|
|
3473
|
+
/**
|
|
3474
|
+
* The fence check for a memory write; call it first inside the write's
|
|
3475
|
+
* transaction. `fence` is the one `MemoryOracle` already resolved against the
|
|
3476
|
+
* run fence scope, so an unfenced write skips the check.
|
|
3477
|
+
*/
|
|
3478
|
+
async function assertMemoryFence(ctx, client, fence, operation) {
|
|
3479
|
+
if (fence) await assertRunFence(client, table(ctx, TABLE_MEMORY_RUN_FENCES), fence, operation);
|
|
3480
|
+
}
|
|
3420
3481
|
function storageError$1(operation, reason, details, cause, category = ErrorCategory.THIRD_PARTY) {
|
|
3421
3482
|
return createOracleStorageError({
|
|
3422
3483
|
storeName: STORE_NAME$5,
|
|
@@ -3974,7 +4035,7 @@ async function listMessagesByResourceId(ctx, args) {
|
|
|
3974
4035
|
orderBy
|
|
3975
4036
|
});
|
|
3976
4037
|
}
|
|
3977
|
-
async function saveMessages(ctx, { messages }) {
|
|
4038
|
+
async function saveMessages(ctx, { messages, fence }) {
|
|
3978
4039
|
if (messages.length === 0) return { messages: [] };
|
|
3979
4040
|
const threadIds = /* @__PURE__ */ new Set();
|
|
3980
4041
|
try {
|
|
@@ -3984,6 +4045,7 @@ async function saveMessages(ctx, { messages }) {
|
|
|
3984
4045
|
}
|
|
3985
4046
|
for (const threadId of threadIds) if (!await ctx.getThreadById({ threadId })) throw storageError$1("SAVE_MESSAGES", "FAILED", { threadId }, /* @__PURE__ */ new Error(`Thread ${threadId} not found`), ErrorCategory.USER);
|
|
3986
4047
|
await ctx.db.tx(async (client) => {
|
|
4048
|
+
await assertMemoryFence(ctx, client, fence, "saveMessages");
|
|
3987
4049
|
await insertMessageBatch(ctx, client, messages);
|
|
3988
4050
|
});
|
|
3989
4051
|
return { messages: new MessageList().add(messages, "memory").get.all.db() };
|
|
@@ -4061,7 +4123,7 @@ async function insertMessageBatch(ctx, client, messages) {
|
|
|
4061
4123
|
threadId: messageThreadId
|
|
4062
4124
|
})));
|
|
4063
4125
|
}
|
|
4064
|
-
async function updateMessages(ctx, { messages }) {
|
|
4126
|
+
async function updateMessages(ctx, { messages, fence }) {
|
|
4065
4127
|
if (messages.length === 0) return [];
|
|
4066
4128
|
const messageIds = messages.map((message) => message.id);
|
|
4067
4129
|
const existingMessages = (await ctx.listMessagesById({ messageIds })).messages;
|
|
@@ -4070,6 +4132,7 @@ async function updateMessages(ctx, { messages }) {
|
|
|
4070
4132
|
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
4071
4133
|
try {
|
|
4072
4134
|
await ctx.db.tx(async (client) => {
|
|
4135
|
+
await assertMemoryFence(ctx, client, fence, "updateMessages");
|
|
4073
4136
|
const semanticRecallMessageIds = [];
|
|
4074
4137
|
for (const updatePayload of messages) {
|
|
4075
4138
|
const existing = existingById.get(updatePayload.id);
|
|
@@ -4116,13 +4179,15 @@ async function updateMessages(ctx, { messages }) {
|
|
|
4116
4179
|
});
|
|
4117
4180
|
return (await ctx.listMessagesById({ messageIds })).messages;
|
|
4118
4181
|
} catch (error) {
|
|
4182
|
+
if (error instanceof MastraError) throw error;
|
|
4119
4183
|
throw storageError$1("UPDATE_MESSAGES", "FAILED", { messageIds: messageIds.join(",") }, error);
|
|
4120
4184
|
}
|
|
4121
4185
|
}
|
|
4122
|
-
async function deleteMessages(ctx, messageIds) {
|
|
4186
|
+
async function deleteMessages(ctx, messageIds, fence) {
|
|
4123
4187
|
if (!messageIds.length) return;
|
|
4124
4188
|
try {
|
|
4125
4189
|
await ctx.db.tx(async (client) => {
|
|
4190
|
+
await assertMemoryFence(ctx, client, fence, "deleteMessages");
|
|
4126
4191
|
const threadIds = /* @__PURE__ */ new Set();
|
|
4127
4192
|
for (const [chunkIndex, chunk] of chunkValues(messageIds).entries()) {
|
|
4128
4193
|
const { sql, binds } = inClause(`messageId${chunkIndex}`, chunk);
|
|
@@ -4138,6 +4203,7 @@ async function deleteMessages(ctx, messageIds) {
|
|
|
4138
4203
|
})));
|
|
4139
4204
|
});
|
|
4140
4205
|
} catch (error) {
|
|
4206
|
+
if (error instanceof MastraError) throw error;
|
|
4141
4207
|
throw storageError$1("DELETE_MESSAGES", "FAILED", { messageIds: messageIds.join(",") }, error);
|
|
4142
4208
|
}
|
|
4143
4209
|
}
|
|
@@ -4519,9 +4585,10 @@ async function insertObservationalMemoryRecord(ctx, record) {
|
|
|
4519
4585
|
}, error);
|
|
4520
4586
|
}
|
|
4521
4587
|
}
|
|
4522
|
-
async function updateActiveObservations(ctx, input) {
|
|
4588
|
+
async function updateActiveObservations(ctx, input, fence) {
|
|
4523
4589
|
try {
|
|
4524
|
-
return await ctx.db.tx(async (
|
|
4590
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
4591
|
+
await assertMemoryFence(ctx, client, fence, "commitActiveObservations");
|
|
4525
4592
|
const row = await lockOMRow(ctx, connection, input.id, "UPDATE_ACTIVE_OBSERVATIONS");
|
|
4526
4593
|
if (row.supersededBy) return {
|
|
4527
4594
|
applied: false,
|
|
@@ -4558,10 +4625,11 @@ async function updateActiveObservations(ctx, input) {
|
|
|
4558
4625
|
throw storageError$1("UPDATE_ACTIVE_OBSERVATIONS", "FAILED", { id: input.id }, error);
|
|
4559
4626
|
}
|
|
4560
4627
|
}
|
|
4561
|
-
async function createReflectionGeneration(ctx, input) {
|
|
4628
|
+
async function createReflectionGeneration(ctx, input, fence) {
|
|
4562
4629
|
const { currentRecord } = input;
|
|
4563
4630
|
try {
|
|
4564
|
-
return await ctx.db.tx(async (
|
|
4631
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
4632
|
+
await assertMemoryFence(ctx, client, fence, "createReflectionGeneration");
|
|
4565
4633
|
const row = await findOMRowForUpdate(ctx, connection, currentRecord.id);
|
|
4566
4634
|
if (!row) return currentRecord;
|
|
4567
4635
|
if (row.supersededBy) return parseOMRow(await resolveLiveOMRow(ctx, connection, row));
|
|
@@ -4969,9 +5037,10 @@ async function resolveLiveOMRow(ctx, connection, row) {
|
|
|
4969
5037
|
}
|
|
4970
5038
|
//#endregion
|
|
4971
5039
|
//#region src/storage/domains/memory/observational-buffering.ts
|
|
4972
|
-
async function updateBufferedObservations(ctx, input) {
|
|
5040
|
+
async function updateBufferedObservations(ctx, input, fence) {
|
|
4973
5041
|
try {
|
|
4974
|
-
return await ctx.db.tx(async (
|
|
5042
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
5043
|
+
await assertMemoryFence(ctx, client, fence, "appendBufferedObservations");
|
|
4975
5044
|
const row = await resolveLiveOMRow(ctx, connection, await lockOMRow(ctx, connection, input.id, "UPDATE_BUFFERED_OBSERVATIONS"));
|
|
4976
5045
|
const existingChunks = parseBufferedChunks(row.bufferedObservationChunks);
|
|
4977
5046
|
if (existingChunks.some((existing) => existing.cycleId === input.chunk.cycleId) || isBufferedChunkCoveredByCursor(input.chunk.lastObservedAt, row.lastObservedAt)) return {
|
|
@@ -5014,9 +5083,10 @@ async function updateBufferedObservations(ctx, input) {
|
|
|
5014
5083
|
throw storageError$1("UPDATE_BUFFERED_OBSERVATIONS", "FAILED", { id: input.id }, error);
|
|
5015
5084
|
}
|
|
5016
5085
|
}
|
|
5017
|
-
async function swapBufferedToActive(ctx, input) {
|
|
5086
|
+
async function swapBufferedToActive(ctx, input, fence) {
|
|
5018
5087
|
try {
|
|
5019
|
-
return await ctx.db.tx(async (
|
|
5088
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
5089
|
+
await assertMemoryFence(ctx, client, fence, "swapBufferedToActive");
|
|
5020
5090
|
const row = await lockOMRow(ctx, connection, input.id, "SWAP_BUFFERED_TO_ACTIVE");
|
|
5021
5091
|
if (row.supersededBy) return {
|
|
5022
5092
|
...emptySwapResult(),
|
|
@@ -5061,9 +5131,10 @@ async function swapBufferedToActive(ctx, input) {
|
|
|
5061
5131
|
throw storageError$1("SWAP_BUFFERED_TO_ACTIVE", "FAILED", { id: input.id }, error);
|
|
5062
5132
|
}
|
|
5063
5133
|
}
|
|
5064
|
-
async function updateBufferedReflection(ctx, input) {
|
|
5134
|
+
async function updateBufferedReflection(ctx, input, fence) {
|
|
5065
5135
|
try {
|
|
5066
|
-
await ctx.db.tx(async (
|
|
5136
|
+
await ctx.db.tx(async (client, connection) => {
|
|
5137
|
+
await assertMemoryFence(ctx, client, fence, "updateBufferedReflection");
|
|
5067
5138
|
assertRowsAffected((await connection.execute(`UPDATE ${table(ctx, TABLE_OBSERVATIONAL_MEMORY)}
|
|
5068
5139
|
SET ${OM_BUFFERED_REFLECTION} = CASE
|
|
5069
5140
|
WHEN ${OM_BUFFERED_REFLECTION} IS NOT NULL AND DBMS_LOB.GETLENGTH(${OM_BUFFERED_REFLECTION}) > 0
|
|
@@ -5088,10 +5159,11 @@ async function updateBufferedReflection(ctx, input) {
|
|
|
5088
5159
|
throw storageError$1("UPDATE_BUFFERED_REFLECTION", "FAILED", { id: input.id }, error);
|
|
5089
5160
|
}
|
|
5090
5161
|
}
|
|
5091
|
-
async function swapBufferedReflectionToActive(ctx, input) {
|
|
5162
|
+
async function swapBufferedReflectionToActive(ctx, input, fence) {
|
|
5092
5163
|
const { currentRecord } = input;
|
|
5093
5164
|
try {
|
|
5094
|
-
return await ctx.db.tx(async (
|
|
5165
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
5166
|
+
await assertMemoryFence(ctx, client, fence, "swapBufferedReflectionToActive");
|
|
5095
5167
|
const row = await findOMRowForUpdate(ctx, connection, currentRecord.id);
|
|
5096
5168
|
if (!row) return currentRecord;
|
|
5097
5169
|
if (row.supersededBy) return parseOMRow(await resolveLiveOMRow(ctx, connection, row));
|
|
@@ -5244,17 +5316,24 @@ async function saveResource(ctx, { resource }) {
|
|
|
5244
5316
|
throw storageError$1("SAVE_RESOURCE", "FAILED", { resourceId: resource.id }, error);
|
|
5245
5317
|
}
|
|
5246
5318
|
}
|
|
5247
|
-
async function updateResource(ctx, { resourceId, workingMemory, metadata }) {
|
|
5319
|
+
async function updateResource(ctx, { resourceId, workingMemory, metadata, fence }) {
|
|
5248
5320
|
const existing = await ctx.getResourceById({ resourceId });
|
|
5249
|
-
|
|
5321
|
+
const newResource = () => ({
|
|
5250
5322
|
id: resourceId,
|
|
5251
5323
|
workingMemory,
|
|
5252
5324
|
metadata: metadata ?? {},
|
|
5253
5325
|
createdAt: /* @__PURE__ */ new Date(),
|
|
5254
5326
|
updatedAt: /* @__PURE__ */ new Date()
|
|
5255
|
-
}
|
|
5327
|
+
});
|
|
5328
|
+
if (!existing && !fence) return ctx.saveResource({ resource: newResource() });
|
|
5256
5329
|
try {
|
|
5257
|
-
return await ctx.db.tx(async (
|
|
5330
|
+
return await ctx.db.tx(async (client, connection) => {
|
|
5331
|
+
await assertMemoryFence(ctx, client, fence, "updateResource");
|
|
5332
|
+
if (!existing) {
|
|
5333
|
+
const resource = newResource();
|
|
5334
|
+
await mergeResourceRow(ctx, connection, resource);
|
|
5335
|
+
return resource;
|
|
5336
|
+
}
|
|
5258
5337
|
const row = rows(await connection.execute(`${resourceSelect()} FROM ${table(ctx, TABLE_RESOURCES)} WHERE id = :resourceId FOR UPDATE`, { resourceId }, executeOptions()))[0];
|
|
5259
5338
|
const locked = row ? parseResource(row) : existing;
|
|
5260
5339
|
const updated = {
|
|
@@ -5348,11 +5427,15 @@ async function getThreadById(ctx, { threadId, resourceId }) {
|
|
|
5348
5427
|
throw storageError$1("GET_THREAD_BY_ID", "FAILED", { threadId }, error);
|
|
5349
5428
|
}
|
|
5350
5429
|
}
|
|
5351
|
-
async function saveThread(ctx, { thread }) {
|
|
5430
|
+
async function saveThread(ctx, { thread, fence }) {
|
|
5352
5431
|
try {
|
|
5353
|
-
await
|
|
5432
|
+
await ctx.db.tx(async (client) => {
|
|
5433
|
+
await assertMemoryFence(ctx, client, fence, "saveThread");
|
|
5434
|
+
await mergeThreadRow(ctx, client, thread);
|
|
5435
|
+
});
|
|
5354
5436
|
return thread;
|
|
5355
5437
|
} catch (error) {
|
|
5438
|
+
if (error instanceof MastraError) throw error;
|
|
5356
5439
|
throw storageError$1("SAVE_THREAD", "FAILED", { threadId: thread.id }, error);
|
|
5357
5440
|
}
|
|
5358
5441
|
}
|
|
@@ -5437,7 +5520,7 @@ async function insertThreadRow(ctx, client, thread) {
|
|
|
5437
5520
|
updatedAt: thread.updatedAt ?? /* @__PURE__ */ new Date()
|
|
5438
5521
|
});
|
|
5439
5522
|
}
|
|
5440
|
-
async function updateThread(ctx, { id, title, metadata }) {
|
|
5523
|
+
async function updateThread(ctx, { id, title, metadata, fence }) {
|
|
5441
5524
|
const existingThread = await ctx.getThreadById({ threadId: id });
|
|
5442
5525
|
if (!existingThread) throw storageError$1("UPDATE_THREAD", "FAILED", {
|
|
5443
5526
|
threadId: id,
|
|
@@ -5449,16 +5532,19 @@ async function updateThread(ctx, { id, title, metadata }) {
|
|
|
5449
5532
|
};
|
|
5450
5533
|
const updatedAt = /* @__PURE__ */ new Date();
|
|
5451
5534
|
try {
|
|
5452
|
-
await ctx.db.
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5535
|
+
await ctx.db.tx(async (client) => {
|
|
5536
|
+
await assertMemoryFence(ctx, client, fence, "updateThread");
|
|
5537
|
+
await client.none(`
|
|
5538
|
+
UPDATE ${table(ctx, TABLE_THREADS)}
|
|
5539
|
+
SET title = COALESCE(:title, title),
|
|
5540
|
+
metadata = :metadata,
|
|
5541
|
+
${THREAD_UPDATED_AT} = :updatedAt
|
|
5542
|
+
WHERE id = :id`, {
|
|
5543
|
+
id,
|
|
5544
|
+
title: optionalStringBind(title),
|
|
5545
|
+
metadata: jsonBind(mergedMetadata),
|
|
5546
|
+
updatedAt
|
|
5547
|
+
});
|
|
5462
5548
|
});
|
|
5463
5549
|
const updatedThread = await ctx.getThreadById({ threadId: id });
|
|
5464
5550
|
if (!updatedThread) throw storageError$1("UPDATE_THREAD", "FAILED", {
|
|
@@ -5610,7 +5696,8 @@ var MemoryOracle = class MemoryOracle extends MemoryStorage {
|
|
|
5610
5696
|
TABLE_THREADS,
|
|
5611
5697
|
TABLE_MESSAGES,
|
|
5612
5698
|
TABLE_RESOURCES,
|
|
5613
|
-
TABLE_OBSERVATIONAL_MEMORY
|
|
5699
|
+
TABLE_OBSERVATIONAL_MEMORY,
|
|
5700
|
+
TABLE_MEMORY_RUN_FENCES
|
|
5614
5701
|
];
|
|
5615
5702
|
db;
|
|
5616
5703
|
schemaName;
|
|
@@ -5648,6 +5735,10 @@ var MemoryOracle = class MemoryOracle extends MemoryStorage {
|
|
|
5648
5735
|
}
|
|
5649
5736
|
async init() {
|
|
5650
5737
|
await initMemorySchema(this.ctx);
|
|
5738
|
+
await this.db.createTable({
|
|
5739
|
+
tableName: TABLE_MEMORY_RUN_FENCES,
|
|
5740
|
+
schema: RUN_FENCING_TABLE_SCHEMAS[TABLE_MEMORY_RUN_FENCES]
|
|
5741
|
+
});
|
|
5651
5742
|
}
|
|
5652
5743
|
/**
|
|
5653
5744
|
* Mark observational memory rows that are not their key's canonical head as superseded.
|
|
@@ -5660,15 +5751,61 @@ var MemoryOracle = class MemoryOracle extends MemoryStorage {
|
|
|
5660
5751
|
}
|
|
5661
5752
|
async dangerouslyClearAll() {
|
|
5662
5753
|
await clearAllMemoryTables(this.ctx);
|
|
5754
|
+
await this.db.clearTable(TABLE_MEMORY_RUN_FENCES);
|
|
5755
|
+
}
|
|
5756
|
+
supportsRunFencing() {
|
|
5757
|
+
return true;
|
|
5758
|
+
}
|
|
5759
|
+
async raiseRunFence(fence) {
|
|
5760
|
+
const fences = table(this.ctx, TABLE_MEMORY_RUN_FENCES);
|
|
5761
|
+
try {
|
|
5762
|
+
return await claimTransaction(this.db, async (client) => {
|
|
5763
|
+
const row = await client.oneOrNone(`SELECT generation AS "generation", "ownerId" FROM ${fences} WHERE "runId" = :runId FOR UPDATE`, { runId: fence.runId });
|
|
5764
|
+
const binds = {
|
|
5765
|
+
runId: fence.runId,
|
|
5766
|
+
generation: fence.generation,
|
|
5767
|
+
ownerId: fence.ownerId
|
|
5768
|
+
};
|
|
5769
|
+
if (!row) {
|
|
5770
|
+
await client.none(`INSERT INTO ${fences} ("runId", generation, "ownerId") VALUES (:runId, :generation, :ownerId)`, binds);
|
|
5771
|
+
return true;
|
|
5772
|
+
}
|
|
5773
|
+
const generation = Number(row.generation);
|
|
5774
|
+
if (!(generation < fence.generation) && !(generation === fence.generation && String(row.ownerId) === fence.ownerId)) return false;
|
|
5775
|
+
await client.none(`UPDATE ${fences} SET generation = :generation, "ownerId" = :ownerId, "retiredAt" = NULL WHERE "runId" = :runId`, binds);
|
|
5776
|
+
return true;
|
|
5777
|
+
});
|
|
5778
|
+
} catch (error) {
|
|
5779
|
+
throw storageError$1("RAISE_RUN_FENCE", "FAILED", { runId: fence.runId }, error);
|
|
5780
|
+
}
|
|
5781
|
+
}
|
|
5782
|
+
async retireRunFence(fence) {
|
|
5783
|
+
const fences = table(this.ctx, TABLE_MEMORY_RUN_FENCES);
|
|
5784
|
+
try {
|
|
5785
|
+
return await this.db.tx(async (client) => {
|
|
5786
|
+
const row = await client.oneOrNone(`SELECT generation AS "generation", "ownerId" FROM ${fences} WHERE "runId" = :runId FOR UPDATE`, { runId: fence.runId });
|
|
5787
|
+
if (!row || Number(row.generation) !== fence.generation || String(row.ownerId) !== fence.ownerId) return false;
|
|
5788
|
+
await client.none(`UPDATE ${fences} SET "retiredAt" = ${DB_NOW_MS} WHERE "runId" = :runId`, { runId: fence.runId });
|
|
5789
|
+
return true;
|
|
5790
|
+
});
|
|
5791
|
+
} catch (error) {
|
|
5792
|
+
throw storageError$1("RETIRE_RUN_FENCE", "FAILED", { runId: fence.runId }, error);
|
|
5793
|
+
}
|
|
5663
5794
|
}
|
|
5664
5795
|
async getThreadById(args) {
|
|
5665
5796
|
return getThreadById(this.ctx, args);
|
|
5666
5797
|
}
|
|
5667
|
-
async saveThread(args) {
|
|
5668
|
-
return saveThread(this.ctx,
|
|
5798
|
+
async saveThread({ fence, ...args }) {
|
|
5799
|
+
return saveThread(this.ctx, {
|
|
5800
|
+
...args,
|
|
5801
|
+
fence: resolveRunFence(this, fence)
|
|
5802
|
+
});
|
|
5669
5803
|
}
|
|
5670
|
-
async updateThread(args) {
|
|
5671
|
-
return updateThread(this.ctx,
|
|
5804
|
+
async updateThread({ fence, ...args }) {
|
|
5805
|
+
return updateThread(this.ctx, {
|
|
5806
|
+
...args,
|
|
5807
|
+
fence: resolveRunFence(this, fence)
|
|
5808
|
+
});
|
|
5672
5809
|
}
|
|
5673
5810
|
async deleteThread(args) {
|
|
5674
5811
|
return deleteThread(this.ctx, args);
|
|
@@ -5688,14 +5825,20 @@ var MemoryOracle = class MemoryOracle extends MemoryStorage {
|
|
|
5688
5825
|
async listMessagesByResourceId(args) {
|
|
5689
5826
|
return listMessagesByResourceId(this.ctx, args);
|
|
5690
5827
|
}
|
|
5691
|
-
async saveMessages(args) {
|
|
5692
|
-
return saveMessages(this.ctx,
|
|
5828
|
+
async saveMessages({ fence, ...args }) {
|
|
5829
|
+
return saveMessages(this.ctx, {
|
|
5830
|
+
...args,
|
|
5831
|
+
fence: resolveRunFence(this, fence)
|
|
5832
|
+
});
|
|
5693
5833
|
}
|
|
5694
|
-
async updateMessages(args) {
|
|
5695
|
-
return updateMessages(this.ctx,
|
|
5834
|
+
async updateMessages({ fence, ...args }) {
|
|
5835
|
+
return updateMessages(this.ctx, {
|
|
5836
|
+
...args,
|
|
5837
|
+
fence: resolveRunFence(this, fence)
|
|
5838
|
+
});
|
|
5696
5839
|
}
|
|
5697
|
-
async deleteMessages(messageIds) {
|
|
5698
|
-
return deleteMessages(this.ctx, messageIds);
|
|
5840
|
+
async deleteMessages(messageIds, options) {
|
|
5841
|
+
return deleteMessages(this.ctx, messageIds, resolveRunFence(this, options?.fence));
|
|
5699
5842
|
}
|
|
5700
5843
|
async getResourceById(args) {
|
|
5701
5844
|
return getResourceById(this.ctx, args);
|
|
@@ -5703,8 +5846,11 @@ var MemoryOracle = class MemoryOracle extends MemoryStorage {
|
|
|
5703
5846
|
async saveResource(args) {
|
|
5704
5847
|
return saveResource(this.ctx, args);
|
|
5705
5848
|
}
|
|
5706
|
-
async updateResource(args) {
|
|
5707
|
-
return updateResource(this.ctx,
|
|
5849
|
+
async updateResource({ fence, ...args }) {
|
|
5850
|
+
return updateResource(this.ctx, {
|
|
5851
|
+
...args,
|
|
5852
|
+
fence: resolveRunFence(this, fence)
|
|
5853
|
+
});
|
|
5708
5854
|
}
|
|
5709
5855
|
async cloneThread(args) {
|
|
5710
5856
|
const sourceThread = await this.getThreadById({ threadId: args.sourceThreadId });
|
|
@@ -5797,10 +5943,10 @@ var MemoryOracle = class MemoryOracle extends MemoryStorage {
|
|
|
5797
5943
|
assertActiveObservationsApplied(await this.commitActiveObservations(input), input.id);
|
|
5798
5944
|
}
|
|
5799
5945
|
async commitActiveObservations(input) {
|
|
5800
|
-
return updateActiveObservations(this.ctx, input);
|
|
5946
|
+
return updateActiveObservations(this.ctx, input, resolveRunFence(this, void 0));
|
|
5801
5947
|
}
|
|
5802
5948
|
async createReflectionGeneration(input) {
|
|
5803
|
-
return createReflectionGeneration(this.ctx, input);
|
|
5949
|
+
return createReflectionGeneration(this.ctx, input, resolveRunFence(this, void 0));
|
|
5804
5950
|
}
|
|
5805
5951
|
async setReflectingFlag(id, isReflecting) {
|
|
5806
5952
|
return setReflectingFlag(this.ctx, id, isReflecting);
|
|
@@ -5827,16 +5973,16 @@ var MemoryOracle = class MemoryOracle extends MemoryStorage {
|
|
|
5827
5973
|
await this.appendBufferedObservations(input);
|
|
5828
5974
|
}
|
|
5829
5975
|
async appendBufferedObservations(input) {
|
|
5830
|
-
return updateBufferedObservations(this.ctx, input);
|
|
5976
|
+
return updateBufferedObservations(this.ctx, input, resolveRunFence(this, void 0));
|
|
5831
5977
|
}
|
|
5832
5978
|
async swapBufferedToActive(input) {
|
|
5833
|
-
return swapBufferedToActive(this.ctx, input);
|
|
5979
|
+
return swapBufferedToActive(this.ctx, input, resolveRunFence(this, void 0));
|
|
5834
5980
|
}
|
|
5835
5981
|
async updateBufferedReflection(input) {
|
|
5836
|
-
return updateBufferedReflection(this.ctx, input);
|
|
5982
|
+
return updateBufferedReflection(this.ctx, input, resolveRunFence(this, void 0));
|
|
5837
5983
|
}
|
|
5838
5984
|
async swapBufferedReflectionToActive(input) {
|
|
5839
|
-
return swapBufferedReflectionToActive(this.ctx, input);
|
|
5985
|
+
return swapBufferedReflectionToActive(this.ctx, input, resolveRunFence(this, void 0));
|
|
5840
5986
|
}
|
|
5841
5987
|
};
|
|
5842
5988
|
//#endregion
|
|
@@ -8181,8 +8327,19 @@ const STORE_NAME$1 = "ORACLEDB";
|
|
|
8181
8327
|
const WORKFLOW_CREATED_AT = "\"createdAt\"";
|
|
8182
8328
|
const WORKFLOW_UPDATED_AT = "\"updatedAt\"";
|
|
8183
8329
|
const WORKFLOW_RESOURCE_ID = "\"resourceId\"";
|
|
8330
|
+
const RUN_OWNER_COLUMNS = `generation AS "generation", "ownerId", "leaseExpiresAt", ${DB_NOW_MS} AS "nowMs"`;
|
|
8331
|
+
function toRunOwnershipRecord(runId, row) {
|
|
8332
|
+
const leaseExpiresAt = row.leaseExpiresAt === null ? null : Number(row.leaseExpiresAt);
|
|
8333
|
+
return {
|
|
8334
|
+
runId,
|
|
8335
|
+
generation: Number(row.generation),
|
|
8336
|
+
ownerId: String(row.ownerId),
|
|
8337
|
+
leaseExpiresAt: leaseExpiresAt === null ? null : new Date(leaseExpiresAt),
|
|
8338
|
+
live: leaseExpiresAt !== null && leaseExpiresAt > Number(row.nowMs)
|
|
8339
|
+
};
|
|
8340
|
+
}
|
|
8184
8341
|
var WorkflowsOracle = class WorkflowsOracle extends WorkflowsStorage {
|
|
8185
|
-
static MANAGED_TABLES = [TABLE_WORKFLOW_SNAPSHOT];
|
|
8342
|
+
static MANAGED_TABLES = [TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKFLOW_RUN_OWNERS];
|
|
8186
8343
|
db;
|
|
8187
8344
|
schemaName;
|
|
8188
8345
|
skipDefaultIndexes;
|
|
@@ -8203,10 +8360,114 @@ var WorkflowsOracle = class WorkflowsOracle extends WorkflowsStorage {
|
|
|
8203
8360
|
}
|
|
8204
8361
|
async dangerouslyClearAll() {
|
|
8205
8362
|
await this.db.clearTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
8363
|
+
await this.db.clearTable(TABLE_WORKFLOW_RUN_OWNERS);
|
|
8364
|
+
}
|
|
8365
|
+
supportsRunFencing() {
|
|
8366
|
+
return true;
|
|
8367
|
+
}
|
|
8368
|
+
async claimRunOwnership({ runId, ownerId, leaseMs, force, expectedGeneration }) {
|
|
8369
|
+
const table = this.runOwnersTable();
|
|
8370
|
+
try {
|
|
8371
|
+
return await claimTransaction(this.db, async (client) => {
|
|
8372
|
+
const existing = await client.oneOrNone(`SELECT generation AS "generation" FROM ${table} WHERE "runId" = :runId FOR UPDATE`, { runId });
|
|
8373
|
+
if (!existing) {
|
|
8374
|
+
if (expectedGeneration !== void 0 && expectedGeneration !== 0) return {
|
|
8375
|
+
acquired: false,
|
|
8376
|
+
record: null
|
|
8377
|
+
};
|
|
8378
|
+
await client.none(`INSERT INTO ${table} ("runId", generation, "ownerId", "leaseExpiresAt", "updatedAt")
|
|
8379
|
+
VALUES (:runId, ${DB_NOW_MS}, :ownerId, ${DB_NOW_MS} + :leaseMs, ${DB_NOW_MS})`, {
|
|
8380
|
+
runId,
|
|
8381
|
+
ownerId,
|
|
8382
|
+
leaseMs
|
|
8383
|
+
});
|
|
8384
|
+
return {
|
|
8385
|
+
acquired: true,
|
|
8386
|
+
record: await this.readRunOwner(client, runId)
|
|
8387
|
+
};
|
|
8388
|
+
}
|
|
8389
|
+
await client.none(`UPDATE ${table}
|
|
8390
|
+
SET generation = generation + 1, "ownerId" = :ownerId, "leaseExpiresAt" = ${DB_NOW_MS} + :leaseMs,
|
|
8391
|
+
"updatedAt" = ${DB_NOW_MS}
|
|
8392
|
+
WHERE "runId" = :runId
|
|
8393
|
+
AND (:expectedGeneration IS NULL OR generation = :expectedGeneration)
|
|
8394
|
+
AND (:force = 1 OR "leaseExpiresAt" IS NULL OR "leaseExpiresAt" <= ${DB_NOW_MS})`, {
|
|
8395
|
+
runId,
|
|
8396
|
+
ownerId,
|
|
8397
|
+
leaseMs,
|
|
8398
|
+
expectedGeneration: expectedGeneration ?? null,
|
|
8399
|
+
force: force === true ? 1 : 0
|
|
8400
|
+
});
|
|
8401
|
+
const record = await this.readRunOwner(client, runId);
|
|
8402
|
+
return record && record.generation === Number(existing.generation) + 1 ? {
|
|
8403
|
+
acquired: true,
|
|
8404
|
+
record
|
|
8405
|
+
} : {
|
|
8406
|
+
acquired: false,
|
|
8407
|
+
record
|
|
8408
|
+
};
|
|
8409
|
+
});
|
|
8410
|
+
} catch (error) {
|
|
8411
|
+
throw this.storageError("CLAIM_RUN_OWNERSHIP", "FAILED", { runId }, error);
|
|
8412
|
+
}
|
|
8413
|
+
}
|
|
8414
|
+
async renewRunOwnership({ leaseMs, ...fence }) {
|
|
8415
|
+
try {
|
|
8416
|
+
return await this.db.tx(async (client) => {
|
|
8417
|
+
await client.none(`UPDATE ${this.runOwnersTable()}
|
|
8418
|
+
SET "leaseExpiresAt" = ${DB_NOW_MS} + :leaseMs, "updatedAt" = ${DB_NOW_MS}
|
|
8419
|
+
WHERE "runId" = :runId AND generation = :generation AND "ownerId" = :ownerId
|
|
8420
|
+
AND "leaseExpiresAt" IS NOT NULL`, {
|
|
8421
|
+
runId: fence.runId,
|
|
8422
|
+
generation: fence.generation,
|
|
8423
|
+
ownerId: fence.ownerId,
|
|
8424
|
+
leaseMs
|
|
8425
|
+
});
|
|
8426
|
+
const record = await this.readRunOwner(client, fence.runId);
|
|
8427
|
+
return record !== null && record.generation === fence.generation && record.ownerId === fence.ownerId && record.leaseExpiresAt !== null ? {
|
|
8428
|
+
renewed: true,
|
|
8429
|
+
record
|
|
8430
|
+
} : {
|
|
8431
|
+
renewed: false,
|
|
8432
|
+
record
|
|
8433
|
+
};
|
|
8434
|
+
});
|
|
8435
|
+
} catch (error) {
|
|
8436
|
+
throw this.storageError("RENEW_RUN_OWNERSHIP", "FAILED", { runId: fence.runId }, error);
|
|
8437
|
+
}
|
|
8206
8438
|
}
|
|
8207
|
-
async
|
|
8439
|
+
async releaseRunOwnership(fence) {
|
|
8208
8440
|
try {
|
|
8209
8441
|
return await this.db.tx(async (client) => {
|
|
8442
|
+
await client.none(`UPDATE ${this.runOwnersTable()} SET "leaseExpiresAt" = NULL, "updatedAt" = ${DB_NOW_MS}
|
|
8443
|
+
WHERE "runId" = :runId AND generation = :generation AND "ownerId" = :ownerId`, {
|
|
8444
|
+
runId: fence.runId,
|
|
8445
|
+
generation: fence.generation,
|
|
8446
|
+
ownerId: fence.ownerId
|
|
8447
|
+
});
|
|
8448
|
+
const record = await this.readRunOwner(client, fence.runId);
|
|
8449
|
+
return record !== null && record.generation === fence.generation && record.ownerId === fence.ownerId;
|
|
8450
|
+
});
|
|
8451
|
+
} catch (error) {
|
|
8452
|
+
throw this.storageError("RELEASE_RUN_OWNERSHIP", "FAILED", { runId: fence.runId }, error);
|
|
8453
|
+
}
|
|
8454
|
+
}
|
|
8455
|
+
async getRunOwnership({ runId }) {
|
|
8456
|
+
try {
|
|
8457
|
+
return await this.readRunOwner(this.db, runId);
|
|
8458
|
+
} catch (error) {
|
|
8459
|
+
throw this.storageError("GET_RUN_OWNERSHIP", "FAILED", { runId }, error);
|
|
8460
|
+
}
|
|
8461
|
+
}
|
|
8462
|
+
async readRunOwner(client, runId) {
|
|
8463
|
+
const row = await client.oneOrNone(`SELECT ${RUN_OWNER_COLUMNS} FROM ${this.runOwnersTable()} WHERE "runId" = :runId`, { runId });
|
|
8464
|
+
return row ? toRunOwnershipRecord(runId, row) : null;
|
|
8465
|
+
}
|
|
8466
|
+
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state, fence: explicitFence }) {
|
|
8467
|
+
const fence = resolveRunFence(this, explicitFence, runId);
|
|
8468
|
+
try {
|
|
8469
|
+
return await this.db.tx(async (client) => {
|
|
8470
|
+
if (fence) await assertRunFence(client, this.runOwnersTable(), fence, "updateWorkflowResults");
|
|
8210
8471
|
await this.ensureWorkflowRunRow(client, workflowName, runId);
|
|
8211
8472
|
const existing = await client.one(`SELECT snapshot AS "snapshot", ${WORKFLOW_RESOURCE_ID} AS "resourceId" FROM ${this.table()} WHERE workflow_name = :workflowName AND run_id = :runId FOR UPDATE`, {
|
|
8212
8473
|
workflowName,
|
|
@@ -8230,6 +8491,7 @@ var WorkflowsOracle = class WorkflowsOracle extends WorkflowsStorage {
|
|
|
8230
8491
|
return snapshot.context;
|
|
8231
8492
|
});
|
|
8232
8493
|
} catch (error) {
|
|
8494
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
8233
8495
|
throw this.storageError("UPDATE_WORKFLOW_RESULTS", "FAILED", {
|
|
8234
8496
|
workflowName,
|
|
8235
8497
|
runId,
|
|
@@ -8237,9 +8499,11 @@ var WorkflowsOracle = class WorkflowsOracle extends WorkflowsStorage {
|
|
|
8237
8499
|
}, error);
|
|
8238
8500
|
}
|
|
8239
8501
|
}
|
|
8240
|
-
async updateWorkflowState({ workflowName, runId, opts }) {
|
|
8502
|
+
async updateWorkflowState({ workflowName, runId, opts, fence: explicitFence }) {
|
|
8503
|
+
const fence = resolveRunFence(this, explicitFence, runId);
|
|
8241
8504
|
try {
|
|
8242
8505
|
return await this.db.tx(async (client) => {
|
|
8506
|
+
if (fence) await assertRunFence(client, this.runOwnersTable(), fence, "updateWorkflowState");
|
|
8243
8507
|
const existing = await client.oneOrNone(`SELECT snapshot AS "snapshot" FROM ${this.table()} WHERE workflow_name = :workflowName AND run_id = :runId FOR UPDATE`, {
|
|
8244
8508
|
workflowName,
|
|
8245
8509
|
runId
|
|
@@ -8262,24 +8526,26 @@ var WorkflowsOracle = class WorkflowsOracle extends WorkflowsStorage {
|
|
|
8262
8526
|
return updatedSnapshot;
|
|
8263
8527
|
});
|
|
8264
8528
|
} catch (error) {
|
|
8529
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
8265
8530
|
throw this.storageError("UPDATE_WORKFLOW_STATE", "FAILED", {
|
|
8266
8531
|
workflowName,
|
|
8267
8532
|
runId
|
|
8268
8533
|
}, error);
|
|
8269
8534
|
}
|
|
8270
8535
|
}
|
|
8271
|
-
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) {
|
|
8536
|
+
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
|
|
8272
8537
|
try {
|
|
8273
8538
|
const now = /* @__PURE__ */ new Date();
|
|
8274
|
-
await this.db.none(this.workflowMergeSql(), {
|
|
8539
|
+
await withRunFence(this.db, this.runOwnersTable(), resolveRunFence(this, fence, runId), "persistWorkflowSnapshot", (client) => client.none(this.workflowMergeSql(), {
|
|
8275
8540
|
workflowName,
|
|
8276
8541
|
runId,
|
|
8277
8542
|
resourceId: resourceId ?? null,
|
|
8278
8543
|
snapshot: jsonBind(snapshot),
|
|
8279
8544
|
createdAt: createdAt ?? now,
|
|
8280
8545
|
updatedAt: updatedAt ?? now
|
|
8281
|
-
});
|
|
8546
|
+
}));
|
|
8282
8547
|
} catch (error) {
|
|
8548
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
8283
8549
|
throw this.storageError("PERSIST_WORKFLOW_SNAPSHOT", "FAILED", {
|
|
8284
8550
|
workflowName,
|
|
8285
8551
|
runId
|
|
@@ -8343,13 +8609,14 @@ var WorkflowsOracle = class WorkflowsOracle extends WorkflowsStorage {
|
|
|
8343
8609
|
}, error);
|
|
8344
8610
|
}
|
|
8345
8611
|
}
|
|
8346
|
-
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
8612
|
+
async deleteWorkflowRunById({ runId, workflowName, fence }) {
|
|
8347
8613
|
try {
|
|
8348
|
-
await this.db.none(`DELETE FROM ${this.table()} WHERE workflow_name = :workflowName AND run_id = :runId`, {
|
|
8614
|
+
await withRunFence(this.db, this.runOwnersTable(), resolveRunFence(this, fence, runId), "deleteWorkflowRunById", (client) => client.none(`DELETE FROM ${this.table()} WHERE workflow_name = :workflowName AND run_id = :runId`, {
|
|
8349
8615
|
workflowName,
|
|
8350
8616
|
runId
|
|
8351
|
-
});
|
|
8617
|
+
}));
|
|
8352
8618
|
} catch (error) {
|
|
8619
|
+
if (isRunFenceConflictError(error)) throw error;
|
|
8353
8620
|
throw this.storageError("DELETE_WORKFLOW_RUN_BY_ID", "FAILED", {
|
|
8354
8621
|
runId,
|
|
8355
8622
|
workflowName
|
|
@@ -8368,6 +8635,10 @@ var WorkflowsOracle = class WorkflowsOracle extends WorkflowsStorage {
|
|
|
8368
8635
|
CONSTRAINT ${this.constraint("MASTRA_WORKFLOW_SNAPSHOT_PK")} PRIMARY KEY (workflow_name, run_id)
|
|
8369
8636
|
)`, [-955]);
|
|
8370
8637
|
await this.db.executeDdl(`ALTER TABLE ${this.table()} ADD (${WORKFLOW_RESOURCE_ID} VARCHAR2(512))`, [-1430]);
|
|
8638
|
+
await this.db.createTable({
|
|
8639
|
+
tableName: TABLE_WORKFLOW_RUN_OWNERS,
|
|
8640
|
+
schema: RUN_FENCING_TABLE_SCHEMAS[TABLE_WORKFLOW_RUN_OWNERS]
|
|
8641
|
+
});
|
|
8371
8642
|
}
|
|
8372
8643
|
async createIndexes() {
|
|
8373
8644
|
await this.db.withConnection(async (connection) => {
|
|
@@ -8505,6 +8776,9 @@ var WorkflowsOracle = class WorkflowsOracle extends WorkflowsStorage {
|
|
|
8505
8776
|
table() {
|
|
8506
8777
|
return qualifyName(TABLE_WORKFLOW_SNAPSHOT, this.schemaName);
|
|
8507
8778
|
}
|
|
8779
|
+
runOwnersTable() {
|
|
8780
|
+
return qualifyName(TABLE_WORKFLOW_RUN_OWNERS, this.schemaName);
|
|
8781
|
+
}
|
|
8508
8782
|
indexName(indexName) {
|
|
8509
8783
|
return indexNameForTable(indexName, "IDX");
|
|
8510
8784
|
}
|