Scaling PostgreSQL Reads with AlloyDB: Read Pools, Columnar Execution, and TQF
Read scaling fails when “add replicas” is treated as one mechanism. Capacity, routing, consistency, query acceleration, and replica lifecycle are separate decisions—even when one service exposes them behind a single cluster.
Situation
A PostgreSQL application mixes short transactions with expensive scans and aggregates. Traditional physical replicas can isolate reads, but the application must route traffic, accept asynchronous visibility, and manage recovery conflicts. AlloyDB offers read pool instances attached to distributed storage, an optional columnar engine, and newer routing and autoscaling features.
This article is dated after July 23, 2026 because Google introduced Transparent Query Forwarding in Preview on that date. As of September 26, 2026, both Transparent Query Forwarding and read-pool autoscaling must still be treated as Preview capabilities and revalidated before production use.
The Problem
The original version of this architecture incorrectly placed Transparent Query Forwarding in front of the cluster as a general proxy. Google documents a different behavior: the primary instance intercepts eligible read-only queries and can forward them to an in-region read pool while preserving read-your-writes consistency. Ineligible queries fall back to the primary.
That distinction changes capacity planning. Enabling TQF does not mean all reads leave the primary, and it does not route writes. The core question is: which reads should use an explicit read-pool endpoint, which are eligible for Preview forwarding, and which should remain on the primary?
The AlloyDB Read Path
flowchart TD
App[Application] --> Primary[Primary instance]
App --> Reader[Explicit read-pool endpoint]
Primary --> Eligible{TQF eligible read}
Eligible -->|Forwarded| ReadPool[In-region read pool]
Eligible -->|Fallback| Fallback[Execute on primary]
Reader --> ReadPool
Primary --> Storage[Distributed storage]
ReadPool --> Storage
ReadPool --> Columnar[Optional columnar engine]
Read pools contain one or more read-only nodes behind a load-balanced endpoint. They use the cluster’s distributed storage rather than maintaining independent storage volumes in the same way as conventional physical PostgreSQL replicas. Nodes still have local memory and caches, so adding capacity can introduce cold-cache behavior.
The columnar engine is independent of routing. It can populate selected columns and accelerate eligible scans, joins, and aggregates on the primary or read-pool instances. A query reaching a read pool does not guarantee columnar execution; verify its plan and column coverage.
Transparent Query Forwarding boundaries
Current documentation requires PostgreSQL 17 or 18, at least one active read pool, and explicit enablement. TQF applies only to eligible read-only SELECT statements. Queries using row locks, temporary or unlogged tables, catalogs, volatile or user-defined functions, some transaction patterns, or cheap index scans can be disqualified. It does not forward to a secondary cluster.
Enable it deliberately for a database’s new connections:
ALTER DATABASE logistics
SET alloydb.enable_query_forwarding = ON;
Use EXPLAIN to see the query-forwarding eligibility annotation. Monitor the documented TQF metric by status—completed, fallback, and disqualified—so capacity estimates use the observed forwarding ratio rather than the total read count.
In Practice
Use three routing classes:
- Primary-bound: writes, lock-taking reads, latency-sensitive point lookups, and transactions that depend on primary session state.
- Explicit reader: reporting services that can connect directly to the read-pool endpoint and have a defined consistency contract.
- TQF candidate: expensive eligible reads from an application that cannot yet split endpoints and requires read-your-writes behavior.
For each class, record latency, primary CPU, read-pool CPU, storage reads, columnar execution, and result-visibility requirements.
Read-pool autoscaling, currently Preview, adjusts node count within configured bounds based on documented signals and policies. It is not instantaneous infinite capacity. New nodes need provisioning and cache warm-up; minimum nodes determine steady cost and immediate headroom. Exercise a burst larger than normal traffic and observe queueing before relying on the policy.
Roll out Preview routing in observable stages. First enable TQF for one database or controlled connection pool, then compare completed, fallback, and disqualified counts with the query inventory. Set a primary-CPU rollback threshold and verify that disabling the database setting affects new connections as documented. Finally, test a read-pool outage and a primary failover: fallback behavior may preserve correctness while still violating latency objectives if the primary lacks headroom for the returned reads.
Keep the explicit reader endpoint as an architectural option even if TQF performs well. It gives reporting services a visible capacity boundary, isolates connection budgets, and avoids making a Preview eligibility decision part of every query’s latency path.
Where It Breaks
| Failure mode | Cause | Control |
|---|---|---|
| Primary remains saturated after TQF | Most reads are ineligible or too cheap to forward | Use TQF status metrics and explicit reader endpoints where possible. |
| Read pool scales but latency rises | New nodes start with cold caches | Maintain headroom and ramp traffic while monitoring tail latency. |
| Reporting query uses row execution | Required columns or operations are not covered | Inspect plans and column-store recommendations. |
| Preview behavior changes | Pre-GA terms permit limited support and change | Gate rollout, pin assumptions in the ADR, and reverify before launch. |
| Read pool becomes a correctness bug | Application never defined visibility semantics | Keep correctness-sensitive paths primary-bound and test read-after-write. |
What to Do Next
- Problem: Read scaling is being treated as a replica-count setting rather than routing, execution, and consistency design.
- Solution: Classify reads, use explicit endpoints first, and adopt TQF or autoscaling only with Preview governance.
- Proof: Google documents the primary-side forwarding decision, strict eligibility rules, status metrics, and Preview release stage.
- Action: Replay a production trace, calculate the observed eligible-forwarding ratio, and load-test scale-out before changing application routing.