PlanetScale разобрали механику. У каждого процесса есть кэш идентификаторов вложенных транзакций, обычно на 64 записи. Переполнился — снимок помечается переполненным, и проверка видимости каждой строки идёт через служебную структуру
pg_subtrans: блокировка, а при промахе ещё и диск.Вложенные транзакции при этом появляются сами: их создаёт каждый блок PL/pgSQL с секцией
EXCEPTION, а не только явный SAVEPOINT.Вторая беда приходит позже: записи о работающих транзакциях уезжают в журнал неполными, и новая реплика не может построить снимок. Она проигрывает журнал, но соединения не принимает — то есть поднятая на пике нагрузки реплика не даёт никакой ёмкости.
Следить можно через
pg_stat_get_backend_subxact() и счётчики pg_stat_slru. Готового способа предотвратить переполнение в PostgreSQL 18 нет.@prog_stuff
