Skip to main content
This page describes how SQS (FIFO) maps to services in the environment where the application runs, including backend choices, supported operations and service-specific behavior.

Supported environments

  • Akamai
  • Azure
  • DigitalOcean
  • Google Cloud
  • OCI
  • Private Kubernetes
  • Scaleway
Check the operation and capability tables for the behavior your application depends on. A supported environment does not imply that every operation of the origin service is available.

How the targets compare

Each row compares a capability of SQS (FIFO) with its adaptation on each target. A dash means this profile does not state the capability for that target.

Cloud Adapter

On Akamai, Azure, DigitalOcean, OCI, Private Kubernetes, and Scaleway

PostgreSQL

PostgreSQL hosting

Tensor9 operates CloudNativePG inside the customer’s Kubernetes cluster. Queue data is stored on that cluster’s volumes and does not survive a full cluster rebuild. Plan recovery and message cutover before replacing the cluster. Tensor9 operates the queue schema and adapter. Queues start empty; existing SQS messages are not migrated. Drain each message group before cutover to preserve order.

Queue behavior

The adapter uses the same PostgreSQL tables, atomic message claims, stored receipt handles and transactional dead-letter moves described in PostgreSQL queue behavior. Receipt handles survive adapter restarts; stale claims return an error. Retention still removes messages in flight. All queues share database capacity, so a busy queue can affect others. The documented Standard-queue benchmark does not establish this host’s capacity or FIFO throughput. Duplicate sends are suppressed for five minutes; consumers must still handle redelivery.

On Azure

Azure Service Bus

How it works

Tensor9 adds an adapter, a proxy process, to your application pod and sets AWS_ENDPOINT_URL_SQS to its loopback address. Your application keeps its SendMessage and ReceiveMessage calls. The adapter translates them to Azure Service Bus operations and returns SQS responses, error codes and MD5 checksums. For a .fifo queue, Tensor9 provisions a Service Bus queue with sessions enabled. The adapter uses MessageGroupId as the session ID and selects session-based receiving from the queue’s .fifo suffix. Service Bus grants each session to one consumer at a time. The adapter delivers one message per group at a time, preserving send order across multiple consumer instances.
Before: on AWS the application's SQS SDK calls Amazon SQS FIFO. After: in the target cloud the same application and SDK call a Tensor9 adapter, which serves the SQS FIFO API from Azure Service Bus sessions.Before: on AWS the application's SQS SDK calls Amazon SQS FIFO. After: in the target cloud the same application and SDK call a Tensor9 adapter, which serves the SQS FIFO API from Azure Service Bus sessions.

The application keeps its SQS SDK. A Tensor9 adapter translates FIFO queue calls to Service Bus sessions.

Architecture

The Rust adapter translates SQS calls to Service Bus Standard over AMQP 1.0. On send, it sets the session ID from MessageGroupId. On receive, it delivers one message per group, renews the session lock during the visibility window and settles messages while preserving delivery counts. Authentication uses the customer’s AKS Workload Identity without a static secret. The adapter accepts Service Bus sessions independently of sending and standard-queue receiving, so a FIFO receive does not block those operations. The .fifo suffix selects this receive path; MessageGroupId supplies the session ID. The entity itself is provisioned at apply-time, session-enabled, before the first message arrives: sessions for the FIFO contract, broker-native duplicate detection over a 5-minute history window, a 5-minute session lock (giving the adapter’s lock renewal a 10x margin), expiry dead-lettering, and DLQ auto-forwarding. Microsoft operates Service Bus’s durability, backup, and HA; Tensor9 operates the adapter.
Architecture: the app's SQS SDK calls a Rust Tensor9 adapter over the pod loopback. The adapter serves the SQS FIFO API from an Azure Service Bus session-enabled queue whose session id is the MessageGroupId, giving strict per-group order with one message in flight per group across horizontally scaled consumers; FIFO session receives never block sends or standard receives.Architecture: the app's SQS SDK calls a Rust Tensor9 adapter over the pod loopback. The adapter serves the SQS FIFO API from an Azure Service Bus session-enabled queue whose session id is the MessageGroupId, giving strict per-group order with one message in flight per group across horizontally scaled consumers; FIFO session receives never block sends or standard receives.

A Rust adapter serves the SQS FIFO API in front of Service Bus; each session is one MessageGroupId , giving strict per-group order with one message in flight per group.

How sessions preserve group order

A Service Bus session groups messages under one ID and grants exclusive access to one consumer. While that consumer holds the session, other consumers cannot receive its messages. Messages are delivered in enqueue order; the adapter allows only one message in flight per group. Service Bus assigns each active group to one consumer even when several application pods receive from the same queue. Consumer pods therefore need no shared lock table, leader election or partition assignment to preserve group order.
  • One message in flight per group. A group’s head must be settled before its next message is delivered, the same one-in-flight contract as the memory and Postgres FIFO backends. This costs single-group throughput (one message per round-trip) in exchange for strict order.
  • The session lock keeps the group held. A held session is renewed by RenewSessionLock under your visibility window, so a slow consumer keeps its group rather than losing it mid-sequence; ChangeMessageVisibility extend maps to a lock renewal, and zero maps to an in-session immediate redelivery with order preserved.
  • Redelivery counts stay accurate. When the adapter releases a session, the in-flight message is abandoned first so its delivery count advances correctly, moving a poison message toward its redrive limit rather than being silently reset.
  • Receive counts reflect delivered messages. The adapter limits session receives to the remaining MaxNumberOfMessages allowance. It does not receive and discard extra messages that would increase their delivery count.
Sessions are the FIFO analog: each MessageGroupId is a broker session. The broker grants each session exclusively to one consumer, so two horizontally-scaled pods hold disjoint groups with one message in flight per group. Service Bus stores and coordinates session ownership; adapter instances do not share an in-memory lock table.Sessions are the FIFO analog: each MessageGroupId is a broker session. The broker grants each session exclusively to one consumer, so two horizontally-scaled pods hold disjoint groups with one message in flight per group. Service Bus stores and coordinates session ownership; adapter instances do not share an in-memory lock table.

Each MessageGroupId is a broker session; the broker hands each session to exactly one consumer, so ordering survives horizontal scale with no coordination between pods.

Duplicate detection

Service Bus duplicate detection uses a 5-minute history window. Sends with the same deduplication key within that window create one queued message. A consumer can still receive that message again after visibility expires. The broker stores the deduplication state; the adapter needs no separate reconciliation loop. An explicit MessageDeduplicationId takes precedence. Otherwise, the adapter uses a hash of the body for every .fifo send. It always sets this hash because its messaging permissions do not allow it to read the queue’s deduplication setting. With duplicate detection enabled, Service Bus uses the hash to suppress retries; with detection disabled, it is only an ID. The adapter separately stores the SQS MessageId as an application property and returns it on receive. Equal-body messages sent outside the deduplication window retain distinct SQS IDs.
Duplicate detection: on a .fifo send the adapter sets the dedup key from an explicit MessageDeduplicationId, else a content hash of the body. The queue is provisioned with broker-native duplicate detection over a 5-minute window, so a retried equal-body send is suppressed by the broker without a second enqueue; consumers can still receive a message again after visibility expires; the SQS MessageId is preserved as an application property.Duplicate detection: on a .fifo send the adapter sets the dedup key from an explicit MessageDeduplicationId, else a content hash of the body. The queue is provisioned with broker-native duplicate detection over a 5-minute window, so a retried equal-body send is suppressed by the broker without a second enqueue; consumers can still receive a message again after visibility expires; the SQS MessageId is preserved as an application property.

The adapter uses an explicit MessageDeduplicationId or a body hash as the Service Bus message ID. The broker suppresses repeated IDs within 5 minutes.

Dead-lettering

Service Bus dead-letters a message when its delivery count reaches the limit set by maxReceiveCount. The next message in that group can then be delivered. The adapter cannot receive from Service Bus’s internal dead-letter subqueue. Tensor9 therefore configures automatic forwarding to your declared dead-letter queue (DLQ), preserving the session ID. A FIFO source requires a FIFO DLQ, which is provisioned with sessions enabled. Messages whose time to live (TTL) expires are also dead-lettered, so they can be inspected or recovered from the DLQ. The pairing rule is enforced at compile time: a non-.fifo DLQ declared on a .fifo origin stops the build with a clear error rather than compiling a queue whose dead letters would have nowhere valid to land.
Dead-lettering: a poison message that fails delivery maxReceiveCount times is dead-lettered by the broker and its group advances; the dead letter auto-forwards to your declared, session-enabled DLQ, retaining its session id, and TTL-expired messages land there too.Dead-lettering: a poison message that fails delivery maxReceiveCount times is dead-lettered by the broker and its group advances; the dead letter auto-forwards to your declared, session-enabled DLQ, retaining its session id, and TTL-expired messages land there too.

Service Bus forwards messages exceeding maxReceiveCount to the declared DLQ, which the adapter can receive from.

Limitations

△ Where SQS FIFO and Service Bus diverge, read before you adopt
  • A dedup-suppressed send returns a fresh MessageId. SQS echoes the original message’s MessageId (and SequenceNumber) when it suppresses a duplicate; here the suppressed send returns a fresh MessageId that corresponds to no delivered message. The duplicate enqueue is still suppressed (consumer redelivery remains possible), but a producer that correlates on the duplicate’s echoed id must not wait on it.
  • Send responses omit SequenceNumber. Service Bus cannot return its sequence number synchronously on send. Receive responses include the broker-assigned number, which increases across the queue and therefore within each group.
  • The visibility timeout is capped at 5 minutes. A visibility timeout maps to the session lock, whose ceiling is the 5-minute session lock the compiler sets (a 10x margin over the adapter’s lock renewal), so a requested visibility above 5 minutes is rejected with an error rather than being silently clamped. Extension via ChangeMessageVisibility renews the lock.
  • Receipt handles expire on adapter restart. A handle requires a live session lock. After a restart, old handles are invalid and the group’s in-flight message is delivered again in order when its session lock expires, within 5 minutes.
  • Expiry can affect a whole session. SQS retention applies per message. On Service Bus, an expired message at the front can expire its entire session. The expired messages are dead-lettered and can be recovered from the DLQ, but more messages may expire together than on SQS.
  • Single-group throughput is one message per round-trip. The one-in-flight strictness that guarantees per-group order costs single-group throughput: one message per round-trip per group, versus up to 10 per receive batch on SQS. Total throughput scales with the number of concurrent groups, so parallelism comes from having many groups, not from batching within one.
  • An empty-queue receive can block past the requested wait. SQS returns within WaitTimeSeconds . A Service Bus session accept is broker-controlled and is never cancelled mid-attach (a cancelled accept could strand a broker-side session lock), so a FIFO receive on an empty queue can overrun the requested wait by up to one accept window, about 60 seconds worst case (the exact figure on real Azure is reported by your own monitoring once this is running).
  • Per-message DelaySeconds is rejected. A per-message delay is rejected exactly as real SQS rejects it on a FIFO queue: a scheduled enqueue would activate after later siblings and break in-group order. Queue-level policy changes have a separate scope: Cloud Adapter manages logical queue settings, but the messaging backend rejects nonzero queue delay and new dead-letter routes.
  • Changing FIFO or deduplication settings replaces the queue. Sessions and duplicate detection are fixed at queue creation. Changing standard/FIFO mode or content-based deduplication destroys and recreates the queue, losing queued messages. Every FIFO compilation includes a warning about replacement. Drain the queue before changing these settings.
  • A send without a deduplication ID can be accepted when SQS would reject it. SQS requires MessageDeduplicationId when content-based deduplication is disabled. The adapter cannot read that queue setting with its messaging permissions, so it accepts the send. Group ordering is preserved, but the SQS validation error is not reproduced.

Other considerations

  • Data migration. The Service Bus namespace and session-enabled queue are provisioned empty at apply-time; in-flight SQS messages are not migrated. Cut over at a drain point (or dual-write) so the new queue starts clean; for FIFO, drain per group so no group cuts over mid-sequence.
  • Queue configuration is applied during provisioning. Each queue gets a 5-minute session lock, ten times the adapter’s renewal interval. The declared maxReceiveCount determines dead-letter forwarding to the DLQ. Runtime message operations require no queue-management credential.
  • Operations and ownership. The adapter provisions the Service Bus deployment itself: the namespace and the session-enabled queue (with duplicate detection, a 5-minute lock, expiry dead-lettering, and DLQ auto-forwarding), all derived from your SQS queues’ declared shapes; the adapter is injected alongside the application. Microsoft operates Service Bus (Standard): durability, backup, and HA. What remains operational is the surrounding platform: the Azure subscription, cluster patching, and monitoring.
  • FIFO requires an explicit selection. Older deployments that used the unsplit SQS-to-Service-Bus target supported standard queues only. Tensor9 does not convert those deployments to FIFO. A FIFO queue must be declared and selected explicitly to use sessions.

Azure Cosmos DB (queue)

How it works

Tensor9 adds an adapter, a proxy process, to your application pod and sets AWS_ENDPOINT_URL_SQS to its loopback address. Your application keeps its SendMessage and ReceiveMessage calls. The adapter implements them using Cosmos DB and returns SQS responses, error codes and MD5 checksums. Each queue has a Cosmos DB container, with one document per message. The adapter uses these documents for sending, receiving, deletion, delay, visibility, dead-letter delivery and counts. FIFO queues add ordering by message group and duplicate detection, described below.
Before: on AWS the application's SQS SDK calls Amazon SQS. After: in the target cloud the same application and SDK call a Tensor9 adapter, which serves the SQS API from Azure Cosmos DB.Before: on AWS the application's SQS SDK calls Amazon SQS. After: in the target cloud the same application and SDK call a Tensor9 adapter, which serves the SQS API from Azure Cosmos DB.

The application keeps its SQS SDK. The adapter stores and reads messages in Azure Cosmos DB.

Architecture

The Rust adapter stores durable state in Cosmos DB, so instances can restart or scale out without sharing an in-memory lock table. It authenticates through the customer’s AKS Workload Identity without a static secret. Microsoft operates Cosmos DB’s durability, backups and high availability; Tensor9 operates the adapter. On the Cosmos side, the compiler provisions one serverless Cosmos account and one database per Cloud Adapter deployment , and inside it one container per declared queue (the container id is the queue name). Every message is a document that holds the SQS body, its attributes, its receive count, and a visibility timestamp. A Standard queue stores its messages durably and claims eligible messages in arrival order. Concurrent claims do not establish a fairness or no-starvation guarantee. FIFO queues order by MessageGroupId, with one message in flight per group, as described in the FIFO section. Retention rides Cosmos’s native per-item TTL, so an expired message is reclaimed by Cosmos itself.
  • Cosmos DB coordinates message claims. Adapter instances share message and claim records in the database, so an adapter restart does not erase them.
The adapter serves SQS requests using Azure Cosmos DB. One serverless account and database hold one container per declared queue. Cosmos stores messages and coordinates claims. FIFO queues keep strict per-group order with one message in flight per group.The adapter serves SQS requests using Azure Cosmos DB. One serverless account and database hold one container per declared queue. Cosmos stores messages and coordinates claims. FIFO queues keep strict per-group order with one message in flight per group.

A queue is one Cosmos container and each message is a document; a FIFO queue keeps strict per-group order with one message in flight per group.

Receiving messages

Receiving atomically claims the oldest visible message, hides it from other receivers and increments its receive count. If another receiver claims the candidate first, the adapter moves to the next one. Each successful conditional write has one owner; visibility expiry still permits redelivery. FIFO queues select the next sequence position within each message group and allow one message in flight per group. Messages can be redelivered after visibility expires. ApproximateReceiveCount is the real receive count (incremented on each claim, not estimated), and the SQS MD5-of-body and MD5-of-attributes are recomputed so your SDK verifies them byte-for-byte. FIFO rejects per-message DelaySeconds, including zero; omit the parameter. A SendMessageBatch runs as per-entry writes with faithful per-entry outcomes.
A conditional write claims an eligible message. A competing receiver that loses the claim tries another candidate. Visibility expiry can cause redelivery.A conditional write claims an eligible message. A competing receiver that loses the claim tries another candidate. Visibility expiry can cause redelivery.

A receive reads the oldest visible message and claims it for exactly one consumer; if another consumer claims it first, this receive moves to the next candidate. ApproximateReceiveCount is the real receive count, incremented on each claim, never estimated.

Receipt handles & visibility

SQS hands your consumer a receipt handle to delete or extend a message. Here that handle is a token for claim state stored in Cosmos DB. Because the adapter keeps no in-memory lock table, a current handle resolves the same way before and after a restart: DeleteMessage and ChangeMessageVisibility keep working. A stale handle (the message was already deleted, or a newer claim superseded this one) is a no-op on delete, exactly as SQS’s own stale handle is; a cross-queue or unparseable handle is rejected as ReceiptHandleIsInvalid. ChangeMessageVisibility updates the stored visibility time without a broker lock-duration maximum. Zero makes the message immediately available to claim again. Retention can expire a message while it is in flight; extending visibility does not extend its lifetime. A stale handle is rejected, allowing the consumer to detect that it no longer owns the claim. Visibility expiry does not stop a worker from continuing to process an earlier receive.
A receipt handle is plain data with no broker lock behind it, so it resolves the same after the adapter restarts. The visibility timeout has no ceiling: ChangeMessageVisibility extends it forward with no cap, where a broker-backed backend would stop at its lock ceiling.A receipt handle is plain data with no broker lock behind it, so it resolves the same after the adapter restarts. The visibility timeout has no ceiling: ChangeMessageVisibility extends it forward with no cap, where a broker-backed backend would stop at its lock ceiling.

Receipt handles remain usable after an adapter restart. Visibility is stored in Cosmos DB and has no broker lock-duration limit.

FIFO ordering & dedup

FIFO queues order messages by MessageGroupId and support duplicate detection. Standard queues have neither feature. Cosmos DB assigns each message a group position before enqueueing it. Concurrent senders therefore share one order without relying on their clocks. Each FIFO send returns that position as SequenceNumber. Sequence numbers increase but need not be consecutive; a gap does not stop the group. Receiving claims the next message from a group only if no message in that group is already in flight. The check and claim preserve this rule across concurrent receivers, including when a slow send arrives late. Different groups can be processed concurrently without coordination between consumers. When a claim times out, delivery resumes from the same message in its group. Within the 5-minute deduplication window, a repeated send is suppressed and returns the original MessageId and SequenceNumber, including after a crash during the send. The key is an explicit MessageDeduplicationId, or a hash of the body when content-based deduplication is enabled. A FIFO send with neither returns InvalidParameterValue. Standard queues reject MessageDeduplicationId because they do not support deduplication.
  • Sending and receiving use separate tracking. Sequence assignment does not block the group-claim mechanism used by receivers, so producers and consumers can proceed independently.
  • Clock skew can shorten visibility. The one-message-per-group claim check does not use a clock. Visibility checks do, so clock differences between consumers can cause early redelivery without allowing two active claims in the same group.
FIFO on Cosmos: each group is delivered in strict send order with exactly one message in flight per group, correct across horizontally scaled consumers; duplicate sends are suppressed and echo the original MessageId and SequenceNumber.FIFO on Cosmos: each group is delivered in strict send order with exactly one message in flight per group, correct across horizontally scaled consumers; duplicate sends are suppressed and echo the original MessageId and SequenceNumber.

Order within a group is strict send order; exactly one message is in flight per group; duplicate sends are suppressed and echo the original MessageId and SequenceNumber. This section applies to FIFO queues only.

Dead-letter queues

A message received more than maxReceiveCount times is moved to the declared dead-letter queue. If the adapter crashes during the move, retry ensures it reaches the DLQ before removal from the source. A retry can produce a duplicate in the DLQ; consumers there must handle at-least-once delivery. The adapter moves a message that exceeded its receive limit to the DLQ before claiming another message in the group. The moved message retains its group and order in the FIFO DLQ, with its receive count reset to 0. If a message expires under retention instead, it is removed and the next message becomes available in the group.
A failed message is copied to its dead-letter queue and then removed from the source. If the adapter crashes between those steps, retry completes the move; the dead-letter queue can contain a duplicate.A failed message is copied to its dead-letter queue and then removed from the source. If the adapter crashes between those steps, retry completes the move; the dead-letter queue can contain a duplicate.

The adapter retries interrupted dead-letter moves. Delivery to the DLQ is at-least-once, so a crash can cause a duplicate.

Limitations

△ Where SQS and Cosmos diverge, read before you adopt
  • Approximate counts can lag by about 5 seconds. The adapter caches counts briefly, so recent sends, settlements and expirations may not be reflected immediately. Use Azure monitoring to observe counts after deployment.
  • Dead-letter delivery can produce duplicates. Retry completes a move interrupted by an adapter crash without losing the message, but can deliver it to the DLQ more than once.
  • Queue capacity is bounded by the serverless container count. All of a Cloud Adapter deployment’s queues share one serverless Cosmos account, which caps at about 500 containers, so roughly 499 queues per Cloud Adapter deployment (one container per queue). Beyond that the build stops with a clear error rather than half-provisioning. The account is serverless by design, a fit for idle-heavy queue fleets.
  • Single region. The account is single-region (the Cloud Adapter deployment’s region); there is no geo-replica.
  • Clock skew can shorten FIFO visibility. The group claim check is independent of clocks, but visibility uses wall-clock time. Clock differences between consumers can cause early redelivery while preserving one active claim per group.
  • Cosmos DB removes expired messages. Per-item time to live (TTL) is enforced by background cleanup. Expiry can occur while a consumer is processing the message. Removing an expired first message allows the next message in its group to proceed.
  • Per-message delay is rejected. FIFO queues reject a supplied DelaySeconds, including zero. Omit it from individual messages.
  • Separate logical queue management from container provisioning. Cloud Adapter manages logical queues on provisioned containers, listings, tags and supported defaults, including retention. Terraform owns the account, database and containers; deleting a logical queue does not delete its container. Nonzero queue-level delay and new dead-letter routes are rejected.

Other considerations

  • Data migration. The Cosmos account, database, and per-queue containers are provisioned empty at apply-time. In-flight SQS messages are not migrated; drain each group before cutover so no group switches mid-sequence.
  • Operations and ownership. The adapter provisions the Cosmos deployment itself (one serverless account, one database per Cloud Adapter deployment, and one container per declared queue), all derived from your declared queues; the adapter is injected alongside the application. Microsoft operates Cosmos’s durability, backup, and HA; Tensor9 operates the adapter. What remains operational for the deployment is the surrounding platform: the Azure subscription, cluster patching, and monitoring.
  • Monitor database usage. Use Azure monitoring to track Cosmos DB request units and latency; queue counts are cached for about 5 seconds.
  • Retention is native, whole-second Cosmos TTL. A message’s remaining lifetime is Cosmos’s native per-item TTL set to the remaining whole-second budget (floored at one second, so Cosmos reclaims at or after the message is hidden, never before it), with no background cleanup of its own.

PostgreSQL Flexible Server

PostgreSQL hosting

Microsoft operates PostgreSQL Flexible Server, including zone-redundant HA and point-in-time recovery backups. The adapter uses short-lived Microsoft Entra tokens as database passwords. Queue data stays outside the customer’s Kubernetes cluster and survives its rebuild. One server and database hold all queues for the deployment, sharing capacity and maintenance; the default server size is fixed because SQS supplies no source instance size. Tensor9 operates the queue schema and adapter. Queues start empty; existing SQS messages are not migrated. Drain each message group before cutover to preserve order.

Queue behavior

The adapter uses the same PostgreSQL tables, atomic message claims, stored receipt handles and transactional dead-letter moves described in PostgreSQL queue behavior. Receipt handles survive adapter restarts; stale claims return an error. Retention still removes messages in flight. All queues share database capacity, so a busy queue can affect others. The documented Standard-queue benchmark does not establish this host’s capacity or FIFO throughput. Duplicate sends are suppressed for five minutes; consumers must still handle redelivery.

On Google Cloud

Cloud SQL for PostgreSQL

PostgreSQL hosting

Google operates Cloud SQL for PostgreSQL, including database backups and HA. The adapter uses short-lived Cloud SQL IAM tokens as database passwords, without an Auth Proxy or static password. Queue data stays outside the customer’s Kubernetes cluster and survives its rebuild. Tensor9 operates the queue schema and adapter. Queues start empty; existing SQS messages are not migrated. Drain each message group before cutover to preserve order.

PostgreSQL queue behavior

Tensor9 adds an adapter, a proxy process, to your application pod and sets AWS_ENDPOINT_URL_SQS to its loopback address. Your application keeps its SendMessage and ReceiveMessage calls. The adapter implements them using PostgreSQL and returns SQS responses, error codes and MD5 checksums. Each queue has a durable PostgreSQL table with one row per message. SQL operations implement sending, receiving, deletion, delay, visibility, dead-letter delivery and counts. FIFO queues add ordering by message group, per-group sequence numbers and duplicate detection. Consumer redelivery remains possible.
Before: on AWS the application's SQS SDK calls Amazon SQS. After: in the target cloud the same application and SDK call a Tensor9 adapter, which serves the SQS API from a durable Postgres table per queue.Before: on AWS the application's SQS SDK calls Amazon SQS. After: in the target cloud the same application and SDK call a Tensor9 adapter, which serves the SQS API from a durable Postgres table per queue.

The application keeps its SQS SDK. The adapter stores each queue in a PostgreSQL table.

Architecture

The Rust adapter stores all durable state in PostgreSQL, including messages, claims and receipt handles. Instances can restart or scale out without a shared in-memory lock table, receiver pool or renewal loop. On Cloud SQL and Azure Flexible Server, it authenticates with short-lived Cloud SQL IAM or Microsoft Entra tokens generated in-process and passed as database passwords, without a static secret. On the Postgres side, the compiler provisions one database per Cloud Adapter deployment, a catalog entry per queue, and one durable table per declared queue. Each message is a row holding the SQS body and its attributes, its message-group, a visibility deadline, a receipt handle, a receive count, and a sent-at timestamp; the table is indexed so the hot paths stay a single scan. A FIFO queue uses the message group as the ordering unit; each row’s arrival position supplies its per-group sequence number. A dead-letter queue is another queue table, and a background retention reaper physically removes rows past their retention. CloudNativePG, Cloud SQL for PostgreSQL and Azure Flexible Server use the same adapter code and tables. Their operational difference is where data is stored: the managed servers are outside the Cloud Adapter deployment cluster, while CloudNativePG is inside it. See the rebuild limitation below.
The adapter stores messages, receipt handles, receive counts and visibility deadlines in PostgreSQL. Each declared queue has a table; a dead-letter queue is another table. Background cleanup enforces retention. FIFO rows also store a message group and sequence number; receives claim one message per group.The adapter stores messages, receipt handles, receive counts and visibility deadlines in PostgreSQL. Each declared queue has a table; a dead-letter queue is another table. Background cleanup enforces retention. FIFO rows also store a message group and sequence number; receives claim one message per group.

Each queue uses a PostgreSQL table, including dead-letter queues. The same adapter code runs against CloudNativePG, Cloud SQL for PostgreSQL and Azure Flexible Server.

Receiving messages

In one SQL operation, the adapter selects messages whose visibility deadlines have passed, in arrival order and up to the requested batch size. It assigns each a fresh receipt handle, advances its visibility deadline, increments the receive count and returns the claimed rows. Rows locked by another receiver are skipped. FIFO queues claim the next eligible message in each group, keeping one in flight per group. Visibility expiry permits redelivery; it does not stop a worker that is still processing an earlier receive. ApproximateReceiveCount is the message’s real receive count (the claim increments it; it is not estimated), and the SQS MD5-of-body and MD5-of-attributes are recomputed so your SDK verifies them byte-for-byte. DelaySeconds stamps the message’s visibility deadline at send, so a delayed message remains unavailable until that deadline. A long poll is a poll loop bounded by WaitTimeSeconds (capped at 20s), returning the instant a claim succeeds. A SendMessageBatch is a single multi-row insert with faithful per-entry outcomes, and the message body is stored in the queue table. The adapter defaults to a 262144-byte (256 KiB) queue limit; managed SQS requests enforce the declared limit before sending. Native AWS SQS now supports messages up to 1 MiB.
A receive claims messages in one atomic step: the adapter takes the head-of-queue messages that are due (their visibility deadline has passed), in arrival order, and in the same step assigns a fresh receipt handle, pushes the visibility deadline into the future, and increments the receive count, returning the claimed messages. Each receiver atomically claims a different batch of eligible messages. Messages can be redelivered when their visibility expires.A receive claims messages in one atomic step: the adapter takes the head-of-queue messages that are due (their visibility deadline has passed), in arrival order, and in the same step assigns a fresh receipt handle, pushes the visibility deadline into the future, and increments the receive count, returning the claimed messages. Each receiver atomically claims a different batch of eligible messages. Messages can be redelivered when their visibility expires.

Concurrent receives use atomic row claims and skip rows locked by another receiver. ApproximateReceiveCount is the message’s real receive count.

Receipt handles & visibility

Receiving writes a receipt token to the message row. DeleteMessage and ChangeMessageVisibility find that row by its token, including after an adapter restart or through another adapter instance. No in-memory receiver or broker lock is required. A delete or visibility change with no matching row returns ReceiptHandleIsInvalid. This includes messages already deleted or claimed again under a newer token. Visibility is a timestamp on the row, defaulting to 30 seconds on receive. ChangeMessageVisibility can extend it without a broker lock-duration maximum; zero makes the message immediately available to claim again. Retention can delete a message while it is in flight, and a newer claim makes the old token stale.
A receipt handle is a durable token stored on the message's own row, a stable id stamped on it, with no in-memory broker lock behind it, so it resolves the same after an adapter restart: Delete and ChangeMessageVisibility find the message again by that token. The visibility timeout is a timestamp on the row with no ceiling: ChangeMessageVisibility pushes it forward with no cap, where a broker-backed backend would stop at its lock ceiling.A receipt handle is a durable token stored on the message's own row, a stable id stamped on it, with no in-memory broker lock behind it, so it resolves the same after an adapter restart: Delete and ChangeMessageVisibility find the message again by that token. The visibility timeout is a timestamp on the row with no ceiling: ChangeMessageVisibility pushes it forward with no cap, where a broker-backed backend would stop at its lock ceiling.

Receipt handles are stored with messages, so they remain usable after an adapter restart. Visibility deadlines have no broker lock-duration limit.

Dead-letter & retention

During receive, one database transaction moves messages at or past maxReceiveCount from the source table to the dead-letter table and claims the remaining candidates. The move and claim commit together, so an interrupted transaction cannot leave a message deleted from the source without its DLQ copy. The DLQ uses the same queue operations and retention cleanup. Moved messages start with a reset receive count. A background cleanup task deletes messages whose send time is older than the queue’s retention period, including messages in flight. Receiving does not change that timestamp. Cleanup reads the stored retention attribute, defaults to four days and clamps it to 60 seconds–14 days. A missing or invalidly short value therefore cannot delete newly sent messages immediately. One worker per database runs each cleanup pass in batches, backs off during database maintenance and isolates queue errors. A circuit breaker and per-batch time limit bound the work.
Dead-letter and retention. A single atomic operation both claims live messages and redrives poison ones: from the locked head-of-queue set, messages at or past maxReceiveCount are moved from the source into the dead-letter queue and the rest are claimed and returned, in one transaction, so the move and claims commit together. This does not prevent consumer redelivery after visibility expiry. Separately, a background retention reaper removes messages whose send time is older than the queue's retention, clamped between 60 seconds and 14 days.Dead-letter and retention. A single atomic operation both claims live messages and redrives poison ones: from the locked head-of-queue set, messages at or past maxReceiveCount are moved from the source into the dead-letter queue and the rest are claimed and returned, in one transaction, so the move and claims commit together. This does not prevent consumer redelivery after visibility expiry. Separately, a background retention reaper removes messages whose send time is older than the queue's retention, clamped between 60 seconds and 14 days.

One transaction moves failed messages to the DLQ and claims eligible messages. A background worker removes messages older than their retention period, clamped to 60 seconds-14 days.

FIFO ordering & dedup

A FIFO queue stores the group stamped on each message, which is the ordering unit. Order within a group is a head-of-group claim: the receive takes the earliest message of each group that has nothing in flight (it confirms no message in that group is currently claimed and still invisible) and claims those heads atomically. Because each message’s arrival position is a monotonic identity, the earliest-per-group is exactly the group’s next in-order message, and exactly one message is in flight per group at a time. Groups are independent, so different receivers drain different groups concurrently, with Postgres as the only coordinator. An expired claim returns the same head to the front of its group, so a redelivery stays in sequence. Each FIFO send requires MessageGroupId and returns a SequenceNumber: the PostgreSQL-assigned arrival position formatted as a 20-digit, zero-padded number. FIFO deduplication uses a separate PostgreSQL table keyed by queue and deduplication ID. An explicit MessageDeduplicationId takes precedence; otherwise a queue configured for content-based deduplication uses SHA-256 of the body, excluding attributes. The message insert and deduplication marker are part of one transaction. A duplicate within 5 minutes rolls back its new message row and returns the original MessageId and SequenceNumber. Marker retention is independent of message retention, so receiving or deleting the original does not shorten the deduplication window. A claim prevents another receive in that group until deletion or visibility expiry. It does not stop a timed-out worker from continuing to process a previously received message; consumers must make repeated processing safe.
FIFO receives atomically claim the next message from each group that has no active claim. Each send returns a 20-digit, zero-padded sequence number. Explicit or content-based deduplication uses a separate PostgreSQL table with a five-minute window; repeated sends return the original message and sequence IDs.FIFO receives atomically claim the next message from each group that has no active claim. Each send returns a 20-digit, zero-padded sequence number. Explicit or content-based deduplication uses a separate PostgreSQL table with a five-minute window; repeated sends return the original message and sequence IDs.

FIFO receives claim one message per group at a time. Sends return a SequenceNumber and support a 5-minute deduplication window, using an explicit ID or a body hash when content-based deduplication is enabled.

Limitations

△ Where SQS and this backend diverge, read before you adopt
  • Throughput is bounded by one PostgreSQL server. All of a Cloud Adapter deployment’s queues share one PostgreSQL server, so peak throughput is bounded by that one server, and a heavy queue can affect its neighbors. On the Tensor9 round-trip eval the Postgres adapter holds throughput parity with native SQS (about 300 vs 286 ops/s) and a tighter end-to-end tail (p99 140 ms vs SQS’s 172 ms), while SQS is faster at the median (p50 15 ms vs 24 ms), results from that moderate-rate Standard-queue workload, not a measured throughput ceiling. This benchmark does not measure FIFO throughput or every hosting option.
  • CloudNativePG data lives in-cluster and does not survive a cluster rebuild. The in-cluster CloudNativePG host keeps queue data inside the Cloud Adapter deployment cluster, so a full cluster rebuild loses it; the two managed hosts (Cloud SQL Postgres, Azure Flexible Server) keep queue data on a provider-operated server outside the cluster, so it survives a rebuild. All three hosts use the same queue implementation; database placement changes rebuild recovery.
  • FIFO send deduplication lasts 5 minutes. A duplicate send in that window returns the original IDs. Reusing an ID after the window can create a new message. Consumer redelivery remains possible even within the window.
  • Stale receipt handles return an error. DeleteMessage and ChangeMessageVisibility return ReceiptHandleIsInvalid when no row matches the token.
  • Retention cleanup runs periodically. An expired message can remain until the next cleanup pass. Retention is clamped to 60 seconds–14 days, with invalidly short values raised to 60 seconds to avoid deleting newly sent messages.
  • Approximate counts are a point-in-time exact count. ApproximateNumberOfMessages and ...NotVisible are a real count of the visible and not-yet-visible messages in the queue’s table, exact at query time, but still Approximate under concurrent traffic; the delayed-count is reported as zero.
  • Control-plane calls operate on the apply-time schema. The database, the catalog, and each per-queue table (and its dead-letter table) are created by the compiler’s provisioner; runtime CreateQueue / DeleteQueue / SetQueueAttributes operate on that provisioned schema, and a switch between standard and FIFO is refused as a replace rather than silently mutating semantics.

Other considerations

  • Plan message cutover. The database, catalog and queue tables start empty. Existing SQS messages are not migrated. Drain each group before switching it to preserve order.
  • Provisioning and ownership. The adapter provisions and owns its own schema atomically per queue (one database per Cloud Adapter deployment, a catalog, and one durable table per declared queue, plus its dead-letter table), so a crash never leaves a half-provisioned queue. Each queue is marked with an ownership stamp: the layer never touches a queue it doesn’t own (an unstamped or foreign-owned queue detaches rather than deletes), and it refuses to drop a non-empty queue without force.
  • Three hosts, one backend, keyless on the managed ones. The same backend supports CloudNativePG, Cloud SQL Postgres and Azure Flexible Server. On the two managed hosts the connection is keyless (an Entra or Cloud SQL IAM token obtained in-process and presented as the database password, no static secret), and the provider operates the database’s durability, backup, and HA while Tensor9 operates the adapter.

On Scaleway

Scaleway Managed Database for PostgreSQL

PostgreSQL hosting

Scaleway operates the managed PostgreSQL instance outside the customer’s Kubernetes cluster. Tensor9 operates the adapter and queue schema. Queue data survives a Kubernetes cluster rebuild; configure the managed database credentials for the adapter. Tensor9 operates the queue schema and adapter. Queues start empty; existing SQS messages are not migrated. Drain each message group before cutover to preserve order.

Queue behavior

The adapter uses the same PostgreSQL tables, atomic message claims, stored receipt handles and transactional dead-letter moves described in PostgreSQL queue behavior. Receipt handles survive adapter restarts; stale claims return an error. Retention still removes messages in flight. All queues share database capacity, so a busy queue can affect others. The documented Standard-queue benchmark does not establish this host’s capacity or FIFO throughput. Duplicate sends are suppressed for five minutes; consumers must still handle redelivery.

Service level agreements

These tables show Tensor9’s standard service levels and adapters whose standard terms are still being defined. Your signed agreement determines the SLAs, covered adapters and operations, limits, remedies and support terms that apply to your deployment.
Listed terms cover requests sent through this service adapter. They do not cover direct connections to the target service or replace the cloud provider’s own SLA. Each origin-to-target pair has its own terms or an explicit pending status. See the SLA tables, measurement rules, and scaling conditions.

SQS to Cosmos DB

What Tensor9 covers. The SLA covers permission checks, deciding whether to accept a request, time waiting inside the adapter, request handling, coordination, retries, internal storage and response handling. Waiting for the target service is excluded only when that wait is measured separately and the agreement allows the exclusion. Your agreement must name the covered deployment, operations, request limits and traffic limits. Unlisted operations are not automatically covered. Incorrect results are still bugs even when the availability target is met. How queue timing works. Time spent waiting for a message in an empty queue does not count as adapter delay. Adapter delays after a message becomes available, or after the requested wait ends, do count. The message-pickup target is not a deadline for clearing a backlog. Measure Standard and FIFO queues separately, and check each batch entry’s result. Empty receives and internal retries do not count as successfully delivered messages. Scaling limits. Tensor9 automatically adds adapter capacity within the traffic growth, burst size and infrastructure limits in your agreement. The standard service level does not promise a fixed request rate or unlimited scaling. Capacity depends on request sizes and types, how traffic is distributed across keys or message groups, target configuration and spare capacity for failures. More adapter processes cannot speed up every shared or sequential step. You remain responsible for target-service capacity; failures caused independently by the provider are outside this SLA. Read the details. Each linked service page lists the covered operations, request limits, measurement rules, data guarantees, scaling limits and examples. This summary does not add coverage beyond those terms. Your signed agreement determines the service levels that apply to your deployment.

Why these latency targets differ

The shared Standard/FIFO point-operation target is 20 ms, with 200 ms for a batch of up to ten sends. Cosmos FIFO uses a per-group sequence counter, a durable deduplication marker and an in-flight ownership slot. Their conditional reads/writes are adapter coordination; the clock cannot exclude them merely because they use Cosmos DB. The shared target preserves room for that work instead of advertising the cost of a simple native queue forwarder. Ready-message pickup and delay after the requested wait remain at 500 ms. Discovery, candidate inspection, conditional claims and polling can dominate this path even for a tiny message. The target is not an end-to-end promise for clearing a backlog, and Standard and FIFO observations must remain separate.

Adapter and scope

This is the queue-specific Cosmos DB profile, using its serverless account and per-queue container arrangement. It is not the provisioned Cosmos DB target used by the DynamoDB service level. Capacity settings and performance results cannot be transferred between those profiles by matching the provider name.

Receive timing

Two phases of ReceiveMessage have separate measurements:
  • Ready-message pickup: time from a message becoming available to a waiting receiver until the adapter sends the response. Only separately measured time spent waiting for the target service is excluded.
  • Delay after the requested wait: extra adapter time after the requested wait for a message has ended.
The pickup target applies when one message becomes ready for one waiting receiver that requested a single message. No other messages or receivers may compete for that delivery. For FIFO, an earlier message in the same group must not still be waiting for deletion. The message must be visible, unexpired, past any delivery delay, and available to that consumer. One ready message and 64 waiting receivers require one delivery, not 64. Ten thousand messages and one outstanding receive do not create ten thousand immediate delivery deadlines. This target does not set a deadline for processing an entire backlog. Waiting on an empty queue for the requested 20 seconds is intentional. If the adapter sleeps between queue checks after a message becomes available or after the wait deadline, that delay counts toward adapter latency. Waiting for a connection from the adapter’s pool also counts. The pickup clock does not start while a message is delayed or invisible.

Message guarantees

A send can return success only after the message state has been durably saved. A deduplication marker alone must not produce success for a message that was never committed. The adapter must use the same record of accepted messages when returning success, checking for duplicates, and recovering from a crash. Standard queues can redeliver messages. FIFO preserves the profile’s per-group ordering and supported deduplication behavior; it does not promise global order across groups or exactly-once effects in your application. A consumer can perform a business action and fail before deleting the message, so application-side idempotency still matters. The adapter follows its documented rules for visibility, expiration, message deletion, and movement to a dead-letter queue. Successfully completed changes must survive the process failures covered by the agreement. An expired receipt handle produces the result documented for that adapter; different backends need not return the same error. Approximate queue counts help with troubleshooting but are not guaranteed to show the exact current queue depth.

Useful throughput

Count completed API calls separately from message work. A valid empty ReceiveMessage completes an API call but delivers no message. One successful send, nonempty receive and delete form a message lifecycle involving at least three API calls. Redelivery and internal retries must not inflate the count of distinct messages processed. Measure Standard and FIFO separately. FIFO group distribution, time between receive and delete, visibility timeout, delayed messages and dead-letter traffic change the scaling limit. A large number of consumers cannot make a single serialized group behave like many independent groups. These standard service levels do not reserve a fixed message rate or specify a maximum throughput. Tensor9 and the customer must agree on limits for the actual workload and production deployment, including how quickly traffic can grow and how large a burst can be. Autoscaling is intended to maintain the stated latency and availability within those limits. It cannot remove ordering requirements or target-service limits.

Operation terms

Availability includes the named operations with the request options supported by the adapter, not only the 1 KiB latency workload. DeleteMessageBatch, ChangeMessageVisibilityBatch, queue management, tagging and approximate-count reads have no numerical SLA under these standard terms. A capability in the service profile does not automatically receive another operation’s SLA. The pickup service level differs from the Cloud SQL service level. These targets apply to their respective receive paths; they are not comparative benchmark results or a recommendation to choose one backend.

Scaling on Cosmos DB

Partition distribution, conditional message claims, deduplication and FIFO group serialization affect useful throughput. Native request-unit consumption and throttling need to be considered alongside adapter scheduling, repeated scans and retry amplification. Additional replicas can process independent work. They do not remove contention around the same message claim or FIFO group, and they do not grant additional native serverless capacity. Track completed message lifecycles, native calls per lifecycle and repeated failed claims separately. Once a message expires, the queue must treat it as expired even if the cloud provider has not yet removed its stored data. Do not count expired messages as available work merely because they still appear in a storage view. Time the adapter takes to find an available message, including waits between queue checks, also counts toward adapter latency. It is not automatically excluded as cloud-provider time.

Example: two receivers see the same candidate

Two receivers discover a message. The adapter must use a conditional update to assign the message to a receiver under the documented queue rules. It must not give both receivers conflicting ownership just to meet a latency target. Record the claim outcomes and native conditional responses, along with local retries and scheduling. A later redelivery after visibility expires can be legitimate. It is different from simultaneous ownership caused by a broken claim. Inspect receipt timing and the selected queue semantics before calling either event data loss or a duplicate-delivery defect.

Example: restart during a dead-letter transition

A message is copied to the dead-letter queue, then the process restarts before removing the source copy. Recovery must preserve the supported message semantics without adapter-induced loss. A valid redelivery is not the same as a missing accepted message. Preserve source and destination observations and identifiers before changing either queue. Do not delete both copies to make the counts agree, and do not replay a send solely to generate an Explain report. If Cosmos DB also reports throttling, investigate serverless/partition pressure and the adapter’s call/retry pattern. Target throttling does not excuse an incorrect recovery algorithm or excessive local retries.

Evidence for support

Record Standard versus FIFO, group distribution, message and attribute sizes, conditional-claim outcomes, wait/visibility settings, dead-letter configuration and the affected time window. Include native request IDs, request-unit/throttling observations and available adapter timing. Keep sensitive bodies and receipt handles out of shared reports. You can contact support before you know what caused the problem.

SQS (Simple Queue Service, FIFO queues) to PostgreSQL Flexible Server

What Tensor9 covers. These service levels cover the adapter between the origin API and target API. They do not replace the target provider’s SLA. How latency is counted. Adapter work, including metadata and coordination, counts. Only separately measured permitted target waits are excluded. Which terms apply. Your signed agreement names the covered operations, workload limits, remedies, and final service levels for your deployment.

What this adapter does

For azure::1.0.0::postgresql::flexible, The SQL queue uses row locking and SKIP LOCKED; FIFO adds head-of-group selection and deduplication state. The current long-poll loop can sleep between polls. Queue configuration is cached and durable queue state remains in PostgreSQL. A ReceiveMessage response budget is not a delivery-time promise: messages may not yet exist, may be delayed, or may be blocked by a FIFO group head. The short-poll row deliberately removes that ambiguity. Requested long-poll waits need their own measurement treatment in the agreement.

How the latency targets were chosen

Small message send and settlement: 10 ms monthly p99. ChangeMessageVisibility, DeleteMessage, SendMessage. Native forwarding needs bounded translation; SQL adds queue state; Cosmos FIFO requires multiple durable coordination steps and cannot honestly inherit a 1 ms forwarding budget. Bounded short-poll receive: 10 ms monthly p99. ReceiveMessage. Receive scans/selects candidates, claims visibility and creates receipt handles. FIFO head selection and Cosmos slot CAS cost more than a simple send. Bounded message batches: 100 ms monthly p99. DeleteMessageBatch, SendMessageBatch. A conservative upper budget for up to ten point paths plus aggregation. It does not assume the implementation parallelizes all target or metadata work. Queue configuration and discovery: 20 ms monthly p99. GetQueueAttributes, GetQueueUrl. Queue identity, attributes, tags and counts can use the adapter’s durable catalog; this is not excluded as target message work. Your signed agreement sets the terms for your deployment. A target does not add an operation or option that the compatibility tables mark unavailable.

What counts toward latency

For a request-response row, measure from the agreed ingress boundary to dispatch of the complete response. A row that explicitly names a first response chunk ends at that chunk; its number does not cover the rest of the stream. A long-poll row names the intentional wait and when adapter delay starts. Include parsing, authorization, admission, translation, serialization, adapter-owned storage and coordination, retries and response handling. Subtract only separately measured target-workload waits and external network segments allowed by the measurement rules. The adapter’s own response handling and dispatch remain covered. A database used for adapter metadata is still adapter work, even if a cloud provider hosts it. For concurrent calls, exclude the union of permitted wait intervals, not the sum of overlapping spans. Calculate each request’s adapter duration first, then the monthly p99. Do not subtract one service’s p99 from another’s. Known adapter timeouts are over-budget samples; failed or incomplete requests cannot disappear to improve the percentile. Missing measurements do not become zero latency. An SDK call span alone does not prove how much of its duration can be excluded.

Availability and failures

The 99.9% request target measures correct adapter handling, not the percentage of application calls that return success. Correctly forwarding a target quota or permission error is different from producing that error because the adapter sent the wrong request. Adapter-caused failures count even when the target is healthy. With 1,000,000 eligible calls in a month, a 99.9% target permits at most 1,000 adapter-attributable failures. Endpoint probes have their own denominator. Correctness defects remain actionable even when the monthly availability percentage is met.

Scaling and target-service capacity

Scale send, receive and settle capacity together, and track in-flight messages, pollers, candidate scans and FIFO group skew. Adding replicas helps independent groups but cannot parallelize the ordered head of one group. The agreement caps active pollers, groups inspected per receive, message bytes, retry amplification and batch entries. Tensor9 scales the fabric within those limits; the customer supplies sufficient broker capacity, Cosmos RU/s or PostgreSQL connections and I/O. Tell Tensor9 the expected steady rate, bursts, concurrency, payload sizes and operation mix. Tensor9 sizes and scales the adapter for the agreed load; you choose and monitor the target service’s capacity with Tensor9’s help. A latency budget is not a requests-per-second rating. Larger requests and higher rates need explicit terms, not silent inheritance of a small-request SLA.

Data and behavior guarantees

Acceptance must not be returned before the documented message persistence point. Settlement must apply to the intended message/receipt; FIFO order and deduplication apply only where this directed profile supports them. Duplicate delivery can be legitimate under at-least-once delivery. Dead-letter transitions must not silently discard an accepted message. Durability of the target broker/database remains its own commitment, while broken adapter receipt, marker or coordination logic remains Tensor9’s responsibility.

Example and diagnosis

For order-events, send a 2 KiB message, receive up to ten messages with WaitTimeSeconds=0, then settle each receipt. For a FIFO queue, distribute independent customers across groups instead of expecting one group to scale with replicas. If native message writes are fast but Cosmos FIFO sends slow down, inspect counter/dedup contention: those coordination waits still count against Tensor9’s fabric budget. Use tensor9 explain and the documented explain headers to understand the selected adapter and its behavior. Correlate available request diagnostics with the target provider’s latency, throttling and capacity metrics. An explain report helps diagnose a request; it is not by itself a qualified SLA timing measurement. Share the operation, request shape, timestamps and request identifiers with support, with credentials and customer payloads removed.

SQS (Simple Queue Service, FIFO queues) to Azure Service Bus

What Tensor9 covers. These service levels cover the adapter between the origin API and target API. They do not replace the target provider’s SLA. How latency is counted. Adapter work, including metadata and coordination, counts. Only separately measured permitted target waits are excluded. Which terms apply. Your signed agreement names the covered operations, workload limits, remedies, and final service levels for your deployment.

What this adapter does

For azure::1.0.0::servicebus, Service Bus messages and locks implement queue delivery; FIFO uses sessions. Lock renewal, receipt mapping and settlement remain adapter responsibilities, while native broker delivery uses the target service. A ReceiveMessage response budget is not a delivery-time promise: messages may not yet exist, may be delayed, or may be blocked by a FIFO group head. The short-poll row deliberately removes that ambiguity. Requested long-poll waits need their own measurement treatment in the agreement.

How the latency targets were chosen

Small message send and settlement: 1 ms monthly p99. ChangeMessageVisibility, DeleteMessage, SendMessage. Native forwarding needs bounded translation; SQL adds queue state; Cosmos FIFO requires multiple durable coordination steps and cannot honestly inherit a 1 ms forwarding budget. Bounded short-poll receive: 3 ms monthly p99. ReceiveMessage. Receive scans/selects candidates, claims visibility and creates receipt handles. FIFO head selection and Cosmos slot CAS cost more than a simple send. Bounded message batches: 10 ms monthly p99. SendMessageBatch. A conservative upper budget for up to ten point paths plus aggregation. It does not assume the implementation parallelizes all target or metadata work. Queue configuration and discovery: 20 ms monthly p99. DeleteQueue, ListQueueTags, ListQueues, PurgeQueue, SetQueueAttributes, TagQueue, UntagQueue. Queue identity, attributes, tags and counts can use the adapter’s durable catalog; this is not excluded as target message work. Your signed agreement sets the terms for your deployment. A target does not add an operation or option that the compatibility tables mark unavailable.

What counts toward latency

For a request-response row, measure from the agreed ingress boundary to dispatch of the complete response. A row that explicitly names a first response chunk ends at that chunk; its number does not cover the rest of the stream. A long-poll row names the intentional wait and when adapter delay starts. Include parsing, authorization, admission, translation, serialization, adapter-owned storage and coordination, retries and response handling. Subtract only separately measured target-workload waits and external network segments allowed by the measurement rules. The adapter’s own response handling and dispatch remain covered. A database used for adapter metadata is still adapter work, even if a cloud provider hosts it. For concurrent calls, exclude the union of permitted wait intervals, not the sum of overlapping spans. Calculate each request’s adapter duration first, then the monthly p99. Do not subtract one service’s p99 from another’s. Known adapter timeouts are over-budget samples; failed or incomplete requests cannot disappear to improve the percentile. Missing measurements do not become zero latency. An SDK call span alone does not prove how much of its duration can be excluded.

Availability and failures

The 99.9% request target measures correct adapter handling, not the percentage of application calls that return success. Correctly forwarding a target quota or permission error is different from producing that error because the adapter sent the wrong request. Adapter-caused failures count even when the target is healthy. With 1,000,000 eligible calls in a month, a 99.9% target permits at most 1,000 adapter-attributable failures. Endpoint probes have their own denominator. Correctness defects remain actionable even when the monthly availability percentage is met.

Scaling and target-service capacity

Scale send, receive and settle capacity together, and track in-flight messages, pollers, candidate scans and FIFO group skew. Adding replicas helps independent groups but cannot parallelize the ordered head of one group. The agreement caps active pollers, groups inspected per receive, message bytes, retry amplification and batch entries. Tensor9 scales the fabric within those limits; the customer supplies sufficient broker capacity, Cosmos RU/s or PostgreSQL connections and I/O. Tell Tensor9 the expected steady rate, bursts, concurrency, payload sizes and operation mix. Tensor9 sizes and scales the adapter for the agreed load; you choose and monitor the target service’s capacity with Tensor9’s help. A latency budget is not a requests-per-second rating. Larger requests and higher rates need explicit terms, not silent inheritance of a small-request SLA.

Data and behavior guarantees

Acceptance must not be returned before the documented message persistence point. Settlement must apply to the intended message/receipt; FIFO order and deduplication apply only where this directed profile supports them. Duplicate delivery can be legitimate under at-least-once delivery. Dead-letter transitions must not silently discard an accepted message. Durability of the target broker/database remains its own commitment, while broken adapter receipt, marker or coordination logic remains Tensor9’s responsibility.

Example and diagnosis

For order-events, send a 2 KiB message, receive up to ten messages with WaitTimeSeconds=0, then settle each receipt. For a FIFO queue, distribute independent customers across groups instead of expecting one group to scale with replicas. If native message writes are fast but Cosmos FIFO sends slow down, inspect counter/dedup contention: those coordination waits still count against Tensor9’s fabric budget. Use tensor9 explain and the documented explain headers to understand the selected adapter and its behavior. Correlate available request diagnostics with the target provider’s latency, throttling and capacity metrics. An explain report helps diagnose a request; it is not by itself a qualified SLA timing measurement. Share the operation, request shape, timestamps and request identifiers with support, with credentials and customer payloads removed.

SQS to Cloud SQL PostgreSQL

What Tensor9 covers. The SLA covers permission checks, deciding whether to accept a request, time waiting inside the adapter, request handling, coordination, retries, internal storage and response handling. Waiting for the target service is excluded only when that wait is measured separately and the agreement allows the exclusion. Your agreement must name the covered deployment, operations, request limits and traffic limits. Unlisted operations are not automatically covered. Incorrect results are still bugs even when the availability target is met. How queue timing works. Time spent waiting for a message in an empty queue does not count as adapter delay. Adapter delays after a message becomes available, or after the requested wait ends, do count. The message-pickup target is not a deadline for clearing a backlog. Measure Standard and FIFO queues separately, and check each batch entry’s result. Empty receives and internal retries do not count as successfully delivered messages. Scaling limits. Tensor9 automatically adds adapter capacity within the traffic growth, burst size and infrastructure limits in your agreement. The standard service level does not promise a fixed request rate or unlimited scaling. Capacity depends on request sizes and types, how traffic is distributed across keys or message groups, target configuration and spare capacity for failures. More adapter processes cannot speed up every shared or sequential step. You remain responsible for target-service capacity; failures caused independently by the provider are outside this SLA. Read the details. Each linked service page lists the covered operations, request limits, measurement rules, data guarantees, scaling limits and examples. This summary does not add coverage beyond those terms. Your signed agreement determines the service levels that apply to your deployment.

Why these latency targets differ

The shared Standard/FIFO point-operation target stays at 10 ms. A FIFO request can perform durable deduplication and group coordination, so reducing the target to 1 ms would ignore real adapter work. A batch of up to ten entries has a 100 ms budget; this allows sequential per-entry coordination rather than assuming every batch path runs in parallel. Latency targets do not imply a throughput guarantee or favor this backend over another. Ready-message pickup and delay after the requested wait retain their 250 ms targets. The PostgreSQL backend currently polls with sleeps of up to 100 ms between checks. Sleep after a message becomes eligible counts as adapter delay; it is not Cloud SQL execution time. A future notification-driven implementation could support a lower pickup target, but request parsing alone cannot justify one today.

Adapter and scope

Standard-to-Cloud SQL and FIFO-to-Cloud SQL are separate service adapters. They have the same standard latency targets here, but different ordering, deduplication, and scaling requirements. Agreeing to an SLA for one does not cover the other.

Receive timing

Two phases of ReceiveMessage have separate measurements:
  • Ready-message pickup: time from a message becoming available to a waiting receiver until the adapter sends the response. Only separately measured time spent waiting for the target service is excluded.
  • Delay after the requested wait: extra adapter time after the requested wait for a message has ended.
The pickup target applies when one message becomes ready for one waiting receiver that requested a single message. No other messages or receivers may compete for that delivery. For FIFO, an earlier message in the same group must not still be waiting for deletion. The message must be visible, unexpired, past any delivery delay, and available to that consumer. One ready message and 64 waiting receivers require one delivery, not 64. Ten thousand messages and one outstanding receive do not create ten thousand immediate delivery deadlines. This target does not set a deadline for processing an entire backlog. Waiting on an empty queue for the requested 20 seconds is intentional. If the adapter sleeps between queue checks after a message becomes available or after the wait deadline, that delay counts toward adapter latency. Waiting for a connection from the adapter’s pool also counts. The pickup clock does not start while a message is delayed or invisible.

Message guarantees

A send can return success only after the message state has been durably saved. A deduplication marker alone must not produce success for a message that was never committed. The adapter must use the same record of accepted messages when returning success, checking for duplicates, and recovering from a crash. Standard queues can redeliver messages. FIFO preserves the profile’s per-group ordering and supported deduplication behavior; it does not promise global order across groups or exactly-once effects in your application. A consumer can perform a business action and fail before deleting the message, so application-side idempotency still matters. The adapter follows its documented rules for visibility, expiration, message deletion, and movement to a dead-letter queue. Successfully completed changes must survive the process failures covered by the agreement. An expired receipt handle produces the result documented for that adapter; different backends need not return the same error. Approximate queue counts help with troubleshooting but are not guaranteed to show the exact current queue depth.

Useful throughput

Count completed API calls separately from message work. A valid empty ReceiveMessage completes an API call but delivers no message. One successful send, nonempty receive and delete form a message lifecycle involving at least three API calls. Redelivery and internal retries must not inflate the count of distinct messages processed. Measure Standard and FIFO separately. FIFO group distribution, time between receive and delete, visibility timeout, delayed messages and dead-letter traffic change the scaling limit. A large number of consumers cannot make a single serialized group behave like many independent groups. These standard service levels do not reserve a fixed message rate or specify a maximum throughput. Tensor9 and the customer must agree on limits for the actual workload and production deployment, including how quickly traffic can grow and how large a burst can be. Autoscaling is intended to maintain the stated latency and availability within those limits. It cannot remove ordering requirements or target-service limits.

Operation terms

Availability covers all the operations listed here with the request options supported by the adapter. The 1 KiB limits apply to the latency target, not the availability target. Supported larger messages are still covered by availability. Queue management, tagging, approximate-count reads and ChangeMessageVisibilityBatch have no numerical SLA under these standard terms. That does not declare them unsupported. Check the adapter’s support details separately. A similar operation name does not make an operation covered by the SLA. For batch calls, retain per-entry successes and failures. The response to a partial batch is not evidence that every submitted message completed successfully.

Scaling on Cloud SQL

Connection budgets, transaction/lock contention and message claims can limit the adapter before the database reaches its advertised compute capacity. A pool limit configured independently on every replica can exceed the database’s total allowance during scale-out or rollout. Limit total database connections across all adapter instances, including instances that are starting or shutting down. Review query work and lock contention alongside Cloud SQL CPU, storage and connection metrics. Native database execution is separately timed, but acquiring a local connection and scheduling local polling remain adapter work. Independent messages and FIFO groups offer parallel work. A hot group still requires ordered progress. Adding adapter instances must not make competing claims or repeated retries appear to be more successfully processed messages.

Example: an empty long poll receives a job

The jobs queue has an outstanding 20-second ReceiveMessage. Ten seconds later, a message becomes eligible for that receiver. The first ten seconds are legitimate waiting. Measure the subsequent local polling, claim scheduling and response handling against pickup conditions, with native execution recorded separately. If no message becomes eligible, measure the adapter delay after the requested 20-second deadline instead. Do not compare the entire long poll with the receive processing budget, and do not use approximate queue depth to infer the exact eligibility timestamp.

Example: a committed send loses its response

PostgreSQL commits a message, then the adapter restarts before returning success. The response-path failure remains a adapter availability issue even though the message exists. The caller’s outcome is uncertain. Inspect the observed state and supported deduplication behavior before retrying. Standard-queue redelivery remains possible. If recovery leaks connections or fails to restore message state, that is not excluded merely because a managed database holds the data. A Cloud SQL outage not caused by the adapter is outside the standard adapter SLA. Tensor9 still helps investigate recovery and backlog behavior. Excessive retries or broken connection recovery remain adapter issues.

Evidence for support

Keep the queue type, operation, message-size range, batch outcomes, active FIFO group distribution, wait/visibility settings, time between receiving and deleting a message and relevant request IDs. Include Cloud SQL connections, contention and target timing for the same interval. Redact message bodies and receipt handles before sharing. An available Explain report supplements these observations; its absence does not prevent support.

SQS (Simple Queue Service, FIFO queues) to PostgreSQL

What Tensor9 covers. These service levels cover the adapter between the origin API and target API. They do not replace the target provider’s SLA. How latency is counted. Adapter work, including metadata and coordination, counts. Only separately measured permitted target waits are excluded. Which terms apply. Your signed agreement names the covered operations, workload limits, remedies, and final service levels for your deployment.

What this adapter does

For postgres::1.0.0::database, The SQL queue uses row locking and SKIP LOCKED; FIFO adds head-of-group selection and deduplication state. The current long-poll loop can sleep between polls. Queue configuration is cached and durable queue state remains in PostgreSQL. A ReceiveMessage response budget is not a delivery-time promise: messages may not yet exist, may be delayed, or may be blocked by a FIFO group head. The short-poll row deliberately removes that ambiguity. Requested long-poll waits need their own measurement treatment in the agreement.

How the latency targets were chosen

Small message send and settlement: 10 ms monthly p99. ChangeMessageVisibility, DeleteMessage, SendMessage. Native forwarding needs bounded translation; SQL adds queue state; Cosmos FIFO requires multiple durable coordination steps and cannot honestly inherit a 1 ms forwarding budget. Bounded short-poll receive: 10 ms monthly p99. ReceiveMessage. Receive scans/selects candidates, claims visibility and creates receipt handles. FIFO head selection and Cosmos slot CAS cost more than a simple send. Bounded message batches: 100 ms monthly p99. DeleteMessageBatch, SendMessageBatch. A conservative upper budget for up to ten point paths plus aggregation. It does not assume the implementation parallelizes all target or metadata work. Queue configuration and discovery: 20 ms monthly p99. GetQueueAttributes, GetQueueUrl. Queue identity, attributes, tags and counts can use the adapter’s durable catalog; this is not excluded as target message work. Your signed agreement sets the terms for your deployment. A target does not add an operation or option that the compatibility tables mark unavailable.

What counts toward latency

For a request-response row, measure from the agreed ingress boundary to dispatch of the complete response. A row that explicitly names a first response chunk ends at that chunk; its number does not cover the rest of the stream. A long-poll row names the intentional wait and when adapter delay starts. Include parsing, authorization, admission, translation, serialization, adapter-owned storage and coordination, retries and response handling. Subtract only separately measured target-workload waits and external network segments allowed by the measurement rules. The adapter’s own response handling and dispatch remain covered. A database used for adapter metadata is still adapter work, even if a cloud provider hosts it. For concurrent calls, exclude the union of permitted wait intervals, not the sum of overlapping spans. Calculate each request’s adapter duration first, then the monthly p99. Do not subtract one service’s p99 from another’s. Known adapter timeouts are over-budget samples; failed or incomplete requests cannot disappear to improve the percentile. Missing measurements do not become zero latency. An SDK call span alone does not prove how much of its duration can be excluded.

Availability and failures

The 99.9% request target measures correct adapter handling, not the percentage of application calls that return success. Correctly forwarding a target quota or permission error is different from producing that error because the adapter sent the wrong request. Adapter-caused failures count even when the target is healthy. With 1,000,000 eligible calls in a month, a 99.9% target permits at most 1,000 adapter-attributable failures. Endpoint probes have their own denominator. Correctness defects remain actionable even when the monthly availability percentage is met.

Scaling and target-service capacity

Scale send, receive and settle capacity together, and track in-flight messages, pollers, candidate scans and FIFO group skew. Adding replicas helps independent groups but cannot parallelize the ordered head of one group. The agreement caps active pollers, groups inspected per receive, message bytes, retry amplification and batch entries. Tensor9 scales the fabric within those limits; the customer supplies sufficient broker capacity, Cosmos RU/s or PostgreSQL connections and I/O. Tell Tensor9 the expected steady rate, bursts, concurrency, payload sizes and operation mix. Tensor9 sizes and scales the adapter for the agreed load; you choose and monitor the target service’s capacity with Tensor9’s help. A latency budget is not a requests-per-second rating. Larger requests and higher rates need explicit terms, not silent inheritance of a small-request SLA.

Data and behavior guarantees

Acceptance must not be returned before the documented message persistence point. Settlement must apply to the intended message/receipt; FIFO order and deduplication apply only where this directed profile supports them. Duplicate delivery can be legitimate under at-least-once delivery. Dead-letter transitions must not silently discard an accepted message. Durability of the target broker/database remains its own commitment, while broken adapter receipt, marker or coordination logic remains Tensor9’s responsibility.

Example and diagnosis

For order-events, send a 2 KiB message, receive up to ten messages with WaitTimeSeconds=0, then settle each receipt. For a FIFO queue, distribute independent customers across groups instead of expecting one group to scale with replicas. If native message writes are fast but Cosmos FIFO sends slow down, inspect counter/dedup contention: those coordination waits still count against Tensor9’s fabric budget. Use tensor9 explain and the documented explain headers to understand the selected adapter and its behavior. Correlate available request diagnostics with the target provider’s latency, throttling and capacity metrics. An explain report helps diagnose a request; it is not by itself a qualified SLA timing measurement. Share the operation, request shape, timestamps and request identifiers with support, with credentials and customer payloads removed.

SQS (Simple Queue Service, FIFO queues) to Scaleway Managed Database for PostgreSQL

What Tensor9 covers. These service levels cover the adapter between the origin API and target API. They do not replace the target provider’s SLA. How latency is counted. Adapter work, including metadata and coordination, counts. Only separately measured permitted target waits are excluded. Which terms apply. Your signed agreement names the covered operations, workload limits, remedies, and final service levels for your deployment.

What this adapter does

For scaleway::1.0.0::rdb::postgresql, The SQL queue uses row locking and SKIP LOCKED; FIFO adds head-of-group selection and deduplication state. The current long-poll loop can sleep between polls. Queue configuration is cached and durable queue state remains in PostgreSQL. A ReceiveMessage response budget is not a delivery-time promise: messages may not yet exist, may be delayed, or may be blocked by a FIFO group head. The short-poll row deliberately removes that ambiguity. Requested long-poll waits need their own measurement treatment in the agreement.

How the latency targets were chosen

Small message send and settlement: 10 ms monthly p99. ChangeMessageVisibility, DeleteMessage, SendMessage. Native forwarding needs bounded translation; SQL adds queue state; Cosmos FIFO requires multiple durable coordination steps and cannot honestly inherit a 1 ms forwarding budget. Bounded short-poll receive: 10 ms monthly p99. ReceiveMessage. Receive scans/selects candidates, claims visibility and creates receipt handles. FIFO head selection and Cosmos slot CAS cost more than a simple send. Bounded message batches: 100 ms monthly p99. DeleteMessageBatch, SendMessageBatch. A conservative upper budget for up to ten point paths plus aggregation. It does not assume the implementation parallelizes all target or metadata work. Queue configuration and discovery: 20 ms monthly p99. GetQueueAttributes, GetQueueUrl. Queue identity, attributes, tags and counts can use the adapter’s durable catalog; this is not excluded as target message work. Your signed agreement sets the terms for your deployment. A target does not add an operation or option that the compatibility tables mark unavailable.

What counts toward latency

For a request-response row, measure from the agreed ingress boundary to dispatch of the complete response. A row that explicitly names a first response chunk ends at that chunk; its number does not cover the rest of the stream. A long-poll row names the intentional wait and when adapter delay starts. Include parsing, authorization, admission, translation, serialization, adapter-owned storage and coordination, retries and response handling. Subtract only separately measured target-workload waits and external network segments allowed by the measurement rules. The adapter’s own response handling and dispatch remain covered. A database used for adapter metadata is still adapter work, even if a cloud provider hosts it. For concurrent calls, exclude the union of permitted wait intervals, not the sum of overlapping spans. Calculate each request’s adapter duration first, then the monthly p99. Do not subtract one service’s p99 from another’s. Known adapter timeouts are over-budget samples; failed or incomplete requests cannot disappear to improve the percentile. Missing measurements do not become zero latency. An SDK call span alone does not prove how much of its duration can be excluded.

Availability and failures

The 99.9% request target measures correct adapter handling, not the percentage of application calls that return success. Correctly forwarding a target quota or permission error is different from producing that error because the adapter sent the wrong request. Adapter-caused failures count even when the target is healthy. With 1,000,000 eligible calls in a month, a 99.9% target permits at most 1,000 adapter-attributable failures. Endpoint probes have their own denominator. Correctness defects remain actionable even when the monthly availability percentage is met.

Scaling and target-service capacity

Scale send, receive and settle capacity together, and track in-flight messages, pollers, candidate scans and FIFO group skew. Adding replicas helps independent groups but cannot parallelize the ordered head of one group. The agreement caps active pollers, groups inspected per receive, message bytes, retry amplification and batch entries. Tensor9 scales the fabric within those limits; the customer supplies sufficient broker capacity, Cosmos RU/s or PostgreSQL connections and I/O. Tell Tensor9 the expected steady rate, bursts, concurrency, payload sizes and operation mix. Tensor9 sizes and scales the adapter for the agreed load; you choose and monitor the target service’s capacity with Tensor9’s help. A latency budget is not a requests-per-second rating. Larger requests and higher rates need explicit terms, not silent inheritance of a small-request SLA.

Data and behavior guarantees

Acceptance must not be returned before the documented message persistence point. Settlement must apply to the intended message/receipt; FIFO order and deduplication apply only where this directed profile supports them. Duplicate delivery can be legitimate under at-least-once delivery. Dead-letter transitions must not silently discard an accepted message. Durability of the target broker/database remains its own commitment, while broken adapter receipt, marker or coordination logic remains Tensor9’s responsibility.

Example and diagnosis

For order-events, send a 2 KiB message, receive up to ten messages with WaitTimeSeconds=0, then settle each receipt. For a FIFO queue, distribute independent customers across groups instead of expecting one group to scale with replicas. If native message writes are fast but Cosmos FIFO sends slow down, inspect counter/dedup contention: those coordination waits still count against Tensor9’s fabric budget. Use tensor9 explain and the documented explain headers to understand the selected adapter and its behavior. Correlate available request diagnostics with the target provider’s latency, throttling and capacity metrics. An explain report helps diagnose a request; it is not by itself a qualified SLA timing measurement. Share the operation, request shape, timestamps and request identifiers with support, with credentials and customer payloads removed.

Install and run

The documentation and SLAs are public. Downloading and running the Cloud Adapter binary requires an entitled customer account. Set T9_API_KEY as described in Install and Authenticate, then list the adapters your account may use with this origin:
The result is limited to directed mappings for this origin that your account is entitled to use. Use a returned target identifier when configuring the adapter or investigating a request. Request binary access from [email protected] if your account does not yet include this direction.

Existing data and credentials

Selecting a backend does not copy existing data, credentials or access policies. Plan and verify migration separately before changing an application’s endpoint. Do not assume an identifier, credential or encrypted value from the origin service works unchanged on the target.

Configure, tune and debug

Start with setup and configuration. Use tuning to understand supported request tags, debugging to investigate a request, and High Fidelity Cloud Emulators to validate a bounded reproduction. For requests that go through an entitled service adapter, use tensor9 explain and x-t9-explain to inspect the selected mapping.