Message queues are the oldest trick in distributed systems, and they remain the most underused one. Teams reach for synchronous APIs by default, and then the checkout page errors because a downstream report generator is busy, and an entire user journey is held hostage by the least reliable component in the chain. A queue breaks that hostage situation. The producer hands its message to durable storage and moves on, the consumer picks it up when it has capacity, and neither needs the other to be fast, available, or even running at the same moment.
This article is the deep dive on OCI Queue within our broader series on application development and serverless on OCI. We will cover what the service is, how a message moves through its lifecycle, what at least once delivery demands of consumers, how dead letter queues and channels work, what it costs, and how it compares with Streaming and Notifications.
What OCI Queue actually is
OCI Queue is a fully managed, serverless message queue. You create a queue, producers publish messages to it, and consumers pull messages from it. There are no brokers to size, no clusters to patch, and no partitions to plan; the service stores messages durably within the region and scales capacity as traffic grows. From the team's point of view, a queue is an OCID and an endpoint, and everything else is Oracle's problem.
Access comes through two doors. The first is a straightforward REST API, callable from the OCI SDKs, the CLI, or anything that can sign an HTTP request. The second is STOMP, the Simple Text Oriented Messaging Protocol, an open standard that long predates OCI and is supported by a wide ecosystem of client libraries, which means applications already written against open messaging standards can often point at OCI Queue with minimal change. Messages carry payloads up to 128 KB, with custom metadata alongside the body, and the queue retains each message for a configurable period of up to seven days if nobody consumes it.
Why decoupling earns its keep
The producer consumer model sounds academic until you watch it absorb a real incident. Decoupling buys you three concrete things. It absorbs bursts: a campaign sends ten times the normal traffic, the queue accepts every message instantly, and consumers work through the backlog at their sustainable rate. It isolates failures: when the consumer crashes, producers keep working and messages accumulate safely, so the outage becomes a delay rather than data loss. And it smooths load: a database that would be hammered by spiky writes instead receives a steady stream, sized for the average rather than the worst minute of the year.
This is the same reasoning that underpins the wider shift to event driven architecture on OCI, but a queue is the gentlest entry point. You do not need to redesign your system around events; you need exactly one seam. Find the synchronous call that hurts the most, and replace it with a publish on one side and a consume loop on the other.
The message lifecycle
Every message moves through a small, predictable lifecycle, and understanding it is the whole job of operating the service well. A producer publishes the message; the service stores it durably and acknowledges the write. A consumer requests messages and the service delivers a batch, but delivery does not remove the message. Instead, it becomes invisible to other consumers for a window called the visibility timeout. The consumer processes the message and, on success, explicitly deletes it using a receipt handle returned at delivery. If the consumer crashes or fails to delete the message before the visibility timeout expires, the message becomes visible again and will be delivered on a later request.
Two refinements round out the lifecycle. A consumer that needs more time can update the message to extend its visibility timeout, which beats setting a huge timeout for every message, because a short default means failed work returns to the queue quickly. And a message that is never consumed does not live forever: when its retention period passes, the service expires and removes it. The lifecycle gives you a clean contract. Deletion is the consumer's deliberate statement that the work is done; everything else is the platform making sure no message is lost in between.
At least once delivery and the idempotency tax
OCI Queue, like nearly every distributed queue ever built, promises at least once delivery, not exactly once. The visibility timeout mechanics make it obvious why: if a consumer processes a message but crashes a millisecond before the delete call succeeds, the message reappears and gets processed again. The platform cannot tell a consumer that died before doing the work from one that died after, so it errs on the side of redelivery.
The consequence is not negotiable: consumers must be idempotent. Processing the same message twice has to produce the same end state as processing it once. The techniques are familiar: key side effects on a unique identifier carried in the message so the second attempt sees the work already recorded and skips it, use database upserts rather than blind inserts, and make external calls conditional on state you can check. Design the handler so that a duplicate is boring, and redelivery stops being a bug class entirely.
Dead letter queues: where bad messages go to be understood
Some messages will never process successfully no matter how often they are retried: a malformed payload, a reference to a deleted record, a poison input that crashes the handler deterministically. Redelivering these forever burns consumer capacity and buries real work, so OCI Queue handles them with a dead letter queue. You configure a maximum delivery attempts value, and when a message has been delivered that many times without being deleted, the service moves it into the DLQ automatically.
The DLQ is not a graveyard; it is an inbox for engineering attention. Messages in it can be read and inspected like any others, so you can see exactly which payloads failed, and once the defect is fixed they can be replayed by publishing them back to the main queue. We hold clients to two rules here. Every production queue gets a DLQ, because without one poison messages cycle forever or silently expire. And the DLQ gets an alarm on its depth, because a message arriving there is a defect report, and defect reports that nobody reads are how small bugs become weekend incidents.
Channels: grouping without the ceremony of partitions
Channels are the most distinctive feature of OCI Queue. A channel is a lightweight, ephemeral subqueue inside a queue, created implicitly when a producer publishes with a channel identifier; there is nothing to provision. Consumers can ask for messages from a specific channel, or from any channel using the default behaviour, where the service selects among nonempty channels in a way that spreads attention across them.
The two payoffs are grouping and fairness. Grouping: if every message for a given tenant, device, or order flows through its own channel, a consumer can drain that channel exclusively and process related messages together. Fairness, the quietly brilliant part: in a multitenant system, putting each tenant on a channel means the random channel selection naturally prevents a noisy tenant from starving everyone else, so one tenant's burst stays its own problem rather than becoming everybody's.
Long polling: cheaper and faster at the same time
A naive consumer loop hammers the queue with requests that mostly return nothing, which costs money on per request pricing and adds latency because a message that arrives between polls waits for the next one. Long polling fixes both at once: the consumer's request includes a wait parameter, and if the queue is empty the service holds the connection open, up to 30 seconds, returning the moment a message arrives. There is no tradeoff hiding here; long polling is simply how every consumer should be written, and the only reason we mention it is that we keep finding tight empty loops in code reviews.
Capacity, throughput, and what it costs
The pricing model is refreshingly small. You pay per million API requests, where a request is a publish, a consume, a delete, or an update, and the first million requests each month cost nothing. There are no broker fees and no charge for an idle queue, so a modest workload can sit entirely inside the free allowance. Throughput is governed by capacity units that define how many requests and how much data per second a queue sustains, scaled transparently for most workloads. Two habits keep costs honest: batch where the API allows it, since several messages per request multiplies throughput without multiplying the bill, and use long polling, since the difference between a tight empty loop and a 30 second wait is often a tenfold difference in request volume.
Security: IAM all the way down
OCI Queue carries no separate credential system; access control is ordinary OCI IAM. A producer group can be allowed to publish to queues in one compartment and nothing more, while a consumer dynamic group is allowed to consume and delete but not publish. Splitting those rights is cheap insurance, and the policy statements end up reading like a data flow diagram. For network isolation, the service supports private endpoints, so queue traffic from your VCN never crosses the public internet, and messages are encrypted at rest and in transit as standard. There is nothing exotic to learn here, which is exactly the compliment we intend.
Queue, Streaming, or Notifications?
OCI offers three messaging services, and choosing the wrong one is the most common design error we see in reviews. The distinction is about what happens to a message after delivery and who decides the pace.
| Service | Model | Choose it when |
|---|---|---|
| OCI Queue | Point to point queueing. Consumers pull, process, and delete each message. At least once delivery, no ordering guarantee. | Work distribution: tasks and commands that exactly one worker should handle and then delete. |
| OCI Streaming | Ordered, replayable log with partitions. Messages persist for the retention window, and multiple consumer groups read independently. | Event sourcing and pipelines where several systems must read the same history, in order, possibly more than once. |
| OCI Notifications | Fan out push. A message published to a topic is pushed to every subscription: email, Functions, HTTPS endpoints, Slack, PagerDuty. | Broadcasting: alarms, alerts, and signals that many parties should receive simultaneously without polling. |
A useful shorthand: Queue is for work, Streaming is for history, Notifications is for announcements. If a message is a task one worker should do, queue it. If it is a fact several systems will want to read, perhaps replaying from the past, stream it. If it is news everyone should hear right now, publish it to a topic; we cover that service in our guide to OCI Notifications. The three combine naturally, and the OCI Events service frequently sits upstream of all of them, turning resource state changes into messages that flow into whichever transport fits.
Patterns that pay for themselves
Four patterns account for most of the queues we deploy. The work queue behind an API: the API validates a request, publishes a task, and returns immediately with an identifier the client can poll, so user facing latency stays constant however heavy the work is. Buffering writes to a database: producers enqueue records and a consumer applies them in steady, batched transactions, so the database is sized for the average and the queue eats the variance.
Task offload from the web tier: anything a request handler does that the user does not need to wait for, sending emails, resizing images, updating search indexes, moves behind a queue, keeping the web tier thin and fast. And the dead letter path for Functions: when a function cannot process its event, it publishes the failed payload to a queue with diagnostic context, an alarm watches the depth, and the team replays after fixing the defect. The same patterns scale gracefully into containerised estates, where queues form the seams between services; we explore that territory in microservice patterns on OKE.
Introducing a queue between two coupled services
Retrofitting a queue into a live system is delicate if improvised and routine if sequenced. This is the framework we use in engagements.
- Pick the seam with evidence. Identify the synchronous call causing the most timeouts and cascading failures, using traces and incident history rather than intuition.
- Define the message contract. Specify the payload schema, a unique identifier for idempotency, and a version field, agreed between both teams before any code changes.
- Create the queue with its safety rails. Set retention and visibility timeout to fit the work, configure maximum delivery attempts, and attach the dead letter queue from day one.
- Build the consumer first. Write an idempotent consumer with long polling, and prove the delete on success and redelivery on failure paths with injected faults.
- Dual run before cutting over. Have the producer publish to the queue while still making the original call, and verify the consumer's output matches the legacy path on real traffic.
- Cut over and remove the old call. Switch the producer to publish only, watch depth and consumer errors through the first peak cycle, and keep the rollback switch warm.
- Instrument for the long term. Build alarms on queue depth, message age, and DLQ depth, and document the replay procedure so the first poison message is handled by process, not heroics.
Running queues in production
Day two with a queue revolves around three signals. Queue depth is the headline: growing depth means consumers are falling behind, and an alarm on depth, or better, on the age of the oldest message, tells you before users notice. DLQ depth should alarm at one, because every message there is a defect to triage. Consumer health, error rates and processing duration, explains most depth problems before you ever look at the queue itself.
Scaling the consumer fleet is where queues repay the investment. Depth is a direct, honest measure of backlog, which makes it the ideal autoscaling signal: scale worker counts on depth or message age, and the system self corrects through bursts without anyone touching it. Keep visibility timeouts modest so a crashed worker's messages return quickly, and load test the full path, publish through consume through delete, rather than each side in isolation. None of this is difficult, which is the point. The operational surface of a managed queue is a handful of alarms and a scaling rule.
Bringing it together
OCI Queue is the simplest, cheapest way to stop two services from sharing a fate. The lifecycle is small enough to hold in your head, at least once delivery asks one discipline of you, idempotent consumers, and channels and dead letter queues handle the multitenancy and failure cases that require real engineering elsewhere. Most estates we review would be more resilient with two or three more queues in them, at the seams where synchronous calls currently fail loudly. If you are modernising applications onto OCI and working out where queues, events, and streams belong in the target architecture, that is exactly the work our app modernization practice does, and our OCI implementation service builds the tenancy, networking, and delivery pipelines underneath it.
Free white paper
Go deeper on this topic with The OCI Landing Zone and Architecture Guide, a reference architecture for security, networking, and governance on OCI. An independent analyst style report with comparison tables and recommendations, free with a work email. Prefer a monthly summary instead? The OCI Brief delivers one practical OCI briefing a month.
Part of a series
This guide is part of OCI Platform & Architecture — our complete pillar guide on the topic.
Moving Oracle workloads to OCI, or already running on OCI and not sure the architecture or the spend is right? Most teams bring in a specialist before they commit to a region, a shape, or a Universal Credits number. OCISpecialists.com plans the landing zone, runs the migration, and manages the estate after go live, on a fixed project fee, a managed monthly retainer, or a cost optimization fee paid only on verified savings.