Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →To make a PostgreSQL asynchronous replica show a write made moments earlier by PHP, capture a WAL location that covers the committed write on the primary, send it to the replica, run WAIT FOR LSN in standby_replay mode, and read only if the wait succeeds. This provides read-your-writes consistency for that request; it does not eliminate replication lag or guarantee that every replica read is current.
What WAIT FOR LSN guarantees
PostgreSQL 19’s WAIT FOR LSN can pause a standby until it reaches a specified WAL position. For a query that must see a recent write, use the default standby_replay mode: it waits until the WAL at the target position has been replayed, or applied, on the standby. On success, pg_last_wal_replay_lsn() is at least the requested LSN.
As an Amazon Associate I earn from qualifying purchases.
The target matters as much as the wait. PostgreSQL’s documentation says the target must be at or after the end of the relevant transaction’s COMMIT record on the primary. Otherwise, the standby can successfully reach the requested position without having applied the write you care about. See the PostgreSQL 19 WAIT documentation for the command’s semantics and constraints.
Choose the mode for the result you need
| Mode | Waits for | What it means for reads |
|---|---|---|
standby_replay |
WAL replayed and applied on the standby | Use this for read-your-writes query visibility. |
standby_flush |
WAL flushed to durable storage on the standby | Durability is not the same as application; a query may not yet see the changes. |
standby_write |
WAL written to the standby’s operating-system buffers | Weaker still; it does not promise the changes are query-visible. |
primary_flush |
WAL flushed on a primary | Not a standby visibility wait. |
Standby modes require a server in recovery; primary_flush requires a primary. PostgreSQL’s official read-your-writes pattern is to commit on the primary, obtain a suitable LSN, pass it to the client or connection-pooler layer, wait on the standby, and then read. The documentation’s example uses pg_current_wal_insert_lsn() to account for synchronous_commit possibly being off.
#1 Best Overall
A practical PHP request flow
After the write transaction commits on the primary, capture and carry the LSN as text. For synchronous commit, the PHP article discussed below uses pg_current_wal_flush_lsn(); when synchronous_commit is off, it recommends an insert LSN because the flush position may still precede the commit record. Select a position that covers the transaction’s commit record, then wait on the replica before issuing the read.
-- Primary: after the write transaction has committed (synchronous commit on)
SELECT pg_current_wal_flush_lsn();
-- Replica: outside a transaction, before the read
WAIT FOR LSN '0/0306EE20' WITH (MODE 'standby_replay', TIMEOUT '50ms', NO_THROW);
-- Only if WAIT reports success:
SELECT ...;
The LSN above is illustrative syntax, not a value to reuse. In PHP, route to the replica only when the command returns success; otherwise use an explicit fallback policy such as reading from the primary, retrying within a bounded budget, or returning a consistency delay. A successful wait proves the requested LSN was reached, not that an incorrectly chosen LSN covered the write.
Rank #2
Four pitfalls to handle
1. Native PDO placeholders may not work in WAIT
Szj’s PHP article reports that native PDO prepared statements reject a parameter placeholder in this utility statement. PostgreSQL documents the SQL syntax, but does not specify a PHP driver’s binding behavior, so treat this as a PDO-specific report rather than a universal driver rule. The article’s workaround validates the LSN as uppercase hexadecimal digits, a slash, then hexadecimal digits, and only then interpolates it into the SQL. Never interpolate arbitrary input. The article also mentions emulated prepares, but its shown sample uses validation.
Free tools Windows power users keep installed
One-click scans. No signup required.
2. WAIT must run before a transaction snapshot or held locks
WAIT must be a top-level command: it cannot run inside a function, procedure, or DO block. It cannot run while the session holds a snapshot, and it may be rejected if the session holds a lock while the requested position has not yet been reached. Run it outside a transaction block, or as the transaction’s first statement, before statements that take locks. A lock can otherwise hold up replay while the waiting session itself blocks on replay, a cycle ordinary deadlock detection does not break. Calling it before opening a transaction is the safer PHP request pattern.
A test may not reveal this restriction if the replica has already reached the target and the wait returns immediately; it becomes relevant when a wait is actually needed.
3. An insert-LSN page-boundary timeout was reported, but its cause is unproven
In an idle test, Szj reports 5 timeouts among 5,000 waits using an insert LSN. The observed target positions ended at offset 0x18 (24 bytes). The author hypothesizes that a position after a WAL page header may leave the standby waiting for future WAL, but labels this as an inference. PostgreSQL’s documentation supports use of an insert LSN in its pattern; it does not confirm this proposed page-boundary mechanism. Use a finite timeout and inspect the result rather than assuming any wait will complete promptly.
Rank #4
4. With synchronous_commit off, a flush LSN may be too early
Szj reports that in an experiment with synchronous_commit = off, waiting for the flush LSN returned success quickly but was followed by stale reads in all 300 attempts. Waiting for the insert LSN produced correct reads in that sample, with a reported median wait of 201 milliseconds and 8 timeouts among 300 attempts. These are the author’s test results, not a PostgreSQL guarantee or production forecast. The underlying issue is target selection: a wait can succeed for its LSN even when that position does not include the write’s commit record.
Bound the wait and handle its outcomes
A timeout of zero is the default and means wait indefinitely. Set a positive timeout to bound request latency. NO_THROW is useful when timeout and role-state outcomes are expected: inspect the returned status and proceed to the replica only for success. It does not set a timeout or suppress malformed-input, invalid-mode, or invalid-state errors. A promoted server can return not in recovery; promotion creates a new timeline, so reassess whether the old target describes the history you intend to read.
- Commit the write on the primary.
- Capture an LSN at or beyond the transaction’s commit record and pass it to the PHP request or routing layer.
- On the replica, execute
WAIT FOR LSNbefore opening a transaction or taking locks, with a positive timeout and (if desired)NO_THROW. - Check the result. Read from the replica only on
success; otherwise follow the application’s defined fallback or retry policy.
What the reported measurements do—and do not—show
Szj’s September 30, 2026 DEV Community article reports a test using PostgreSQL 19 Beta 4, PHP 8.5.10, and PDO. The author says the setup ran the primary, standby, PHP, and pgbench together on one vCPU, cautions that a real network adds a round trip, and recommends treating ratios rather than microseconds as the point. These figures describe that author’s setup; they are not portable latency expectations or independently verified results.
| Test reported by Szj | Reported result |
|---|---|
| Immediate reads on an idle asynchronous replica | 5,000 stale reads out of 5,000 attempts |
| Immediate reads under the author’s write load | 1,496 stale reads out of 1,500 attempts |
| Reads after adding WAIT, idle test | 0 stale reads in 5,000 attempts; 315 microseconds median reported wait |
| Reads after adding WAIT, write-load test | 0 stale reads in 1,500 attempts; 1.2 milliseconds median reported wait |
| Idle waits with insert LSN | 5 timeouts in 5,000 attempts |
| Waits with flush LSN and synchronous commit on | 0 timeouts in 5,000 reported attempts |
| Asynchronous commit experiment: flush-LSN waits | 300 stale reads in 300 attempts |
| Asynchronous commit experiment: insert-LSN waits | Correct reads in the reported sample; 201 milliseconds median wait and 8 timeouts in 300 attempts |
The PostgreSQL 19 documentation page currently marks that version as unsupported. The article’s measurements used Beta 4 and explicitly cautions that details could change before final release; verify the behavior against the PostgreSQL release and PHP driver you deploy.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitches




