chore(compose): параметризовать ресурсы postgres под переезд на новый хост #3058

Merged
lekss361 merged 1 commit from chore/compose-host-parameterised-resources into main 2026-08-23 16:16:39 +00:00
Owner

Снимает два блокера окна 30.08 (#3057), которых не было ни в одном issue — их вскрыла репетиция полного перелива на Poincare 2026-08-23.

Мина 1: mem_limit: 3g зашит жёстко

tradein-mvp/docker-compose.prod.yml:126. На 62 ГиБ целевое 44g (не 64g — нужен запас под page cache вне контейнера). Ключевой порядок: лимит контейнера обязан подниматься раньше shared_buffers, потому что он срабатывает раньше postgresql.confshared_buffers=12GB при mem_limit=3g убивает контейнер OOM-kill'ом на старте.

Предупреждение об этом в файле уже было (строки 128-131), правки не было.

Мина 2: initdb.d против восстановления дампа

Оба compose монтируют каталог миграций в /docker-entrypoint-initdb.d. На свежем томе цепочка из 249 миграций отработает до восстановления дампа и засеет данные — scrape_schedules 157 строк, tradein_users 13, deals 80 (003_seed_deals.sql, 193_tradein_users_seed.sql) — а дамп идёт с --clean --if-exists и ляжет поверх.

Решение: источник монтирования становится переменной. На первом старте нового хоста она указывает на пустой каталог, схема приезжает восстановлением дампа; после restore переменная снимается.

Что параметризовано

Только то, что зависит от размера хоста:

Переменная Дефолт (= текущий прод) Целевое на 62 ГиБ
TRADEIN_PG_MEM_LIMIT 3g 44g
TRADEIN_PG_SHM_SIZE 512m не меняется без причины
TRADEIN_PG_SHARED_BUFFERS 768MB 12GB
TRADEIN_PG_EFFECTIVE_CACHE_SIZE 6GB 36GB
TRADEIN_PG_WORK_MEM 16MB 64MB
TRADEIN_PG_MAINTENANCE_WORK_MEM 256MB 2GB
TRADEIN_PG_MAX_WAL_SIZE 4GB 16GB
TRADEIN_PG_INITDB_DIR ./backend/data/sql пустой каталог на первом старте
GENDESIGN_PG_INITDB_DIR ./backend/db/init пустой каталог на первом старте

TRADEIN_PG_MEM_LIMIT управляет и mem_limit, и memswap_limit намеренно: инвариант «без свапа» (memswap == mem) обязан держаться и после правки, а не только на дефолте.

checkpoint_timeout, wal_compression, random_page_cost, pg_stat_statements и прочие от размера хоста не зависят — не тронуты.

Целевые значения перечислены прямо в комментарии compose, а не в .env.example: файлы .env* заблокированы guard-rail'ом сессии. Инженеру в окне не придётся искать их в другом месте.

Нулевое изменение поведения на Beget — проверено, не заявлено

docker compose config прогнан дважды на обоих файлах.

Без переменных резолвится бит-в-бит как в main: mem_limit/memswap_limit = 3221225472 (3g), shm_size = 536870912 (512m), shared_buffers=768MB, effective_cache_size=6GB, work_mem=16MB, maintenance_work_mem=256MB, max_wal_size=4GB, initdb source = штатные пути.

С переменными: mem_limit = 47244640256 (44g), memswap_limit синхронно, 12GB / 36GB / 64MB / 2GB / 16GB, initdb source подменяется.

Лимиты остальных сервисов не изменились (browser 2560m/3g, backend 768m/768m). $${POSTGRES_USER} в healthcheck диффом не задет.

Что намеренно НЕ сделано

Postgres Site Finder (корневой compose) получил только переменную initdb. mem_limit и тюнинга у него сейчас нет вовсе — добавление изменило бы поведение текущего прода, это отдельный риск и отдельная задача.

При этом он до сих пор на стоковом конфиге: shared_buffers 128 МБ на базу 15 ГБ, wal_compression off, max_wal_size 1 ГБ. Фикс #2991 применили только к кластеру Меры. Отсюда и разрыв в репетиции — перелив Меры 23 с против 173 с у Site Finder.

Риск, который стоит внести в чек-лист окна

Если после restore забыть снять *_INITDB_DIR, а том потом пересоздать (docker volume prune, повторная раскатка) — initdb отработает по пустому каталогу, и база останется без схемы вообще, молча. Снятие переменной должно быть отдельным шагом с проверкой, а не «не забыть». Добавлю пунктом в #3057.

Refs #2989, #3057

Снимает два блокера окна 30.08 (#3057), которых не было ни в одном issue — их вскрыла репетиция полного перелива на Poincare 2026-08-23. ## Мина 1: `mem_limit: 3g` зашит жёстко `tradein-mvp/docker-compose.prod.yml:126`. На 62 ГиБ целевое **44g** (не 64g — нужен запас под page cache вне контейнера). Ключевой порядок: лимит **контейнера** обязан подниматься **раньше** `shared_buffers`, потому что он срабатывает раньше `postgresql.conf` — `shared_buffers=12GB` при `mem_limit=3g` убивает контейнер OOM-kill'ом на старте. Предупреждение об этом в файле **уже было** (строки 128-131), правки не было. ## Мина 2: `initdb.d` против восстановления дампа Оба compose монтируют каталог миграций в `/docker-entrypoint-initdb.d`. На **свежем** томе цепочка из 249 миграций отработает **до** восстановления дампа и засеет данные — `scrape_schedules` 157 строк, `tradein_users` 13, `deals` 80 (`003_seed_deals.sql`, `193_tradein_users_seed.sql`) — а дамп идёт с `--clean --if-exists` и ляжет поверх. Решение: источник монтирования становится переменной. На первом старте нового хоста она указывает на **пустой** каталог, схема приезжает восстановлением дампа; после restore переменная снимается. ## Что параметризовано Только то, что зависит от размера хоста: | Переменная | Дефолт (= текущий прод) | Целевое на 62 ГиБ | |---|---|---| | `TRADEIN_PG_MEM_LIMIT` | `3g` | `44g` | | `TRADEIN_PG_SHM_SIZE` | `512m` | не меняется без причины | | `TRADEIN_PG_SHARED_BUFFERS` | `768MB` | `12GB` | | `TRADEIN_PG_EFFECTIVE_CACHE_SIZE` | `6GB` | `36GB` | | `TRADEIN_PG_WORK_MEM` | `16MB` | `64MB` | | `TRADEIN_PG_MAINTENANCE_WORK_MEM` | `256MB` | `2GB` | | `TRADEIN_PG_MAX_WAL_SIZE` | `4GB` | `16GB` | | `TRADEIN_PG_INITDB_DIR` | `./backend/data/sql` | пустой каталог на первом старте | | `GENDESIGN_PG_INITDB_DIR` | `./backend/db/init` | пустой каталог на первом старте | `TRADEIN_PG_MEM_LIMIT` управляет **и** `mem_limit`, **и** `memswap_limit` намеренно: инвариант «без свапа» (`memswap == mem`) обязан держаться и после правки, а не только на дефолте. `checkpoint_timeout`, `wal_compression`, `random_page_cost`, `pg_stat_statements` и прочие от размера хоста не зависят — **не тронуты**. Целевые значения перечислены прямо в комментарии compose, а не в `.env.example`: файлы `.env*` заблокированы guard-rail'ом сессии. Инженеру в окне не придётся искать их в другом месте. ## Нулевое изменение поведения на Beget — проверено, не заявлено `docker compose config` прогнан дважды на обоих файлах. **Без переменных** резолвится бит-в-бит как в `main`: `mem_limit`/`memswap_limit` = 3221225472 (3g), `shm_size` = 536870912 (512m), `shared_buffers=768MB`, `effective_cache_size=6GB`, `work_mem=16MB`, `maintenance_work_mem=256MB`, `max_wal_size=4GB`, initdb source = штатные пути. **С переменными**: `mem_limit` = 47244640256 (44g), `memswap_limit` синхронно, `12GB` / `36GB` / `64MB` / `2GB` / `16GB`, initdb source подменяется. Лимиты остальных сервисов не изменились (`browser` 2560m/3g, `backend` 768m/768m). `$${POSTGRES_USER}` в healthcheck диффом не задет. ## Что намеренно НЕ сделано **Postgres Site Finder** (корневой compose) получил только переменную initdb. `mem_limit` и тюнинга у него сейчас **нет вовсе** — добавление изменило бы поведение текущего прода, это отдельный риск и отдельная задача. При этом он до сих пор на стоковом конфиге: **`shared_buffers` 128 МБ на базу 15 ГБ**, `wal_compression` off, `max_wal_size` 1 ГБ. Фикс #2991 применили только к кластеру Меры. Отсюда и разрыв в репетиции — перелив Меры 23 с против 173 с у Site Finder. ## Риск, который стоит внести в чек-лист окна Если после restore забыть снять `*_INITDB_DIR`, а том потом пересоздать (`docker volume prune`, повторная раскатка) — initdb отработает по **пустому** каталогу, и база останется без схемы вообще, молча. Снятие переменной должно быть отдельным шагом с проверкой, а не «не забыть». Добавлю пунктом в #3057. Refs #2989, #3057
lekss361 added 1 commit 2026-08-23 16:12:59 +00:00
chore(compose): параметризовать ресурсы postgres под переезд на новый хост
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
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
2eb7852622
Репетиция полного перелива на Poincare (12 потоков / 62 ГиБ / NVMe RAID1)
2026-08-23 вскрыла две мины окна 30.08, которых нет ни в одном issue.

1. mem_limit: 3g зашит жёстко. На 62 ГиБ целевое 44g, и лимит КОНТЕЙНЕРА
   обязан подниматься РАНЬШЕ shared_buffers — он срабатывает раньше
   postgresql.conf, поэтому shared_buffers=12GB при mem_limit=3g убивает
   контейнер OOM-kill'ом на старте. Предупреждение об этом в файле было,
   правки не было.

2. Оба compose монтируют каталог миграций в /docker-entrypoint-initdb.d.
   На СВЕЖЕМ томе цепочка из 249 миграций отработает ДО восстановления
   дампа и засеет данные (scrape_schedules 157 строк, tradein_users 13,
   deals 80), а дамп идёт с --clean --if-exists.

Параметризованы только те значения, что зависят от размера хоста:
TRADEIN_PG_MEM_LIMIT (одна переменная на mem_limit и memswap_limit —
инвариант «без свапа» обязан держаться и после правки), TRADEIN_PG_SHM_SIZE,
shared_buffers, effective_cache_size, work_mem, maintenance_work_mem,
max_wal_size, плюс TRADEIN_PG_INITDB_DIR / GENDESIGN_PG_INITDB_DIR.
checkpoint_timeout, wal_compression, random_page_cost, pg_stat_statements
от размера хоста не зависят — не тронуты.

Все дефолты равны текущим прод-значениям на Beget. Проверено docker compose
config дважды на обоих файлах: без переменных резолвится в 3g / 512m /
768MB / 6GB / 16MB / 256MB / 4GB и штатные пути initdb — бит-в-бит как
в main; с переменными — в 44g / 12GB / 36GB / 64MB / 2GB / 16GB.
Лимиты остальных сервисов не изменились.

Postgres Site Finder (корневой compose) намеренно получил только переменную
initdb: mem_limit и тюнинга у него сейчас нет вовсе, добавление изменило бы
поведение текущего прода. Он до сих пор на стоковом конфиге —
shared_buffers 128 МБ на базу 15 ГБ, wal_compression off — отдельная задача.

Refs #2989, #3057
lekss361 merged commit 1c16ea45a3 into main 2026-08-23 16:16:39 +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#3058
No description provided.