Skip to content
Putting technology to work.
Insights to guide decisions and action.

Search articles

Committing DB updates and shipping requests together with Spanner queues

Table of contents · 5 items

When an order is confirmed, a shipping request is sent to the warehouse system. If the database update succeeds but only the message send fails, the order is "confirmed" but never shipped. Conversely, if the database update is rolled back after the message is sent, shipping starts for an order that does not exist. This is a typical inconsistency in business systems caused by "dual writes," where the database and the message queue are written separately.

In its official blog on October 2, 2026, Google Cloud announced the general availability of Spanner queues, which let Spanner handle messages within transactions. The blog focuses on AI agent use cases, but says it can also be used for order processing and inventory workflows. We read the blog and the Spanner API specification and outline how it addresses the dual-write problem.

What happens with dual writes

The blog cites two failures that occur when state updates and message sends are handled by separate systems.

  • The database update succeeds and the message send fails: a decision is made but never carried out
  • The message send succeeds and the database update is rolled back: an action is carried out based on an invalid state

To avoid this, the blog explains, developers have built their own "outboxes" (a mechanism where messages to be sent are first written to a table in the same DB and then picked up and sent by a separate process), mechanisms to prevent duplicate processing, and processes to reconcile discrepancies.

With Spanner queues, creating a message becomes one of the writes in the same transaction. The state update and the next action to perform are either both committed or both rolled back.

Diagram showing the difference between dual writes and Spanner queues. On the left, a setup where the database update and the send to the queue are committed separately, so inconsistencies can occur where only one succeeds. On the right, a setup where the order update and inserting a message into the queue are committed together in a single transaction. The diagram is based on the explanation in Google Cloud's official blog

How to write it in SQL

Queues are table-like structures that you define and operate with Spanner's GoogleSQL. Below is the refund approval example from the blog (SQL comments summarized by the editorial team).

CREATE QUEUE OrderAgentTasks (
  OrderId STRING(64) NOT NULL,
  TaskId STRING(64) NOT NULL,
  TaskType STRING(64) NOT NULL,
  Payload JSON NOT NULL
) PRIMARY KEY (OrderId, TaskId, TaskType), INTERLEAVE IN Orders;

-- 同じ読み書きトランザクションの中で
UPDATE Orders
SET Status = 'REFUND_APPROVED',
    UpdatedAt = PENDING_COMMIT_TIMESTAMP()
WHERE OrderId = @orderId;

INSERT INTO OrderAgentTasks (OrderId, TaskId, TaskType, Payload)
VALUES (@orderId, @taskId, 'EXECUTE_REFUND',
  JSON '{"action": "execute_refund", "amount": 49.99}');

The blog explains that with this pattern, "a refund task exists only if the order status changed to approved."

The blog also shows the following patterns.

  • Delay delivery. If you set a time in the system column DeliverTime, the message is not visible to receivers until that time. The example schedules an escalation to a manager 72 hours later
  • Cancel a scheduled message. If the approval comes first, run DELETE in the same transaction as the state update to delete the scheduled message
  • Receive. The consumer reads a function called RECEIVE_<キュー名> as a stream and borrows messages for a set period of time (a lease). For long-running work, it extends the lease with RENEWLEASE_<キュー名>
  • Record completion. Perform the result write and the message's DELETE in a single transaction, adding ASSERT_ROWS_MODIFIED 1. If the lease expired and another process has already handled the message, this returns an error, preventing an old process from overwriting the newer state

Completing "exactly once" is up to you

The blog's wording is that it "guarantees at-least-once delivery and at-most-once acknowledgment (ACK), which together enable exactly-once processing." This assumes the same message may be received twice. When calling an external API, the blog shows an example of passing TaskId as the recipient's idempotency key (a value that lets the same request be treated as a single request even if it is received twice). For processes where duplicate execution is a problem, such as payments and shipping, this design is essential.

What to check before using it

When to use change streams instead

Spanner also has change streams, which capture data changes. The blog distinguishes them: change streams are for continuously ingesting change data into analytics and storage destinations, while queues are for executing tasks that involve leases, scheduled delivery, and recording completion within a transaction.

Client library support

In the Spanner API v1 specification (Discovery document, revision 20260909), Mutation, the unit of writes, includes send and ack for queues. send can specify deliverTime, and ack can treat acknowledging a nonexistent message as successful via ignoreNotFound.

The CHANGELOGs of the official libraries for each language list support for "Send and Ack" in the following versions.

  • Go: 1.87.0 (December 10, 2025), 1.88.0 (February 11, 2026)
  • Java: 6.109.0 (February 2, 2026)
  • Python: 3.60.0 (December 10, 2025)

For Node.js, the CHANGELOG in the nodejs-spanner repository on GitHub stops at 8.6.0 (January 28, 2026) and has no relevant entry. However, the type definitions of @google-cloud/spanner 9.0.0 published on npm (published September 18, 2026) included queueSend and queueAck (which can specify ignoreNotFound) (checked on October 5).

What the official blog does not cover

The blog does not mention eligible editions, pricing, per-queue limits, or whether it is available in Spanner Omni (the version that runs in your own environment). The editorial team could not open the Google Cloud documentation from our environment and has not been able to verify these points. If you are considering adoption, check the documentation.

Considerations for existing systems (editorial team's analysis)

If you already run business systems on PostgreSQL or a similar database, you can also avoid dual writes by placing an outbox table in the same DB. Queues alone are unlikely to justify moving to Spanner. We discuss how far a single DB can take you before adding more in deciding to rely on a single Postgres instance.

Spanner queues are useful when your architecture already uses Spanner, or will, and you want to eliminate an external queue and the relay process for an outbox. The option of running Spanner in your own environment is covered in our article on Spanner Omni.

On October 3, 2026, we opened and cross-checked the official Google Cloud blog post "Announcing Spanner queues: Transactional messaging for agentic workloads and beyond" (October 2, 2026), the Discovery document for Spanner API v1 (revision 20260909), and the CHANGELOGs of googleapis' google-cloud-go (spanner), java-spanner, python-spanner and nodejs-spanner. On October 5, we also checked the type definitions of @google-cloud/spanner 9.0.0 on npm. The editorial team could not open the Spanner documentation (docs.cloud.google.com) from its environment, so editions, pricing and limits have not been checked. We have not tested behavior on an actual Spanner instance or the emulator.

For help with data design for business systems or reviewing how your database integrates with external services, please contact us through consultation on development, AI, and automation.

Sources

Share this articleXFacebook
Kakeru Suzuki

Fascinated by the possibilities of technology, has had a deep interest in programming and digital art since student days

Turn this article's theme into your company's next step

Concrete steps forward for your organization.

We organize your desired architecture, legacy systems, and operational requirements to formulate your next steps toward execution.

  • Desired architecture
  • Integration with existing environments
  • Operational requirements
Consult on development & operations initiatives

You can consult with us from the initial conceptual stage. Details from this article will be carried over to the inquiry form.

Receive the latest articles by email