This page describes the three pull-inbox libraries: Go, TypeScript and C#. Each library claims the rows of one consumption: pull source, runs your handler in a transaction, and completes, retries or dead-letters the row.
The README of each library holds the full text:
Go,
TypeScript and
C#.
Consume the inbox shows a worker from start to end.
One transaction holds the claim and nothing else, and it commits before a handler starts.
Handler transaction
The handler receives an open transaction. The library runs the completion in that transaction, then commits.
Lost lease
A completion that updates no row means that another worker owns the row. The library rolls the handler writes back.
Renewal
The library renews the lease every third of the lease duration.
Failure
A handler error rolls the transaction back. The retry policy then retries the row later or dead-letters it.
Shutdown
A stop ends the claims at once. A handler that runs past the grace period is cancelled. The library completes nothing for it and spends no attempt, and the lease expires.
Redaction
Every log line and every last_error value passes through the redaction that QueueBox uses.
Schema
The library writes no schema. It needs the V10 schema or later, which adds the inbox headers column.
Claim token
The handler never sees the token.
Rules for a handler, in every language:
Write every change through the transaction that the handler receives.
Do not commit and do not roll back. The library owns both.
Fail the row with an error or an exception.
Honour the cancellation. It fires when the lease is lost and when a shutdown runs out of grace.
Deduplicate external work on the source and the idempotency key. A transaction cannot roll back a call to another system.
5 attempts, 1 second base, 5 minute ceiling, 0.2 jitter.
On SQL Server, keep the Microsoft.Data.SqlClient command timeout at 30 seconds or more. The default is 30 seconds.
Entity Framework Core opens its own connection by default. Build the context through InboxDbContextFactory.CreateOn, so the application write and the completion share one transaction.
The default policy of each library retries while attempt is below the ceiling of 5, and dead-letters after that.
The delay is the base delay times 2^attempt, at most the ceiling delay, with a random jitter of 20 percent.
The row starts at attempt 0, so the default allows six deliveries.
Write your own policy when an error must never retry, for example a payload that does not parse.
Claims up to batch rows of the source, one per aggregate. Returns the rows with their claim token.
renew.sql
:id, :token, :lease_ms
Extends the lease.
complete.sql
:id, :token
Marks the row processed. Run it in the transaction of the business writes.
retry.sql
:id, :token, :delay_ms, :error
Returns the row to pending after the delay, and raises attempt by one.
dead.sql
:id, :token, :error
Marks the row dead.
Bind every value as a parameter. Do not build the SQL text from values.
Compute cand_limit as LEAST(GREATEST(3 * batch, 50), 500), and bind it on every call.
Renew, complete, retry and dead-letter must each update exactly one row. Zero rows means that the claim was lost. Stop the work.
Remove secrets from error and cut its length before you bind it.
On SQL Server, the claim takes an application lock per source with a 10 second lock timeout.
A lock failure raises Msg 51000. Treat it as a transient error, and wait before the next claim.
Set the driver timeout to at least 30 seconds, so the server raises Msg 51000 before the driver aborts the call.
An aborted call leaves the lock held until the connection resets.
A SQL Server claim needs READ COMMITTED isolation. It also works with READ_COMMITTED_SNAPSHOT ON.