Cap jobs running at once
Say exports are heavy, and you want each customer workspace to run at most 3 at once. The 4th should wait or be refused, and a crashed export mustn’t keep its slot forever.
1. Create the limit
Section titled “1. Create the limit”curl -X POST https://api.limitry.com/v1/limits \ -H "Authorization: Bearer $LIMITRY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"Exports at once","subjectKind":"workspace","action":"export", "amount":3,"window":"concurrent"}'2. Take a slot for the job
Section titled “2. Take a slot for the job”hold takes a slot before the job starts and gives it back when the job
ends, whether it succeeds or throws:
import { LimitExceeded } from "@limitry/sdk";
try { await limits.hold( { subject: { kind: "workspace", id: workspaceId }, action: "export", ttlSeconds: 600, }, async (held) => { for (const chunk of chunks) { await exportChunk(chunk); await held.extend(); // still running: keep the slot another 600 s } }, );} catch (err) { if (err instanceof LimitExceeded) { // All 3 slots are taken. Try again in err.retryAfterSeconds. return requeue(job, err.retryAfterSeconds); } throw err;}3. If the worker dies
Section titled “3. If the worker dies”A slot is a reservation, and it lasts ttlSeconds. A job that’s still
running extends it as it goes. A worker that crashes stops extending,
and the slot frees itself within ttlSeconds. Pick a ttlSeconds
comfortably longer than the gap between your extend calls.
Combine with a daily limit
Section titled “Combine with a daily limit”A concurrency limit doesn’t count use. To also cap exports per day, add
a second limit on the same action with the window day. The hold
above then has to fit both.
Related: Rolling windows and concurrency, Reservations.