AlloyDB is Google Cloud's PostgreSQL-compatible database, and the word "compatible" hides where all the interesting engineering lives. The front half is a real PostgreSQL engine — same wire protocol, same MVCC, same planner lineage, same drivers and pg_dump. The back half has been replaced: instead of writing data blocks to a disk, the primary writes log records to a regional storage service that understands PostgreSQL block format and materialises the blocks itself. That single substitution is what changes recovery behaviour, what makes read replicas cheap to add, and what an in-memory columnar engine is then bolted onto for mixed transactional and analytical work. This article walks the architecture layer by layer, and is honest about where it stops being magic.

The cluster and instance model — what you actually provision

Cloud SQL asks you to create an instance, and the instance owns the data: delete it and the disk goes with it. AlloyDB inverts that. The top-level object is a cluster, and the cluster owns the storage, the databases, and the backups. Compute is attached to the cluster as separate instances: exactly one primary instance (read-write), zero or more read pool instances (read-only), and, in a cross-region topology, a secondary cluster with its own instances. Removing a read pool removes compute, not data.

You never size a disk. There is no volume type, no provisioned IOPS tier, no high-water-mark autogrow decision — storage is a regional service that grows as the data grows and is replicated across three zones by default. That deletes an entire class of operational mistake familiar to anyone who has run PostgreSQL on cloud block storage, where throughput is a function of a disk size you guessed at provisioning time and cannot easily shrink afterwards.

What you do size is compute: vCPU and memory per node, chosen per instance, so a reporting read pool can be shaped differently from the primary. Billing follows the same split — node-hours per instance, plus storage consumed, plus backup storage. The practical consequence is that scaling reads and scaling writes are now independent purchases, which is the main reason the model exists.

Advertisement

Disaggregated storage — the log is the database

Stock PostgreSQL has a well-known write path. A transaction dirties pages in shared buffers, appends redo records to the WAL, and commits once the WAL is durable. Later, a checkpointer flushes dirty pages to the data files. Because a torn page written during a crash would be unrecoverable, PostgreSQL also writes a full copy of each page into the WAL the first time it is dirtied after a checkpoint — the full_page_writes tax. The result is a system whose latency profile is punctuated by checkpoint-driven IO storms and whose write amplification is substantially larger than the logical bytes you changed.

AlloyDB keeps the WAL and throws away the rest of that path. The primary's commit durably lands log records only, into a low-latency regional log store replicated across zones. It does not write data blocks, and it does not run the checkpoint-to-disk cycle that stock PostgreSQL depends on. Data blocks live in a separate, zone-redundant block storage tier built on Google's cluster filesystem — the same lineage as Colossus, which also underpins Cloud Storage and BigQuery.

The mental model worth carrying is that the log is the database and the blocks are a materialised view of it. Durability is established the moment the log record is safe in multiple zones; block state is derived, asynchronously, by something that is not the primary. Everything else in this article is a consequence of that sentence.

The log processing service — replay as a continuous background job

The component that derives blocks from the log is the log processing service. It is a storage-side fleet that consumes the WAL stream and applies redo to blocks, sharded so that different shards own different ranges of the block space and replay in parallel. Because the storage layer is database-aware — it knows what a PostgreSQL page is and what a redo record means — it can also serve a read of a block whose outstanding log records have not yet been applied, by applying them on demand at read time.

Three familiar PostgreSQL pains change shape here. Full-page writes stop being necessary, because the storage tier applies redo itself rather than relying on the engine to protect against partially written pages, which removes a large slice of write amplification. Checkpoint stalls stop existing on the primary, because the primary has no data files to flush. And crash recovery stops being a long serial replay of everything since the last checkpoint, because replay was never deferred — it has been running continuously all along.

The fourth consequence is the least obvious and the most valuable: replay happens once, in storage, for the whole cluster. In a classic primary-plus-replicas topology, every replica independently replays the same WAL stream, and each one is capable of falling behind on its own. Moving that work below the compute layer means adding a reader does not add another copy of the replay problem.

AlloyDB — stateless compute over database-aware, log-driven storagePrimary instance (read-write)shared buffers + ultra-fast cache+ in-memory columnar engineRead pool instances (N nodes)no private copy of the data —same cluster storage, own cachesRegional log storageWAL records, multi-zone durableLog processing servicecontinuous, sharded WAL replayBlock storage — replicated across three zones in the regionblocks are materialised from the log: no checkpoint writes, no full-page writescommit path: log records onlystreamapply redo to blocksblocks +invalidationsFailover replaces compute over surviving storage; readers never replay their own WAL stream.
AlloyDB's separation of concerns: the primary commits log records, a storage-side log processing service turns them into blocks, and every reader shares those blocks.

Caching — shared buffers, ultra-fast cache, and the cost of a cold node

Once blocks live across a network, cache hierarchy becomes the dominant performance variable. AlloyDB compute nodes hold the usual PostgreSQL shared buffers in RAM, and underneath them a second-level block cache on local NVMe SSD — marketed as the ultra-fast cache. A read that misses both tiers becomes a request to the regional storage service. That is a fast request, but it is a network request, and the difference between an in-memory working set and one that spills is more visible here than on a machine with attached local disks.

Sizing therefore stops being "how many vCPUs do my queries need" and becomes a joint question about vCPU, memory for shared buffers, memory for the columnar engine, and how much of the hot set the local cache can hold. Two instances with identical CPU can behave very differently if one holds the working set and the other misses to storage on every index descent.

The corollary is that every compute change starts cold. A new read pool node, a machine-type change, a restart, or a failover all begin with empty shared buffers, an empty local cache, and — as the next sections cover — an unpopulated columnar store. Throughput ramps over minutes rather than appearing instantly. Plan capacity changes ahead of a traffic peak, not during one, and do not judge a resized instance by its first five minutes.

Read pools — replicas that share storage instead of shipping WAL

A read pool instance is not a single replica; it is a set of nodes behind one endpoint, with connections distributed across the nodes. You scale a pool by changing its node count, and you can run several pools with different shapes — a small low-latency pool for an API, a larger pool for reporting — so a heavy analyst query cannot evict the API's cache.

The architectural difference from a Cloud SQL read replica matters. A Cloud SQL replica receives an asynchronous WAL stream and maintains its own complete copy of the data on its own disk; its lag is bounded by how fast it can apply that stream, which is why a bulk write on the primary can push replicas minutes behind. An AlloyDB read pool node reads the cluster's blocks. What it needs from the log is not the data but consistency information: which cached blocks are now stale, and what read point it is allowed to serve.

So adding a node is adding compute, not copying a database — provisioning is quick and does not multiply storage cost per reader. Lag is still real, because reads are still asynchronous relative to primary commits, so read-your-own-writes flows must still go to the primary; but the failure mode of a replica falling arbitrarily far behind under write pressure is structurally weaker. Note also what read pools are not: they share the same regional storage as the primary, so they are a scale and isolation mechanism, not a disaster-recovery one.

Failover and recovery — what separation actually buys

Compare the two failure paths directly. In Cloud SQL's regional HA design, the standby is cold: on failover it attaches the regional disk, starts the engine, and runs ordinary crash recovery, whose duration depends on how much WAL sits between the last checkpoint and the failure. That is a variable you cannot fully control, and it is why the honest recovery-time story there is "a minute or two, sometimes more".

AlloyDB removes that variable. There is no WAL tail to replay on the way up, because the log processing service has been applying continuously and the surviving storage already holds materialised blocks. Recovering the primary is a matter of starting compute and pointing it at storage that is already consistent, which is why the published recovery targets are shorter and, more importantly, less dependent on how busy the database was at the moment it died.

It is still not transparent. Clients see connections drop; there is a window where writes are refused; and the new primary comes up with cold caches and an unpopulated columnar store, so the first minutes after recovery are slower than steady state. Applications need the same discipline they always needed: jittered reconnect backoff, a connection pooler so the recovering node is not met by a stampede, circuit breakers on the write path, and connections established through the Auth Proxy or a language connector with IAM identities rather than IP allowlists. Architecture shortens the outage; it does not remove the need to tolerate one.

Advertisement

The columnar engine — analytics inside a single Postgres process

The second headline feature is the columnar engine: an in-memory column-oriented copy of selected tables and columns, maintained on the compute node alongside the ordinary row store, with its own slice of instance memory. It is enabled as an extension and configured with a memory budget, which is real memory taken from the same node that is serving transactions.

What makes it more than a cache is planner integration. The optimiser is extended with columnar scan paths and vectorised execution, and a single query plan can mix them with row-store access — a columnar scan for the large filtered aggregate, an index path for the selective lookup. Nothing about the application changes: the same SQL, over the same tables, in the same transaction-consistent database.

Population can be manual — you nominate tables or columns — or automatic, where the engine observes the workload and chooses what to keep in columnar form. The store is maintained as rows change rather than rebuilt from scratch, but it is memory-resident, so it must be repopulated after a restart or a failover. That is the third reason cold compute is slow, and the reason a plan that was columnar yesterday can be a row scan an hour after an unplanned restart.

The genuine architectural claim here is HTAP without ETL: analytics run on live operational data, with no export pipeline, no second system, and no staleness window — paid for in memory on the transactional node.

Where the columnar engine stops helping

Because the store lives in memory, its coverage is bounded by the budget you gave it. If the columns you want do not fit, coverage is partial and queries silently fall back to row-store plans; the failure is a performance cliff, not an error, so it needs monitoring rather than faith. Highly volatile tables are also more expensive to keep current than append-mostly ones, so the best candidates are large, frequently scanned, and comparatively stable.

It also helps a specific shape of query. Scans, filters, aggregations and large joins benefit; point lookups, index-nested-loop OLTP and write throughput do not — those are row-store paths, and adding columnar coverage takes memory away from shared buffers that were serving them. On a node under memory pressure, the columnar engine can make transactional latency worse.

Most importantly, it is not a data warehouse. There is no separation of storage and compute for the analytical side, no petabyte-scale scan capability, and no independent concurrency pool: analysts and transactions share one instance. When reports scan the full history rather than the operational working set, the right home is BigQuery, fed by change data capture through Datastream, with BI Engine playing the analogous in-memory acceleration role on that side. The useful rule of thumb: the columnar engine is for analytics on the operational working set, and everything beyond that belongs downstream.

PostgreSQL compatibility and its limits

Compatibility is not emulation. AlloyDB runs a PostgreSQL engine, tracks recent major versions, and speaks the PostgreSQL wire protocol, so psql, pg_dump, JDBC and every ORM behave normally. Index behaviour, planner quirks, and the material in a general PostgreSQL reference such as PostgreSQL indexes carry over unchanged.

The limits are the usual managed-service ones. You do not get true superuser; you get a privileged role, which rules out anything requiring filesystem access or arbitrary C extensions. Extensions come from a curated supported list, and extension availability — not SQL syntax — is the most common migration blocker, so check the list against your schema before planning a cutover. Storage-level knobs are meaningless or fixed here: tuning checkpoints or full-page writes is not something the architecture leaves to you.

What has emphatically not gone away is PostgreSQL's MVCC bookkeeping. Dead tuples, table and index bloat, long-running transactions holding back the cleanup horizon, and transaction-ID wraparound pressure are all still yours to manage, even though vacuum tuning is more adaptive than stock. Anyone who arrives expecting storage separation to have deleted vacuum will be surprised at the worst possible moment.

On the AI side, the vector story is the standard pgvector plus a Google-supplied ScaNN-based index for approximate nearest-neighbour search, and an ML integration extension that lets SQL call models hosted in Vertex AI. Migration in is usually dump-and-restore for small databases, or a logical-replication-based migration for low downtime — the mechanics of which are covered in PostgreSQL logical replication.

Backup, continuous backup and PITR, and cross-region clusters

Backups are cluster-scoped and independent of compute, which follows directly from the storage model: deleting instances does not touch them. Alongside scheduled and on-demand backups, AlloyDB keeps continuous backup — the retained log archive — which is what makes point-in-time recovery possible to any moment inside the retention window. Retention is configurable in days up to a documented maximum; treat the specific ceiling as a number to confirm in the current documentation rather than one to memorise.

Every restore creates a new cluster. That is a feature, not a limitation: the recovery workflow for a bad migration is to restore a clone to the minute before the mistake, extract or compare the damaged objects, reconcile the delta from application logs, and merge back, leaving the production cluster serving traffic throughout. It is also the cheapest way to test a risky migration: run it against a PITR clone first.

For region loss, the mechanism is a secondary cluster in another region, kept current asynchronously, able to host its own read pools, and promotable to a standalone primary. Promotion carries a non-zero RPO and a runbook's worth of RTO, exactly as cross-region replication does everywhere else. And it protects against nothing self-inflicted: an UPDATE that forgot its WHERE is replicated faithfully within seconds, because a secondary cluster's job is to reproduce the primary's state, mistakes included.

AlloyDB vs Cloud SQL for PostgreSQL — and when neither is the answer

Cloud SQL is the multi-engine, instance-plus-disk service: MySQL, SQL Server and PostgreSQL, regional-disk HA with a cold standby, WAL-streaming read replicas, a low price floor, and small shapes for small databases. AlloyDB is PostgreSQL only, cluster-plus-compute, with disaggregated storage, read pools, a columnar engine, and a meaningfully higher entry cost because the minimum sensible compute is larger.

Stay on Cloud SQL when the database is modest, cost sensitivity is real, another engine is required, or you depend on an extension AlloyDB does not support. Move to AlloyDB when you are outgrowing a single PostgreSQL node on the read side and want many low-lag readers without cloning storage per replica; when checkpoint IO, vacuum storms or write amplification dominate your incident history; or when reporting queries on live operational data are the thing forcing an unwanted second system.

Neither is right when the constraint is horizontal write scale-out or globally strong consistency across regions — that is Spanner, whose PostgreSQL interface is a dialect rather than a PostgreSQL engine and whose TrueTime foundations solve a different problem. Nor is either right when the workload is genuinely analytical at warehouse scale.

Finally, the marketing. Google publishes comparative figures — claims on the order of several times faster than standard PostgreSQL for transactional work and up to two orders of magnitude for analytical queries. Those are vendor benchmark claims under chosen conditions, not measurements of your schema, and the analytical multiplier in particular describes queries that the columnar engine happens to fit. Migrate on the basis of a benchmark of your own workload, with your own data volume and concurrency; the architectural arguments above are the durable reasons, and they are stated in terms of failure modes rather than multipliers for exactly that reason.

AlloyDB is PostgreSQL with its storage layer replaced: the primary commits log records, a storage-side log processing service continuously materialises blocks across three zones, and every read pool node shares those blocks instead of replaying its own WAL. That is what makes recovery time independent of write volume and makes readers cheap to add. The in-memory columnar engine then buys analytics on live operational data — bounded by the memory you give it and by the fact that it is not a warehouse. MVCC, vacuum and cold-cache warm-up are all still yours; choose it for the failure modes it removes, not for a vendor multiplier.