====== srv-postgre.olvr: standby только для DR — ограничения collation и recovery ======
Задача: [[https://jr.olvery.ru/browse/OLV-4416|OLV-4416]]. Единственный carrier этого runbook — ''runbooks:srv-postgre-olvr''; он заменяет прежний указатель ''srv-postgre.olvr.md'' из sysmanage.
**Статус: DR-only. Standby не квалифицирован как горячая замена primary. Collation mismatch не устранён; готовность failover и приемлемый численный RTO не доказаны.**
Этот документ описывает ограничения и условную будущую recovery-операцию. Его публикация **не разрешает** promotion, REINDEX, REFRESH, изменение БД, доступов или конфигурации. Реальная операция требует назначения оператора, проверки target и отдельного разрешения владельца на production writes.
===== Targets и сохранённые факты =====
* Standby: ''srv-postgre.olvr'', ''192.168.176.9:5432'', socket ''/var/tmp/pgsql-standby''; PostgreSQL 17.9, FreeBSD 14.3.
* Primary: ''srv-prod-postgre.olvr''; установленный hostname ''pgsql-ng'', Debian 11, glibc ''2.31-13+deb11u6''.
* Сохранённый mismatch: ''datcollversion=2.31'', версия collation на standby — ''43.0''. WARNING при подключении сообщает об этом риске.
* На ''srv-postgre.olvr'' есть также тестовый инстанс: вызов psql без явного endpoint может обратиться не к standby. Для standby использовать только подтверждённый endpoint; имя host само по себе не подтверждает инстанс.
Факты — датированные результаты DESIGN YqIv/gvQSwN и принятых измерений NGw4/9jyCrC от 02.10.2026, а не новая проверка состояния сервера при чтении этого документа.
===== Ограничение чтения =====
**Не использовать standby для сверок, аналитики и любых выборок, где важна корректность результата по текстовым ключам или текстовому порядку.** Индексы построены с правилами сортировки primary; библиотека standby может иначе упорядочивать те же ключи. Возможны неверные результаты поиска и порядка.
Вместо standby использовать primary через существующий разрешённый read-only доступ (например, роль ''monitoring'', если доступ подтверждён для нужной базы) либо разрешённую выгрузку с primary. Этот runbook новых прав доступа не выдаёт.
Технический запрет через pg_hba и автоматическое отключение мониторинга не вводятся. Подтверждённый потребитель standby — локальный ''postgres_exporter'', роль ''prometheus'', соединение с явным standby socket. Его наличие не означает, что все периодические читатели установлены.
Операционный получатель уведомления — РП **OLVR**: ограничения приняты, мониторинг не отключать. Внешний владелец exporter отдельно не установлен; этот gap передан OLVR без нового inventory.
**Отсутствие WARNING после REFRESH не доказывает корректности индексов или размещения строк.** REFRESH меняет метаданные версии, но зависимые объекты не исправляет.
===== Принятая область затронутой collation =====
Определение одинаково для индексов и range-ключей:
* ''pg_collation.collprovider = 'c''' (libc), locale не ''C''/''POSIX'';
* ''collprovider = 'i''' (ICU);
* ''collprovider = 'd''' (default базы), если ''pg_database.datlocprovider = 'i''' либо ''datlocprovider = 'c''' с ''datcollate'' не ''C''/''POSIX''.
**Не входят:** provider ''b'' (builtin), ''C''/''POSIX'' и не-collatable ключи.
ICU включена намеренно: равенство библиотек ICU на primary и standby не доказано. Это сохранённая fail-closed граница принятого DESIGN, а не разрешение переходить на ICU или поручение проверять её версии. Новый алгоритм классификации здесь не вводится.
===== Измеренный вход и предел RTO =====
В **30 измеренных базах, включая template1**:
* **297** btree-индексов с затронутой collation;
* суммарный ''pg_relation_size'' — **1 122 320 384 bytes** (около 1,045 GiB);
* **0** затронутых range-разбиений.
**template0 не измерена:** подключение фактически отклонено. Ноль относится только к 30 измеренным базам. ''template0'' не квалифицирована как источник клонирования; без отдельной будущей qualification её нельзя использовать как проверенный cloning source или включать в заявление о полной готовности recovery. Открывать её или менять ''datallowconn'' ради настоящего документа нельзя.
**Время REINDEX и численный RTO не измерены.** Только сценарная чувствительность компонента по объёму: ''V / v_eff'', где ''V = 1 070,328125 MiB''. При условных 10 / 50 / 100 MiB/s это примерно 107,03 / 21,41 / 10,70 секунды. Эти скорости — предположения, не замер данного host и не обещанный диапазон производительности. Полная добавка к RTO включает чтение heap, сортировку, WAL, конкуренцию за ресурсы и накладные расходы; они не измерены.
===== Условная процедура recovery после promotion =====
**Приложения не подключать на запись до завершения соответствующей ветки и проверки корректности.** Процедура ниже — документ будущей разрешённой операции, не команда исполнять её сейчас.
==== 0. Promotion и актуальная проверка range partitions ====
После разрешённого ''pg_promote()'' / ''pg_ctl promote'' в каждой базе повторить запрос ниже. Использовать актуальное состояние на момент аварии, а не прошлый нулевой снимок NGw4. ''template1'' входит в проверку; предел ''template0'' сохраняется отдельно.
SELECT n.nspname, c.relname, pg_get_partkeydef(c.oid) AS partkey
FROM pg_partitioned_table p
JOIN pg_class c ON c.oid = p.partrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE p.partstrat = 'r'
AND EXISTS (
SELECT 1
FROM unnest(p.partcollation::oid[]) AS k(coll)
JOIN pg_collation co ON co.oid = k.coll
JOIN pg_database d ON d.datname = current_database()
WHERE (co.collprovider = 'c' AND co.collcollate NOT IN ('C', 'POSIX'))
OR co.collprovider = 'i'
OR (co.collprovider = 'd' AND (d.datlocprovider = 'i'
OR (d.datlocprovider = 'c' AND d.datcollate NOT IN ('C', 'POSIX')))));
Не-collatable ключи с oid 0 отсекаются join; builtin и C/POSIX не выбираются. List/hash-разбиение не входит в принятую проверку порядка при детерминированной collation.
==== 1. Непустая выборка — остановка перед записью ====
**Если найден хотя бы один range-ключ с затронутой collation, этот recovery-рецепт недостаточен. Приложения на запись не подключать.**
REINDEX не перемещает строки между разделами. До включения записи требуется проверить/исправить размещение строк найденных таблиц либо получить решение о совместимом target. Список таблиц немедленно передать OLVR/Борису. Проектирование repartitioning этим runbook не выполняется; перенос на Linux не разрешён решением владельца.
==== 2. Пустая выборка — перестроение индексов ====
Если актуальная выборка пуста во всех проверенных базах, в каждой базе, включая ''template1'', выполнить REINDEX индексов с затронутой collation. Принятый контракт допускает ''REINDEX DATABASE'' для соответствующей базы.
Не выдавать это условие за выполненное для ''template0'': её техническая граница требует отдельного disposition и будущей qualification. Не объявлять полную готовность всех баз при сохраняющемся пробеле.
==== 3. Обновление metadata collation ====
После перестроения выполнить для каждой соответствующей базы:
ALTER DATABASE REFRESH COLLATION VERSION;
'''' — placeholder фактического, корректно экранированного имени базы, а не буквальная команда. REFRESH сам по себе не исправляет индексы или range-размещение.
==== 4. Проверка корректности ====
В рамках той же разрешённой recovery-операции: ''CREATE EXTENSION IF NOT EXISTS amcheck'', затем ''bt_index_check'' по индексам с затронутой collation — **без ошибок**. Это будущие изменяющие/проверочные действия; сейчас они не выполнялись.
Проверка отсутствия WARNING при новом подключении касается только metadata. Она не заменяет amcheck и не снимает ветку остановки при range partitions.
==== 5. Подключение приложений ====
Только после необходимого REINDEX, REFRESH и успешной проверки корректности подключать приложения на запись. При непустой range-выборке сначала обязательно завершить ветку проверки/исправления размещения либо получить решение о совместимом target. Непроверенную ''template0'' не включать в обещание полной готовности или безопасного клонирования.
===== Решение владельца и дальнейшая ответственность =====
Борис через OLVR, YqIv/lFILCU: **«4416 — никаких линуксов не будет»**. Перенос standby на Linux исключён. **Переход на ICU не разрешён.**
Отказ от Linux не означает устранения mismatch, приемлемого RTO или готовности горячей замены. Долгосрочное решение и технический disposition ''template0'' остаются за OLVR/Борисом в OLV-4416. Jira пока не закрывать как устранённую.
**Риск той же природы возникнет на primary при будущей смене ОС/glibc srv-prod-postgre.olvr.** До такого изменения учитывать зависимость индексов и range-размещения от правил collation.
===== Основания =====
* Принятый DESIGN: YqIv/gvQSwN; независимая приёмка YqIv/NGmhXR.
* Принятые измерения: NGw4/9jyCrC; полный измерительный handoff NGw4/AjuxDx.
* Решение владельца: YqIv/lFILCU; документационное поручение k4Bf/HSOJfN.
* PostgreSQL 17: [[https://www.postgresql.org/docs/17/sql-altercollation.html|ALTER COLLATION]]; [[https://wiki.postgresql.org/wiki/Locale_data_changes|Locale data changes]].
Публикация этого runbook — документационный результат, не выполнение recovery и не устранение collation mismatch.