Let me paint the problem. You are building an e-commerce app. When a user confirms an order, three things need to happen: inventory gets updated, a shipping label gets created, and a confirmation email gets sent.
The simple approach is to have your order service make direct, sequential API calls to inventory, shipping, and email services. Synchronous communication. The user clicks “Confirm Order” and their browser waits until all three backend services respond.
Now what happens when the email service is slow or temporarily down? The entire order process hangs. The user gets an error. Your system is brittle — one service failure cascades across everything. The services are tightly coupled.
The fix is asynchronous messaging. Instead of direct calls, the order service drops messages into a queue: “Update inventory for order #123,” “Create shipping label for order #123,” “Send email for order #123.” It does not care if the email service is busy. The message waits until the service is ready.
In Google Cloud, that message queue is Cloud Pub/Sub.
How Pub/Sub Works
Pub/Sub is a fully managed, global, real-time messaging service. It handles millions of messages per second. Here are the core concepts:
Topic — A named channel where messages are sent. Publishers do not send messages directly to subscribers — they send to a topic. For our e-commerce app, we might have topics like order-confirmations, inventory-updates, and shipping-requests.
# Create a topic
gcloud pubsub topics create order-confirmations
Publisher — Any application that sends messages to a topic. Our order service is a publisher.
# Publish a message
gcloud pubsub topics publish order-confirmations \
--message='{"orderId": 123, "userId": 456, "total": 99.99}'
Subscription — A named resource representing a stream of messages from a topic. Think of it as a P.O. box for a specific magazine. The email service creates a subscription to receive order confirmations:
# Create a subscription
gcloud pubsub subscriptions create email-service-sub \
--topic=order-confirmations
The key insight: Multiple services can subscribe to the same topic independently. The shipping service creates its own subscription to order-confirmations. When one message is published, Pub/Sub delivers a copy to each subscription separately.
Subscriber — The application that reads messages from a subscription.
Push vs. Pull Delivery
Pull (default, most common) — The subscriber actively polls: “Got any new messages?” The subscriber controls the pace. If busy, it slows down; if idle, it asks for more. Best for backend services.
Push — Pub/Sub acts as the mail carrier. You provide an HTTP endpoint (webhook), and Pub/Sub makes an HTTP POST with each message. Simpler for certain use cases, especially triggering Cloud Functions. But can overwhelm your endpoint during bursts.
# Pull messages from a subscription
gcloud pubsub subscriptions pull email-service-sub \
--auto-ack --limit=5
Acknowledgments — The Critical Concept
What if your email service pulls a message, then the VM crashes before sending the email? Is the message lost? No.
When a subscriber receives a message, a timer starts — the ack deadline (e.g., 60 seconds). The subscriber must process the message and send an acknowledgment (ack) back to Pub/Sub within that time.
- Ack received in time → Message is marked delivered and deleted
- No ack before deadline → Pub/Sub assumes failure and redelivers the message
This gives you at-least-once delivery. Your message will be delivered, but in rare failure scenarios, it might be delivered more than once.
This means your subscribers MUST be idempotent. Running the same message twice should produce the same result. For the email service: check if the email for order #123 was already sent before sending it again.
Dead-Letter Topics — Handling Poison Messages
What if a malformed message causes your subscriber to crash every time it processes it? Without intervention, Pub/Sub redelivers it forever, clogging your queue.
The solution is a Dead-Letter Topic (DLT). Configure your subscription: “If a message fails delivery 5 times, stop retrying and move it to this dead-letter topic.” This gets the poison message out of your main pipeline so you can inspect it later.
# Create subscription with dead-letter topic
gcloud pubsub subscriptions create email-service-sub \
--topic=order-confirmations \
--dead-letter-topic=order-dead-letters \
--max-delivery-attempts=5
Message Ordering and Filtering
By default, Pub/Sub does NOT guarantee message ordering. For many use cases, that is fine. But if you need to process all updates for a specific user in order, you enable message ordering on the subscription and provide an ordering key (e.g., the userId) when publishing.
You can also create subscriptions with filters to receive only a subset of messages:
# Subscription that only gets high-priority orders
gcloud pubsub subscriptions create high-priority-sub \
--topic=order-confirmations \
--message-filter="attributes.priority = 'high'"
Common Pitfalls and Best Practices
Pitfall: Assuming exactly-once delivery and skipping idempotency. Leads to duplicate processing.
Best Practice: Always design subscribers to be idempotent. This is the golden rule of Pub/Sub.
Pitfall: Ack deadline too short. If processing takes longer, Pub/Sub redelivers — creating an infinite loop.
Best Practice: Set the ack deadline longer than expected processing time. Your subscriber can also extend the deadline per-message.
Pitfall: No dead-letter topic. One poison message halts your entire pipeline.
Best Practice: Always configure a DLT on production subscriptions.
Pitfall: Using Push for a service that cannot handle traffic bursts.
Best Practice: Default to Pull for backend services. Use Push for simple, stateless integrations like Cloud Functions.
Quick Reference
# Create a topic
gcloud pubsub topics create [TOPIC]
# List topics
gcloud pubsub topics list
# Publish a message
gcloud pubsub topics publish [TOPIC] --message="Hello"
# Create a subscription
gcloud pubsub subscriptions create [SUB] --topic=[TOPIC]
# Pull messages
gcloud pubsub subscriptions pull [SUB] --auto-ack --limit=5
# Subscription with dead-letter topic
gcloud pubsub subscriptions create [SUB] --topic=[TOPIC] \
--dead-letter-topic=[DLT] --max-delivery-attempts=5
# Subscription with message filter
gcloud pubsub subscriptions create [SUB] --topic=[TOPIC] \
--message-filter="attributes.priority = 'high'"

