Spanner’s PostgreSQL interface reduces language and driver friction; it does not turn Spanner into PostgreSQL. A syntactically successful migration can still fail on key distribution, transaction contention, unsupported features, and missing retry logic.

Situation

A workload needs horizontal write scaling or strong transactions across data that cannot remain on one PostgreSQL primary. Spanner is evaluated because it distributes data and compute while offering a PostgreSQL dialect and compatibility with supported PostgreSQL clients through PGAdapter.

That interface is useful, but the storage engine, transaction manager, administration surface, and observability remain Spanner. The migration is a data-model and application-semantics project, not a binary upgrade.

The Problem

PostgreSQL instincts can point in the wrong direction. There is no host buffer cache to tune, no PostgreSQL WAL archive to manage, and no complete pg_catalog or extension ecosystem. Yet it is equally wrong to say that TrueTime “replaces locks.” Spanner read-write transactions can use locks, and its documented concurrency behavior includes wound-wait deadlock prevention. TrueTime supplies bounded time uncertainty used in commit ordering and external consistency; it does not remove contention.

The core question is: which PostgreSQL assumptions survive the interface, and which must be redesigned for a distributed database?

The Spanner Mental Model

flowchart TD
    Driver[Supported PostgreSQL driver] --> PGAdapter[PGAdapter]
    PGAdapter --> API[Spanner API]
    API --> SplitA[Key-range split A]
    API --> SplitB[Key-range split B]
    SplitA --> ReplicaA[Replicated storage group]
    SplitB --> ReplicaB[Replicated storage group]
    Clock[TrueTime] --> API

PGAdapter translates the PostgreSQL wire protocol to the Spanner API. It can run as a sidecar or, for Java, in-process. The PostgreSQL interface implements a documented subset of PostgreSQL syntax, types, functions, catalogs, drivers, and ORMs. Extensions, user-defined types, user-defined stored procedures, and some tools are not supported. Administration remains a Google Cloud and Spanner control-plane task; pgAdmin is not the service console.

Spanner partitions data into key ranges called splits and replicates them. A sequential leading key can concentrate new writes at one end of the keyspace. Avoiding hotspots may require UUIDs, bit-reversed sequences, or a schema-specific distributed key—not a blanket rule to hash every key, because locality can also be valuable.

Read-write transactions can span rows, tables, and splits. That is the feature teams are paying for, but distributed coordination and contention still affect latency. Client libraries can retry aborted transactions when work is supplied through the transaction runner; custom transaction handling must preserve idempotency and respect retry semantics.

In Practice

Start migration assessment with incompatibility, not throughput:

  1. Inventory extensions, triggers, stored procedures, sequences, isolation assumptions, ORM features, and administrative tooling.
  2. Extract the highest-write key ranges and use Key Visualizer or workload tests to expose concentration.
  3. Identify transactions that touch many rows or splits and determine whether their atomic boundary is essential.
  4. Test supported drivers through PGAdapter, including prepared statements, pooling, authentication, and error handling.
  5. Replace PostgreSQL-specific monitoring with Spanner query statistics, transaction statistics, lock statistics, and Key Visualizer.

Transaction limits require nuance. Spanner documents a 100 MiB mutation-commit size limit. The 80,000 mutation limit applies to commits through the mutation API; DML has a limit of 80,000 mutations per statement. A transaction can still become operationally poor before reaching a documented maximum because it holds resources, increases contention, and expands retry work. Batch large maintenance jobs and make progress resumable.

Schema changes are managed by Spanner and can run as long-running operations. Do not infer that every DDL statement is instant or transactional with application writes. Review the current PostgreSQL-dialect DDL support and test migration tooling against a representative schema.

The go-live gate should include a contention experiment, not only a throughput test. Drive concurrent updates at one deliberately hot key and at the proposed production key distribution. Compare abort rate, retry count, commit latency, and total work completed. Then inject a retryable abort into the client path and confirm that the transaction body is safe to execute again. A driver that retries transport calls is not sufficient if the surrounding application also emits a message or invokes an external service.

Finally, test locality assumptions with production-shaped keys. A random identifier can distribute writes while making range scans or parent-child access more expensive. Key design is an optimization problem across distribution and locality, so the architecture record should include both the hotspot evidence and the access patterns the chosen key preserves.

Where It Breaks

Failure modeEvidenceResponse
Hot leading keyKey Visualizer shows concentrated accessRedesign the leading key while preserving required locality.
Repeated ABORTED transactionsTransaction and lock statistics show contentionShorten transactions, order access consistently, and use supported retry runners.
Migration compiles but semantics differUnsupported-language and known-issues reviewBuild a feature inventory and semantic test suite before data movement.
Large write transaction fails or retries expensivelyMutation count, commit size, durationBatch work with restartable checkpoints and bounded transactions.
Familiar DBA tooling is blindMissing PostgreSQL views or control functionsAdopt Spanner-specific monitoring and incident runbooks.

What to Do Next

  • Problem: PostgreSQL compatibility is being mistaken for PostgreSQL architecture and administration.
  • Solution: Treat the interface as a migration aid over Spanner’s distributed transaction and storage system.
  • Proof: Google documents PGAdapter translation, partial language support, split-based storage, locks, wound-wait behavior, and explicit transaction limits.
  • Action: Run a schema-and-transaction compatibility review, then load-test key distribution and retry behavior before selecting Spanner.

Sources to Verify