Stop waiting for a spread checkpoint, and start the standby on demand ## Why A read replica cost far more than taking a copy of a small database should, and it showed up somewhere the amortised measurement could not see it. In a downstream project, a mutation check that instruments one library and reruns its suite once per mutant went from minutes to **hours**, at a load average of 0.5 on 64 cores. It was not computing. It was waiting for postgres, thousands of times over. Measured on that project's smallest database suite, the fixed cost of a run that executes **zero tests**: | | fixed setup | |---|---| | without a replica | 2.95s | | with a replica | 16.5s | Amortised over a 514-test suite, those 13.5 seconds are invisible, which is exactly why the original change shipped with "45s → 125s" as the measurement and thought that settled it. Per mutant, where the denominator is a handful of tests, it is the whole cost. ## What was actually slow **`pg_basebackup` waits for a checkpoint, and by default spreads it.** A spread checkpoint is paced over `checkpoint_completion_target` of the checkpoint interval, so the wait is proportional to how busy the server is — and a server you are taking a standby from is busy by definition, because a suite is running against it. `--checkpoint=fast` asks for the checkpoint immediately, which is what it is for. This was most of the cost. **This package's own suite goes from 78 seconds to a little over one.** It also showed up as a hang rather than a slowdown once the standby started mid-suite instead of before it: the busier the primary, the longer the spread checkpoint, and the base backup simply sat there. ## What was unnecessary **The standby was started whether or not anything read from it.** `replicatedDBStandby` is now an `IO Standby`, started by the first test that asks for one and by nothing else, so a suite pays for a replica only if it reads from one. The bracket became an acquire/release pair (`acquireStandby`) so that starting could be deferred, and `lazyStandbySetupFunc` memoises it behind an `MVar`: one standby however many tests ask concurrently, stopped with the suite only if it was ever started. ## Breaking `replicatedDBStandby :: Standby` becomes `replicatedDBStandby :: IO Standby`. Anything reaching into a `ReplicatedDB` for its standby runs the action instead. `postgresqlStandbySetupFunc` is unchanged. ## Tests `StandbySpec` asks the primary what is streaming from it, which is the one place the answer is not a matter of opinion: ```haskell streaming `shouldReturn` 0 -- nothing until something asks standby <- getStandby standby' <- getStandby standbyDataDirectory standby' `shouldBe` standbyDataDirectory standby -- and one, not one per ask ``` The existing four tests still pin that it is a real replica: a write is invisible while it is behind, visible once caught up, and refused through the standby by postgres itself.