fix: harden pgdata backup scheduler recovery
This commit is contained in:
@@ -35,9 +35,40 @@ function terminalRunStatus(status: unknown): boolean {
|
||||
return ["succeeded", "failed", "skipped"].includes(String(status || ""));
|
||||
}
|
||||
|
||||
function isPlainNumeric(value: string): boolean {
|
||||
return /^[0-9]+$/.test(value);
|
||||
}
|
||||
|
||||
export function scheduleRunsScope(args: string[]): { scheduleId: string | null; limit: number } {
|
||||
const limit = numberOption(args, "--limit", 50);
|
||||
let scheduleId: string | null = null;
|
||||
for (let index = 1; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
if (arg === "--limit") {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("--")) {
|
||||
throw new Error(`unsupported schedule runs option: ${arg}`);
|
||||
}
|
||||
if (scheduleId !== null) {
|
||||
throw new Error("schedule runs accepts at most one schedule id");
|
||||
}
|
||||
if (isPlainNumeric(arg)) {
|
||||
throw new Error("schedule runs <scheduleId> requires a non-numeric schedule id; use schedule runs --limit N for the global history view");
|
||||
}
|
||||
scheduleId = arg;
|
||||
}
|
||||
return {
|
||||
scheduleId,
|
||||
limit,
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForScheduleRun(scheduleId: string, runId: string, timeoutMs: number): Promise<unknown> {
|
||||
const started = Date.now();
|
||||
let latest: unknown = null;
|
||||
const observeCommand = `bun scripts/cli.ts schedule runs ${scheduleId} --limit 20`;
|
||||
while (Date.now() - started < timeoutMs) {
|
||||
latest = coreInternalFetch(`/api/schedules/${encodeURIComponent(scheduleId)}/runs?limit=20`);
|
||||
const runs = responseBody(latest).runs;
|
||||
@@ -47,7 +78,33 @@ async function waitForScheduleRun(scheduleId: string, runId: string, timeoutMs:
|
||||
}
|
||||
await Bun.sleep(2000);
|
||||
}
|
||||
return { ok: false, timeoutMs, latest };
|
||||
return { ok: false, timedOut: true, timeoutMs, runId, scheduleId, observeCommand, latest };
|
||||
}
|
||||
|
||||
export function scheduleRunObservation(scheduleId: string, response: unknown, wait: unknown): Record<string, unknown> {
|
||||
const run = responseBody(response).run as { id?: unknown } | undefined;
|
||||
const runId = typeof run?.id === "string" ? run.id : null;
|
||||
return {
|
||||
trigger: response,
|
||||
originalRunId: run?.id ?? null,
|
||||
scheduleId,
|
||||
newRunId: runId,
|
||||
observeCommand: `bun scripts/cli.ts schedule runs ${scheduleId} --limit 20`,
|
||||
wait,
|
||||
};
|
||||
}
|
||||
|
||||
export function scheduleRetryRunObservation(originalRunId: string, response: unknown): Record<string, unknown> {
|
||||
const body = responseBody(response);
|
||||
const scheduleId = typeof body.scheduleId === "string" ? body.scheduleId : null;
|
||||
const newRunId = typeof body.newRunId === "string" ? body.newRunId : null;
|
||||
return {
|
||||
originalRunId: String(body.originalRunId ?? originalRunId),
|
||||
scheduleId,
|
||||
newRunId,
|
||||
observeCommand: scheduleId !== null && newRunId !== null ? `bun scripts/cli.ts schedule runs ${scheduleId} --limit 20` : null,
|
||||
response,
|
||||
};
|
||||
}
|
||||
|
||||
function pgdataBackupScheduleBody(config: UniDeskConfig, args: string[]): Record<string, unknown> {
|
||||
@@ -84,9 +141,9 @@ export async function runScheduleCommand(config: UniDeskConfig, args: string[]):
|
||||
return coreInternalFetch(`/api/schedules/${encodeURIComponent(idArg)}`);
|
||||
}
|
||||
if (action === "runs") {
|
||||
const limit = numberOption(args, "--limit", 50);
|
||||
return idArg
|
||||
? coreInternalFetch(`/api/schedules/${encodeURIComponent(idArg)}/runs?limit=${limit}`)
|
||||
const { scheduleId, limit } = scheduleRunsScope(args);
|
||||
return scheduleId
|
||||
? coreInternalFetch(`/api/schedules/${encodeURIComponent(scheduleId)}/runs?limit=${limit}`)
|
||||
: coreInternalFetch(`/api/schedules/runs?limit=${limit}`);
|
||||
}
|
||||
if (action === "delete") {
|
||||
@@ -99,12 +156,17 @@ export async function runScheduleCommand(config: UniDeskConfig, args: string[]):
|
||||
const run = responseBody(response).run as { id?: unknown } | undefined;
|
||||
const waitMs = numberOption(args, "--wait-ms", 0);
|
||||
const wait = waitMs > 0 && typeof run?.id === "string" ? await waitForScheduleRun(idArg, run.id, waitMs) : null;
|
||||
return { trigger: response, wait };
|
||||
return scheduleRunObservation(idArg, response, wait);
|
||||
}
|
||||
if (action === "retry-run") {
|
||||
if (!idArg) throw new Error("schedule retry-run requires failed run id");
|
||||
const response = coreInternalFetch(`/api/schedules/runs/${encodeURIComponent(idArg)}/retry`, { method: "POST", body: {} });
|
||||
return scheduleRetryRunObservation(idArg, response);
|
||||
}
|
||||
if (action === "upsert-pgdata-backup" || action === "pgdata-backup") {
|
||||
const body = pgdataBackupScheduleBody(config, args);
|
||||
const id = String(body.id || "unidesk-pgdata-baidu-daily");
|
||||
return coreInternalFetch(`/api/schedules/${encodeURIComponent(id)}`, { method: "PUT", body });
|
||||
}
|
||||
throw new Error("schedule command must be one of: list, get, runs, run, delete, upsert-pgdata-backup");
|
||||
throw new Error("schedule command must be one of: list, get, runs, run, retry-run, delete, upsert-pgdata-backup");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user