feat(dn-batches): one batch per tick by default
A tick is fully sequential: one vision call per page, then each note's checks and promotion in turn. Claiming three batches of a 13-page scan can approach the route's 300s ceiling, and a tick that times out leaves its batch to be reclaimed and re-read, paying for those pages twice. One batch per tick also makes n8n's execution log directly readable — one run, one document, one outcome. DN_TICK_BATCH_SIZE still overrides it once the page-count distribution is known. 34 batches at a 1-minute schedule is ~34 minutes rather than ~12. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d4ac4bd6e4
commit
d02feee57f
4 changed files with 21 additions and 6 deletions
|
|
@ -48,7 +48,7 @@ MEGA_CUSTOMER_VAT_NO=4280307796
|
|||
|
||||
# How many dn_batches rows the /api/dn-batches/tick route claims per
|
||||
# cron hit. Tune for your batch size / model latency.
|
||||
DN_TICK_BATCH_SIZE=3
|
||||
DN_TICK_BATCH_SIZE=1
|
||||
|
||||
# Public URL of the n8n instance (used by your local Codex MCP config).
|
||||
N8N_MCP_URL=https://wfdash.n8n.apphub.co.za/mcp-server/http
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ and the Coolify environment:
|
|||
| Variable | Value | Used by |
|
||||
| --- | --- | --- |
|
||||
| `MEGA_CUSTOMER_VAT_NO` | `4280307796` | `checks.ts` → `customer_identity` |
|
||||
| `DN_TICK_BATCH_SIZE` | `3` | `/api/dn-batches/tick` claim limit |
|
||||
| `DN_TICK_BATCH_SIZE` | `1` | `/api/dn-batches/tick` claim limit. One batch per tick: a tick is fully sequential, so claiming several risks the 300s ceiling on a long document and pays for re-read pages. |
|
||||
|
||||
`MINIMAX_API_KEY`, `MINIMAX_BASE_URL` and `MINIMAX_MODEL` already exist and
|
||||
are reused by `extract.ts`.
|
||||
|
|
|
|||
|
|
@ -320,7 +320,10 @@ describe("POST /api/dn-batches/tick — happy path", () => {
|
|||
expect(body.results.map((r: { id: string }) => r.id)).toEqual(["a1", "a2"]);
|
||||
});
|
||||
|
||||
it("defaults DN_TICK_BATCH_SIZE to 3 when env is unset", async () => {
|
||||
// One batch per tick by default. A tick is fully sequential — one
|
||||
// vision call per page — so claiming several risks the route's 300s
|
||||
// ceiling on a long document, and a timeout re-reads those pages.
|
||||
it("defaults DN_TICK_BATCH_SIZE to 1 when env is unset", async () => {
|
||||
vi.stubEnv("DN_TICK_BATCH_SIZE", "");
|
||||
dnBatchesGetFullList.mockImplementation(
|
||||
async (opts: { filter?: string } = {}) => {
|
||||
|
|
@ -338,8 +341,8 @@ describe("POST /api/dn-batches/tick — happy path", () => {
|
|||
|
||||
const res = await POST(postWithValidAuth());
|
||||
const body = await res.json();
|
||||
expect(body.claimed).toBe(3);
|
||||
expect(runBatchMock).toHaveBeenCalledTimes(3);
|
||||
expect(body.claimed).toBe(1);
|
||||
expect(runBatchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does nothing when there are no inbox candidates", async () => {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,19 @@ export async function POST(request: Request) {
|
|||
}
|
||||
|
||||
const pb = await pbAdmin();
|
||||
const limit = Number(process.env.DN_TICK_BATCH_SIZE || 3);
|
||||
// One batch per tick by default.
|
||||
//
|
||||
// Everything inside a tick is sequential — one vision call per page,
|
||||
// then each note's checks and promotion in turn — so a tick's runtime
|
||||
// scales with the pages it claims. Three batches of a 13-page scan can
|
||||
// approach the 300s ceiling above, and a tick that times out mid-way
|
||||
// leaves its batch to be reclaimed and re-read, paying for those pages
|
||||
// twice.
|
||||
//
|
||||
// One batch per tick also makes n8n's execution log directly readable:
|
||||
// one run, one document, one outcome. Raise DN_TICK_BATCH_SIZE once
|
||||
// the page-count distribution is known and the ceiling is not a risk.
|
||||
const limit = Number(process.env.DN_TICK_BATCH_SIZE || 1);
|
||||
const results: Record<string, unknown>[] = [];
|
||||
|
||||
await reclaimStale(pb);
|
||||
|
|
|
|||
Loading…
Reference in a new issue