By: Tom Sydney Kerckhove <syd@cs-syd.eu>
Tell the primary what the standby's readers still need The standby was configured to serve reads and to stay deliberately behind, and then left to postgres' defaults on what should happen when recovery meets one of those reads. The defaults are that the primary is never told what a standby reader is holding, so it is free to vacuum those row versions away, and that replaying the cleanup cancels the reader thirty seconds after the conflicting record arrived. So a read here could be destroyed by a vacuum on the primary, through no fault of the application, and come back as SQLSTATE 40001 or as a dropped connection. hot_standby_feedback stops that at the source: the standby reports the oldest snapshot its readers need and the primary keeps those row versions. The base backup already takes a replication slot, which is what the feedback is recorded against, so there was nowhere else it needed to go. wal_receiver_status_interval has to come down with it, because the default of ten seconds is longer than the databases here live, and feedback that arrives after the vacuum protects nothing. Feedback closes the window rather than the door. It cannot retract a cleanup already in flight, and it says nothing about conflicts that are not about row versions, such as a lock that replayed DDL wants. The delay is also measured from when the record arrived rather than from when the query started, so a standby that is already behind can have spent the whole budget before a query begins, and the query is cancelled at once rather than waited for. Recovery waiting is the right answer to all of that here: a replica in a test suite is there to be behind, and a test that wants it caught up says so with awaitReplica. The regression test holds a REPEATABLE READ snapshot on the standby, updates every row on the primary and vacuums away the versions that snapshot needs, then reads again through the same transaction. Read committed would not do: it takes a new snapshot per statement and lets the old one go, so nothing would be held across the cleanup and the conflict would never arise. It takes about ninety seconds without the fix, and about one with it, because no conflict arises to spend the delay budget on. Feedback alone is enough for that test. The two delays are for the conflicts it does not cover.
Total
3m11s
Created Started Finished
| Time to Start | Worker time | Duration | Time to finish | Idle | |
|---|---|---|---|---|---|
| Config | 0s | 0s | 0s | 1s | 0s |
| Eval | 0s | 2m06s | 2m06s | 2m07s | 0s |
| Build | 2m07s | 0s | 1m03s | 3m11s | 1m03s |
| Suite | 0s | 2m07s | 3m11s | 3m11s | 1m03s |