mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 00:48:18 +00:00
gbrain jobs prune --dry-run used to silently drop the flag and run the destructive default — rows were really deleted while the operator believed they were previewing. MinionQueue.prune now takes dryRun: count the would-be-pruned rows without deleting; the CLI parses --dry-run and labels the output. Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Garry Tan
Claude Opus 5
parent
784358f5fd
commit
bd049d2969
+10
-3
@@ -233,7 +233,7 @@ USAGE
|
||||
gbrain jobs get <id>
|
||||
gbrain jobs cancel <id>
|
||||
gbrain jobs retry <id>
|
||||
gbrain jobs prune [--older-than 30d]
|
||||
gbrain jobs prune [--older-than 30d] [--dry-run]
|
||||
gbrain jobs delete <id>
|
||||
gbrain jobs stats
|
||||
gbrain jobs smoke
|
||||
@@ -633,8 +633,15 @@ HANDLER TYPES (built in)
|
||||
try { await queue.ensureSchema(); }
|
||||
catch (e) { console.error(e instanceof Error ? e.message : String(e)); process.exit(1); }
|
||||
|
||||
const count = await queue.prune({ olderThan: new Date(Date.now() - days * 86400000) });
|
||||
console.log(`Pruned ${count} jobs older than ${days} days.`);
|
||||
// #2712: --dry-run previews the count without deleting. It used to be
|
||||
// silently ignored (the destructive default ran anyway).
|
||||
const dryRun = hasFlag(args, '--dry-run');
|
||||
const count = await queue.prune({ olderThan: new Date(Date.now() - days * 86400000), dryRun });
|
||||
if (dryRun) {
|
||||
console.log(`[dry-run] Would prune ${count} jobs older than ${days} days. Nothing deleted.`);
|
||||
} else {
|
||||
console.log(`Pruned ${count} jobs older than ${days} days.`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -534,10 +534,21 @@ export class MinionQueue {
|
||||
}
|
||||
|
||||
/** Prune old jobs in terminal statuses. Returns count of deleted rows. */
|
||||
async prune(opts?: { olderThan?: Date; status?: MinionJobStatus[] }): Promise<number> {
|
||||
async prune(opts?: { olderThan?: Date; status?: MinionJobStatus[]; dryRun?: boolean }): Promise<number> {
|
||||
const statuses = opts?.status ?? ['completed', 'dead', 'cancelled'];
|
||||
const olderThan = opts?.olderThan ?? new Date(Date.now() - 30 * 86400000);
|
||||
|
||||
// #2712: dryRun counts the would-be-pruned rows without deleting.
|
||||
// Silent-ignoring a safety flag on a delete path is data loss.
|
||||
if (opts?.dryRun) {
|
||||
const rows = await this.engine.executeRaw<{ count: string }>(
|
||||
`SELECT count(*)::text as count FROM minion_jobs
|
||||
WHERE status = ANY($1) AND updated_at < $2`,
|
||||
[statuses, olderThan.toISOString()]
|
||||
);
|
||||
return parseInt(rows[0]?.count ?? '0', 10);
|
||||
}
|
||||
|
||||
const rows = await this.engine.executeRaw<{ count: string }>(
|
||||
`WITH pruned AS (
|
||||
DELETE FROM minion_jobs
|
||||
|
||||
@@ -709,6 +709,26 @@ describe('MinionQueue: Prune', () => {
|
||||
const count = await queue.prune({ olderThan: new Date(Date.now() + 86400000) }); // future date = prune everything old enough
|
||||
expect(count).toBe(1); // only the cancelled one
|
||||
});
|
||||
|
||||
// #2712: --dry-run used to be silently ignored — the destructive default
|
||||
// ran and deleted rows while the operator believed they were previewing.
|
||||
test('dryRun counts prunable jobs without deleting', async () => {
|
||||
const job1 = await queue.add('sync', {});
|
||||
await queue.cancelJob(job1.id); // terminal → prunable
|
||||
|
||||
const wouldPrune = await queue.prune({ olderThan: new Date(Date.now() + 86400000), dryRun: true });
|
||||
expect(wouldPrune).toBe(1);
|
||||
|
||||
// The row must still exist after a dry run.
|
||||
const stillThere = await queue.getJob(job1.id);
|
||||
expect(stillThere).not.toBeNull();
|
||||
expect(stillThere!.status).toBe('cancelled');
|
||||
|
||||
// A real prune afterwards actually deletes it.
|
||||
const pruned = await queue.prune({ olderThan: new Date(Date.now() + 86400000) });
|
||||
expect(pruned).toBe(1);
|
||||
expect(await queue.getJob(job1.id)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// --- Stats (1 test) ---
|
||||
|
||||
Reference in New Issue
Block a user