A work queue gives each job to one worker at a time. An expiring collection hides records after a set time. For worker design, see External workers.
Claim, work, complete
Inside your application module
import { expiringCollection, workQueue } from "@flower-js/sdk/temporal";
const jobs = workQueue("jobs", { maxLeaseMs: 30_000 });
const sessions = expiringCollection("sessions", {
expiration: { afterUpdateMs: 60_000 },
});
const tokens = expiringCollection("tokens", {
expiration: { afterCreationMs: 10_000 },
});- Add each helper’s
recordstodefine({collections}). Jobs are claimed oldest first. - Separate tenants with
workQueue("jobs", {maxLeaseMs: 30_000, scope: tenant}); inspect withqueue.scan(ctx). Scopes are not access control.
examples/workers.ts wraps enqueue, claim, complete, fail and retry in mutations. Deploy it to a fresh app:
Enqueue work using the supplied example
node sdk/cli.ts deploy examples/workers.ts
node sdk/cli.ts call jobs.enqueue \
'{"id":"receipt-1","payload":{"invoice":"inv-1"}}' \
--request-id enqueue-receipt-1worker.ts · checkout root
import { FlowerClient } from "@flower-js/sdk";
import type { Claim } from "@flower-js/sdk/temporal";
const client = new FlowerClient("http://127.0.0.1:7101");
const args = { owner: "worker-1", leaseMs: 10_000 };
const claimId = crypto.randomUUID(); // Persist this for retries of this call.
const { value: lease } = await client.call<typeof args, Claim | null>(
"jobs.claim", args, { requestId: claimId },
);
if (lease) {
// Perform external work here, using idempotency or downstream fencing.
// Preserve this completion ID and its exact arguments on uncertain replies.
const completionId = crypto.randomUUID();
await client.call("jobs.complete", {
id: lease.id, owner: lease.owner, token: lease.token,
...(lease.history ? { history: lease.history } : {}),
result: { processed: true },
}, { requestId: completionId });
}claimreturns{id, payload, owner, token, expiresAt, attempt, history?}, ornullwhen there is no work.completeandfailneed the current owner and token before expiry, or fail withLEASE_LOST. Expired work is claimable again at once.- Failed jobs stay until retried. Leases can’t be renewed.
- Retrying a claim with the same request ID returns the original, possibly expired, lease. Use a new ID for new work.
Expiry does not stop the old worker. The receiving service should take idempotency keys or check fencing tokens.
Let records expire
A record is hidden once ctx.now() >= expiresAt. sweep, called from a mutation or maintenance, deletes it. entry returns the value and its timestamps.
Policy passed to set | Meaning |
|---|---|
{ afterCreationMs: 10_000 } | Expires from its original live creation time. |
{ afterUpdateMs: 60_000 } | Restarts the lifetime on each update. |
{ at: epochMilliseconds } | Uses an absolute deadline chosen by your code. |
null | Does not expire. |
- Updates keep the creation time; writing over an expired record starts fresh.
- Reads never extend a lifetime; rewrite in a mutation to refresh. For logic on expiry, use a timer.
- Keep node clocks in sync.
ctx.now()is fixed per evaluation, and commit time counts against leases and lifetimes.