Skip to content

Two-sided dispatcher

The queue as a marketSDKHTTPsearchByFacetsclaimGroupsclaimByFacetsresolveAllOrNone

Demand and supply are both rows. A dispatcher counts free supply, claims that much demand, locks the supply all-or-none, and pairs them.

The situation

Orders wait in one queue and work cells advertise themselves as ready in another. Matching them by hand does not scale, and matching them with a separate scheduler means a second source of truth.

The recipe

activities/dispatch.ts
// 1. Anticipate supply.
const free = await lt.escalations.searchByFacets({
role: 'cells', status: 'pending', available: true, facets: { state: 'ready' },
});
const capacity = free.data?.escalations.length ?? 0;
// 2. Claim that much demand, complete orders only, by priority.
const orders = await lt.escalations.claimGroups({
query: { role: 'orders', available: true }, limit: capacity, sizeFacet: 'orderSize',
});
const groups = orders.data?.groups ?? [];
// 3. Lock the supply for the claimed demand, all or none.
const cells = await lt.escalations.claimByFacets({
query: { role: 'cells', facets: { state: 'ready' } }, limit: groups.length, allOrNone: true,
});
// 4. Pair, hand off, and resolve both sides.

Each cell’s ready row is its advertisement; resolving it with a job hands the work over and wakes the cell’s own workflow.

The guarantee

Every step is a single statement over the shared escalation table: claims use FOR UPDATE SKIP LOCKED and group claims re-check completeness inside the lock. Several dispatchers can run side by side without double-claiming a row. Use resolveAllOrNone to deliver each cell its own job in one statement. See Faceted routing.