How queues are received
This is the detail behind Queues: when a message is
acknowledged, what is parked, how messages are read and fetched, and how
a package provides receiving. The Cloudflare Queues driver is under
The Cloudflare Queues driver.
- When a message is acknowledged. Once the transaction keeping its
event commits: in one read-committed transaction in the events' store,
its id is recorded under
queue <connection>injadpo_received_eventsand its outbox row and a delivery for each subscriber are written, as a webhook's are; then the message is deleted from the queue. Its subscribers run from the outbox with their usual retries, and one that gives up is parked as any delivery is, so "after the subscriber's transaction commits" is read as the transaction that hands the event to them; tying the message to each subscriber's run would keep a second retry budget beside the outbox's. A repeat, its id recorded already, is deleted and keeps nothing. Ids are deleted by the tick after 30 days, as a webhook's are. - Parked, never dropped. A message whose type the queue doesn't list
(
unlisted_type), one that can't be read as its event (unreadable: not JSON, no id or type where the queue says, a field its event refuses, each problem kept by field and rule, never a value) and one over 64 KB as its event (too_large) are kept whole injadpo_parked_messages, once per queue and body, and deleted from the queue.jadpo deliverieslists each asfailed(queue warehouse_queue · OrderShipped, its reason as the code);retrymakes itpendingand the app's next tick reads it again with the build running then, keeping it or parking it again;dismisssets it aside, deleted by the tick 30 days later. A message the store can't take now is left on the queue, which sends it again, and the queue's own dead-letter queue takes one that keeps failing, which on Cloudflare Queues the Worker parks; one whose event the store refuses as a statement, as it would every time, is parkedunreadable. - Reading a message. Its body is JSON; its id is the text or whole
number at
id_at:, its type the text attype_at:, matched against each listed event'swire_name:; a queue of one kind has no type. The event is read from the body's root, every member it doesn't declare left out, the queue's own id and type among them, as a webhook's are. - Naming the events.
events:names each event as code does: the app's own imported on auseline inapp.jadpo(use stock/events { OrderShipped }), or a package's public one asPackage.Event. Withtype_at:each gives a textwire_name:, no two alike; without it the queue lists one event. Anything else isreceiving.invalid, and with no store to keep the events in,event.store_required. - When it is received. On the tick: each queue is asked for up to ten messages at a time, again while a batch comes back full, at most ten times a tick, within the tick's budget; up to 20 parked messages an operator retried are read first. An app that receives a queue ticks at least every 10 seconds. On Workers the queue's consumer is handed batches instead ("The Cloudflare Queues driver").
- How a package provides it.
provides/receiving.jadpoholdsprovide receivingwithreceive:(call:,input:,messages: response.<field>,body: item.<field>,handle: item.<field>) andacknowledge:(call:,input:withmessage.handle). Each input is keyed by the operation's input field and givensettings.<name>, a setting the package declares, or text, a whole number or a flag; every required field is mapped, the list is a list of objects, and the body and the handle are text. An operation whose answer the package's owndecode:reads can't be mapped. Problems arepackage.provider_invalidin the mapping's file. An installed package's resources a mapping calls are loaded with it. - AWS's JSON protocol.
Protocol.aws_jsonsends every call as aPOSTwithContent-Type: application/x-amz-json-1.0, its body the input ({}without one), naming the operation inX-Amz-Target: <target>.<Operation>, its name in UpperCamelCase (receive_messageisReceiveMessage). A refusal's code aterror_code:is AWS's error type, read after its#in lower_snake_case (com.amazonaws.sqs#QueueDoesNotExistisqueue_does_not_exist). The provider keyaws: { service: target: }names the service requests are signed for and the operations' prefix;aws_jsonneedstarget:andaws_sigv4needsservice:(package.provider_invalid). - AWS Signature Version 4.
CredentialScheme.aws_sigv4signs every header butuser-agent, withHostandX-Amz-Date, over the body's SHA-256, as AWS's published test suite and worked example sign, which its tests check. The connection givescredential: { key_id: config.<name> secret: config.<name> }, the secret a secret setting (connection.value_invalidotherwise). The region is read from an AWS host,<service>.<region>.amazonaws.com, and isus-east-1for any other, as AWS's own tools sign for a local emulator; host placeholders wait for a package with fixed regional hosts, so the SQS package's connection gives its endpoint withhost:. The secret appears in no header, problem or log line. - A member a provider leaves out. On a provider's wire, a field its
type may hold
nonein reads asnonewhen the provider leaves its member out, as SQS leavesMessagesout of an empty receive; the app's own contract still needs the member. - The SQS package,
AmazonSqs.v2012_11_05, is installed withjadpo package add. It mapsReceiveMessageandDeleteMessage, and marksReceiveMessagesafe to send again, since repeating it only hides other messages for a while. Its tests run against a local fake of SQS's JSON API that checks each signature.
Capability mappings other than
receiving's andtoolchain.not_built_yet.
Markdown for agents: /docs/how-queues-are-received.md.Built from Jadpo main at a707efb.