chore(db): Postgres tradein уходит со стоковых настроек — конфиг в compose #3012

Merged
lekss361 merged 1 commit from chore/2991-postgres-config into main 2026-08-20 20:53:55 +00:00
Owner

Закрывает блокер переезда #2991. Эпик #2989.

⚠️ Мержить только по команде владельца

Правка docker-compose.prod.yml триггерит deploy-tradein.yml, а изменение command: пересоздаёт контейнер → рестарт боевого Postgres. shared_buffers и shared_preload_libraries иначе и не применить — они требуют рестарта, SIGHUP не помогает.

Ветка готова и ждёт согласованного окна. Сам не мержу.

Что было

Контейнер шёл на полностью стоковой конфигурации: ни command:, ни смонтированного postgresql.conf. Проверено на main — в сервисе postgres были только image, mem_limit, environment, volumes, healthcheck.

Замер прода 2026-08-20: 169 млрд blks_read за 91,75 суток ≈ 175 МБ/с мимо кеша при shared_buffers 128 МБ.

Поправка к диагнозу из issue

Issue связывает 288 чекпойнтов в сутки с max_wal_size=1GB. Это не так: 24ч / 288 = ровно 5 минут, то есть дефолтный checkpoint_timeout. При WAL 7 ГБ/сутки лимит в 1 ГБ дал бы 7 чекпойнтов, а не 288 — значит связывающим ограничением был таймаут, и правка одного лишь max_wal_size не изменила бы ничего.

Поэтому поднят именно checkpoint_timeout до 30min: 288 → 48 чекпойнтов в сутки, шестикратно реже. Full-page image пишется на первую запись в страницу после чекпойнта, а FPI сейчас 55–86 % всего объёма WAL — то есть выигрыш идёт напрямую в объём WAL.

Значения

Подобраны под текущий сервер, все внутри mem_limit=3g:

Параметр Было Стало Почему
shared_buffers 128MB 768MB 6×, с запасом внутри 3g (idle-замер контейнера 292 МБ)
effective_cache_size 4GB 6GB подсказка планировщику; на хосте MemAvailable 6,1 ГБ
work_mem 4MB 16MB
maintenance_work_mem 64MB 256MB autovacuum по listings идёт 193 раза в сутки
checkpoint_timeout 5min 30min связывающее ограничение, см. выше
max_wal_size 1GB 4GB не 8GB: на диске 38 ГБ свободно, pg_wal растёт до этого значения
wal_compression off zstd самая дешёвая победа при доле FPI 55–86 %
random_page_cost 4 1.1 диск NVMe, а 4 — настройка под HDD
effective_io_concurrency 1 200 NVMe
shared_preload_libraries pg_stat_statements было в образе, но не активировано
shm_size 64m 512m дефолт ронял параллельные воркеры на PostGIS-сортировках

Про mem_limit

Оставлен 3g — акцептанс #2991 прямо говорит поднимать shared_buffers в рамках нынешнего лимита.

В комментарии зафиксирована ловушка переезда: mem_limit поднимать ДО правки shared_buffers, а не после. Лимит контейнера срабатывает раньше postgresql.conf, и shared_buffers=16GB при mem_limit=3g даёт OOM-kill на старте. Целевое на 64 ГБ — 40–48g, а не 64g: нужен запас под page cache вне контейнера.

Порядок применения

Issue настаивает: конфиг правится первым, до любых изменений схемы, иначе эффект спишут на разделение таблиц. Этот PR и есть первый шаг.

Замер до/после (pg_stat_bgwriter, объём pg_wal, hit ratio из pg_stat_database) снимается вокруг рестарта — до мержа его сделать негде.

Не вошло

  • synchronous_commit = off через ALTER ROLE для скраперной роли — это SQL-миграция и отдельный scope, глобально synchronous_commit остаётся on (в базе платежи).
  • Пересчёт под 64 ГБ нового сервера — после переезда.

Refs #2991, #2989

Закрывает блокер переезда #2991. Эпик #2989. ## ⚠️ Мержить только по команде владельца Правка `docker-compose.prod.yml` триггерит `deploy-tradein.yml`, а изменение `command:` пересоздаёт контейнер → **рестарт боевого Postgres**. `shared_buffers` и `shared_preload_libraries` иначе и не применить — они требуют рестарта, SIGHUP не помогает. Ветка готова и ждёт согласованного окна. Сам не мержу. ## Что было Контейнер шёл на **полностью стоковой** конфигурации: ни `command:`, ни смонтированного `postgresql.conf`. Проверено на main — в сервисе `postgres` были только `image`, `mem_limit`, `environment`, `volumes`, `healthcheck`. Замер прода 2026-08-20: 169 млрд `blks_read` за 91,75 суток ≈ 175 МБ/с мимо кеша при `shared_buffers` 128 МБ. ## Поправка к диагнозу из issue Issue связывает 288 чекпойнтов в сутки с `max_wal_size=1GB`. Это не так: **24ч / 288 = ровно 5 минут**, то есть дефолтный `checkpoint_timeout`. При WAL 7 ГБ/сутки лимит в 1 ГБ дал бы 7 чекпойнтов, а не 288 — значит связывающим ограничением был таймаут, и правка одного лишь `max_wal_size` не изменила бы ничего. Поэтому поднят именно `checkpoint_timeout` до `30min`: 288 → 48 чекпойнтов в сутки, шестикратно реже. Full-page image пишется на первую запись в страницу после чекпойнта, а FPI сейчас 55–86 % всего объёма WAL — то есть выигрыш идёт напрямую в объём WAL. ## Значения Подобраны под **текущий** сервер, все внутри `mem_limit=3g`: | Параметр | Было | Стало | Почему | |---|---|---|---| | `shared_buffers` | 128MB | 768MB | 6×, с запасом внутри 3g (idle-замер контейнера 292 МБ) | | `effective_cache_size` | 4GB | 6GB | подсказка планировщику; на хосте MemAvailable 6,1 ГБ | | `work_mem` | 4MB | 16MB | | | `maintenance_work_mem` | 64MB | 256MB | autovacuum по `listings` идёт 193 раза в сутки | | `checkpoint_timeout` | 5min | 30min | **связывающее ограничение**, см. выше | | `max_wal_size` | 1GB | 4GB | не 8GB: на диске 38 ГБ свободно, `pg_wal` растёт до этого значения | | `wal_compression` | off | zstd | самая дешёвая победа при доле FPI 55–86 % | | `random_page_cost` | 4 | 1.1 | диск NVMe, а 4 — настройка под HDD | | `effective_io_concurrency` | 1 | 200 | NVMe | | `shared_preload_libraries` | — | pg_stat_statements | было в образе, но не активировано | | `shm_size` | 64m | 512m | дефолт ронял параллельные воркеры на PostGIS-сортировках | ## Про mem_limit Оставлен `3g` — акцептанс #2991 прямо говорит поднимать `shared_buffers` в рамках нынешнего лимита. В комментарии зафиксирована ловушка переезда: **`mem_limit` поднимать ДО правки `shared_buffers`, а не после**. Лимит контейнера срабатывает раньше `postgresql.conf`, и `shared_buffers=16GB` при `mem_limit=3g` даёт OOM-kill на старте. Целевое на 64 ГБ — 40–48g, а не 64g: нужен запас под page cache вне контейнера. ## Порядок применения Issue настаивает: конфиг правится **первым**, до любых изменений схемы, иначе эффект спишут на разделение таблиц. Этот PR и есть первый шаг. Замер до/после (`pg_stat_bgwriter`, объём `pg_wal`, hit ratio из `pg_stat_database`) снимается вокруг рестарта — до мержа его сделать негде. ## Не вошло - `synchronous_commit = off` через `ALTER ROLE` для скраперной роли — это SQL-миграция и отдельный scope, глобально `synchronous_commit` остаётся `on` (в базе платежи). - Пересчёт под 64 ГБ нового сервера — после переезда. Refs #2991, #2989
lekss361 added 1 commit 2026-08-20 20:35:14 +00:00
chore(db): Postgres tradein уходит со стоковых настроек — конфиг в compose
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 16s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
d4692a6ff1
Блокер переезда (#2991). Контейнер шёл на ПОЛНОСТЬЮ стоковой конфигурации:
ни command:, ни смонтированного postgresql.conf. Замер прода 2026-08-20 —
169 млрд blks_read за 91,75 сут ≈ 175 МБ/с мимо кеша при shared_buffers 128 МБ.

Поправка к диагнозу из issue: 288 чекпойнтов в сутки — это ровно 24ч/288 =
5 минут, то есть дефолтный checkpoint_timeout, а НЕ max_wal_size. При WAL
7 ГБ/сут лимит в 1 ГБ дал бы 7 чекпойнтов, а не 288. Поэтому поднят именно
timeout до 30min — правка одного max_wal_size не изменила бы ничего.
Реже чекпойнты → реже full-page images, а они сейчас 55-86% объёма WAL.

Значения подобраны под ТЕКУЩИЙ сервер и не выходят за mem_limit=3g:
shared_buffers 768MB (6× от стоковых), work_mem 16MB, maintenance_work_mem
256MB (autovacuum по listings идёт 193 раза в сутки).

max_wal_size 4GB, а не 8GB: на диске 38 ГБ свободно, а pg_wal растёт до этого
значения. При timeout=30min между чекпойнтами копится ~0,15 ГБ — запас большой.

wal_compression=zstd — самая дешёвая победа при такой доле FPI.
random_page_cost 1.1 вместо стоковых 4: диск NVMe, а 4 — настройка под HDD,
из-за неё планировщик недооценивал индексные сканы.

pg_stat_statements подключён через shared_preload_libraries — расширение было
в образе, но не активировано, и прошлый разбор пришлось вести по косвенным
признакам.

shm_size 512m: дефолтные 64 МБ /dev/shm ронял параллельные воркеры на тяжёлых
PostGIS-сортировках.

mem_limit оставлен 3g. В комментарии зафиксировано, что при переезде его надо
поднимать ДО правки shared_buffers: лимит контейнера срабатывает раньше
postgresql.conf, и shared_buffers=16GB при mem_limit=3g даёт OOM на старте.

Refs #2991, #2989
lekss361 merged commit 1da1ba084d into main 2026-08-20 20:53:55 +00:00
lekss361 deleted branch chore/2991-postgres-config 2026-08-20 20:53:55 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3012
No description provided.