Skip to content

Carry a claim across continueAsNew

The queue as a marketSDKcontinueAsNewclaimGroupsclaimByFacets

A looping dispatcher keeps the orders it claimed but could not place yet, and carries them into its next run instead of releasing them.

The situation

A dispatcher runs in ticks. This tick it claimed five orders but found only three free cells. Releasing the other two would put them back behind newer work; holding them should give them first place next tick.

The recipe

workflows/dispatcher.ts
import { Durable } from '@hotmeshio/hotmesh';
import * as activities from '../activities/dispatch';
const { placeOrders, claimFreshOrders } = Durable.workflow.proxyActivities<typeof activities>({ activities });
export async function dispatcher(envelope: { data: { carried?: OrderGroup[] } }) {
const carried = envelope.data.carried ?? [];
// 1. Place the carried backlog first: already claimed and aging.
const first = carried.length ? await placeOrders(carried) : { unplaced: [] };
// 2. Claim fresh demand only once the backlog is placed.
const unplaced = [...first.unplaced];
if (unplaced.length === 0) {
const fresh = await claimFreshOrders();
unplaced.push(...(await placeOrders(fresh)).unplaced);
}
await Durable.workflow.sleep('30 seconds');
await Durable.workflow.continueAsNew({ data: { carried: unplaced } });
}

The unplaced groups ride the next run’s input. Claim them with a durationMinutes that outlasts a tick, so the lock is still live when the next run places them.

The guarantee

The claim lives on the rows, not in the dispatcher’s memory, so it survives the restart that continueAsNew performs. If the dispatcher stops for good, the windows lapse and the rows return to the pool. The durable workflow is the coordinator; there is no distributed transaction. See Faceted routing.