@convex-dev/action-retrier
Version:
Convex component for retrying idempotent actions.
103 lines (98 loc) • 2.58 kB
text/typescript
import { mutation, query } from "./_generated/server.js";
import { v } from "convex/values";
import { options, runResult } from "./schema.js";
import { finishExecution, startRun } from "./run.js";
export const start = mutation({
args: {
functionHandle: v.string(),
functionArgs: v.any(),
options,
},
returns: v.string(),
handler: async (ctx, args) => {
return await startRun(
ctx,
args.functionHandle,
args.functionArgs,
args.options,
);
},
});
export const status = query({
args: {
runId: v.string(),
},
returns: v.union(
v.object({
type: v.literal("inProgress"),
}),
v.object({
type: v.literal("completed"),
result: runResult,
}),
),
handler: async (ctx, args) => {
const id = ctx.db.normalizeId("runs", args.runId);
if (!id) {
throw new Error(`Run ${args.runId} not found`);
}
const run = await ctx.db.get(id);
if (!run) {
throw new Error(`Run ${args.runId} not found`);
}
if (run.state.type === "inProgress") {
return { type: "inProgress" as const };
} else if (run.state.type === "completed") {
return { type: "completed" as const, result: run.state.result };
} else {
throw new Error(`Invalid run state: ${JSON.stringify(run.state)}`);
}
},
});
export const cancel = mutation({
args: {
runId: v.string(),
},
returns: v.boolean(),
handler: async (ctx, args) => {
const id = ctx.db.normalizeId("runs", args.runId);
if (!id) {
return false;
}
const run = await ctx.db.get(id);
if (!run) {
return false;
}
if (run.state.type !== "inProgress") {
return false;
}
const schedulerId = run.state.schedulerId;
if (!schedulerId) {
return false;
}
// Note that this doesn't terminate execution immediately, but
// we are guaranteed that `finishExecution` won't succeed.
await ctx.scheduler.cancel(schedulerId);
await finishExecution(ctx, { runId: id, result: { type: "canceled" } });
return true;
},
});
export const cleanup = mutation({
args: {
runId: v.string(),
},
handler: async (ctx, args) => {
const id = ctx.db.normalizeId("runs", args.runId);
if (!id) {
throw new Error(`Run ${args.runId} not found`);
}
const run = await ctx.db.get(id);
if (!run) {
throw new Error(`Run ${args.runId} not found`);
}
if (run.state.type !== "completed") {
throw new Error(`Run ${args.runId} hasn't completed.`);
}
await ctx.db.delete(id);
},
});