Delivery lifecycle
Normal receivers produce one delivery per event and consumer group. Batch receivers group up tobatchSize ordered events into one delivery:
- Its queue or terminal state.
- The current attempt number and retry deadline.
- The active lock owner, token, and expiration.
- The terminal outcome and error when present.
- A bounded window containing the latest ten completed attempts.
Delivery guarantees
At-least-once
This is the default. If a process disappears before acknowledging an attempt, another replica can recover it after the lease expires. A handler may therefore run more than once.At-most-once attempt
Withdelivery: 'at-most-once', Pulse does not retry after a handler starts. This reduces duplicate
execution but can lose processing if the worker disappears mid-handler.
Neither mode can make external side effects exactly-once. Use event.id as an idempotency key.
Leases, heartbeats, and fencing
An active attempt stores a randomlockToken and lockedUntil. Pulse renews the lease about every
third of lockTimeoutMs while the callback runs.
Every completion update matches both the delivery ID and lock token. If the lease was reaped and a
new worker owns the delivery, a stale worker cannot acknowledge or reschedule it. Pulse reports
PulseLockLostError instead.
Choose lockTimeoutMs comfortably above ordinary MongoDB latency. Heartbeats protect handlers that
run longer than the timeout; they do not require handlers to finish within it.
Crash recovery
Only the discovery leader for a topic performs maintenance:- Scan a bounded indexed batch of expired delivery locks.
- Commit the expired attempt as an error.
- Schedule the next retry or mark the delivery terminal.
- Periodically delete successful deliveries whose events are behind the persisted cursor.
Discovery cursors
New events receive a MongoDB BSON timestamp through$currentDate. Pulse uses this server-assigned
sequence as its durable discovery order, so publishers with skewed application clocks cannot leave
events behind an advanced cursor.
The discovery leader advances a normal receiver cursor only after materializing its deliveries. For
a batch receiver, inserting the multi-event delivery and advancing the cursor to the last event are
committed in one MongoDB transaction. A crash therefore commits both changes or neither, preventing
overlapping batches and skipped events.
Failure matrix
Idempotent handlers
Use the event ID as the unique key for durable side effects:event.id as their idempotency key when supported. If a workflow spans
multiple writes, use a local transaction or an outbox/inbox design.
Retry policy
The delay after failed attemptn is:
maxRetries counts retries after the first attempt. Pulse retains the latest ten completed attempt
outcomes inside the delivery document until that delivery expires.
Retention
Set either value to
null to disable that TTL. MongoDB TTL deletion is asynchronous.
The discovery leader also deletes successful deliveries in bounded batches, but only after its
persisted cursor has passed the event. When historyRetentionMs is configured, this cleanup requires
expiresAt so it cannot bypass retention. With historyRetentionMs: null, the cursor alone is
sufficient.
Operational guidance
- Keep all replicas of one consumer group on matching persisted settings.
- Increase
configVersionwhenever those settings change. - Make handlers idempotent before increasing concurrency.
- Monitor terminal errors, retry age, expired locks, and delivery backlog.
- Keep event retention longer than the maximum expected outage or retry window.
- Use a new consumer group when an independent replay of retained events is required.