mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-12 06:10:44 +01:00
Merge decision paging repairs into retention stack
Co-Authored-By: Paperclip <noreply@paperclip.ing> * codex/sleep-13-grok-dispatch: fix(server): retain database precision in effect recovery cursor fix(server): preserve expiry paging timestamp precision
This commit is contained in:
commit
2c5babcabc
6 files changed
+66
-9
No files matched your search
@@ -223,6 +223,24 @@ const support = externalUrl ? { supported: true } : await getEmbeddedPostgresTes
|
||||
expect(await recovery.nextEffectRecoveryAt()).not.toBeNull();
|
||||
});
|
||||
|
||||
it("pages past invalid claims using database timestamp precision", async () => {
|
||||
vi.stubEnv("PAPERCLIP_DECISIONS_SWEEP_BATCH_SIZE", "1");
|
||||
const invalid = await create(), valid = await create();
|
||||
await claim(invalid.id); await claim(valid.id, new Date(Date.now() - 120_000));
|
||||
await db.execute(sql`update decisions set decided_by_user_id = null,
|
||||
updated_at = date_trunc('milliseconds', now() - interval '3 minutes') + interval '456 microseconds'
|
||||
where id = ${invalid.id}`);
|
||||
const recovery = service();
|
||||
expect(await recovery.recoverEffects()).toEqual({ resumed: 0 });
|
||||
expect(await recovery.recoverEffects()).toEqual({ resumed: 1 });
|
||||
expect(await comments()).toHaveLength(1);
|
||||
expect((await recovery.get(invalid.id))?.executionStatus).toBe("running");
|
||||
expect(await recovery.nextEffectRecoveryAt()).not.toBeNull();
|
||||
expect(await recovery.recoverEffects()).toEqual({ resumed: 0 });
|
||||
expect(await recovery.recoverEffects()).toEqual({ resumed: 0 });
|
||||
expect(await comments()).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("advances past malformed claims and retries them on a later pass", async () => {
|
||||
vi.stubEnv("PAPERCLIP_DECISIONS_SWEEP_BATCH_SIZE", "1");
|
||||
const invalid = await create(), valid = await create();
|
||||
|
||||
@@ -244,4 +244,24 @@ const support = externalUrl ? { supported: true } : await getEmbeddedPostgresTes
|
||||
await start(); await quiet(); expect(wake).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
|
||||
it("pages past a full failed batch using database timestamp precision", async () => {
|
||||
const seed = await create(); await db.delete(decisions).where(eq(decisions.id, seed.id));
|
||||
const ids = Array.from({ length: 101 }, (_, i) => `20000000-0000-4000-8000-${String(i).padStart(12, "0")}`);
|
||||
await db.insert(decisions).values(ids.map(id => ({ ...seed, id })));
|
||||
await db.execute(sql`update decisions set expires_at = date_trunc('milliseconds', now() - interval '1 minute') + interval '456 microseconds'`);
|
||||
await db.execute(sql`create function decision_expiry_fail() returns trigger language plpgsql as $$ begin
|
||||
if NEW.id <> '20000000-0000-4000-8000-000000000100' and NEW.status = 'expired' then raise exception 'expiry unavailable'; end if;
|
||||
return NEW; end $$`);
|
||||
await db.execute(sql`create trigger decision_expiry_fail before update on decisions for each row execute function decision_expiry_fail()`);
|
||||
const recovery = service();
|
||||
await expect(recovery.sweepTtlExpiry()).rejects.toThrow("Decision expiry failed");
|
||||
expect(await recovery.sweepTtlExpiry()).toEqual({ expired: 1 });
|
||||
expect((await service().get(ids.at(-1)!))?.status).toBe("expired");
|
||||
await db.execute(sql`drop trigger decision_expiry_fail on decisions`);
|
||||
expect(await recovery.sweepTtlExpiry()).toEqual({ expired: 100 });
|
||||
expect(await recovery.sweepTtlExpiry()).toEqual({ expired: 0 });
|
||||
expect(wake).toHaveBeenCalledTimes(101);
|
||||
});
|
||||
|
||||
});
|
||||
Reference in new issue
Block a user