Skip to content

Early signals are buffered

The escalation boundarySDKsignalconditional

A payload sent before the workflow starts waiting is held and delivered when the wait registers. Order of arrival stops mattering.

The situation

An upstream step finishes a label for order ORD-9 before the packing workflow reaches the line that waits for it. The payload must not be lost because it arrived first.

The recipe

workflows/print-label.ts
import { Durable } from '@hotmeshio/hotmesh';
import * as activities from '../activities/labels';
const { renderLabel } = Durable.workflow.proxyActivities<typeof activities>({ activities });
export async function printLabel(envelope: { data: { orderId: string } }) {
const label = await renderLabel(envelope.data.orderId);
await Durable.workflow.signal(`label-${envelope.data.orderId}`, { labelUrl: label.url }, '24h');
return { type: 'return' as const, data: label };
}
workflows/pack-order.ts
import { conditional } from '@hotmeshio/long-tail';
export async function packOrder(envelope: { data: { orderId: string } }) {
const label = await conditional<{ labelUrl: string }>(`label-${envelope.data.orderId}`);
if (!label) return { type: 'return' as const, data: { packed: false } };
return { type: 'return' as const, data: { packed: true, labelUrl: label.labelUrl } };
}

Both sides agree on a deterministic signal id built from the order. The third argument to signal is how long a signal that arrives early is held.

The guarantee

A signal that reaches a wait before it registers is kept as a pending signal (10 minutes by default, longer with the expire argument) and delivered the moment the wait registers. Fan-out over many waits with Promise.all behaves the same way. See Conditional.