diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index ed8408d2..9819a03c 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -98,7 +98,21 @@ services: - "127.0.0.1:5432:5432" volumes: - postgres_data:/var/lib/postgresql/data - - ./backend/db/init:/docker-entrypoint-initdb.d:ro + # GENDESIGN_PG_INITDB_DIR (#2989): та же мина initdb, что описана у trade-in + # (см. tradein-mvp/docker-compose.prod.yml) — на СВЕЖЕМ томе postgres + # прогоняет всё из этого каталога как initdb-миграции ДО восстановления + # дампа Site Finder. На первом старте нового хоста задать переменную на + # ПУСТОЙ каталог — схема тогда приезжает восстановлением дампа, а не + # initdb-цепочкой; после restore переменную снять. + # Дефолт = текущий прод-путь, поведение Beget не меняется. + # + # ⚠️ Postgres этого стека (Site Finder) НЕ параметризован по mem_limit/ + # shared_buffers, в отличие от trade-in выше — сейчас у него этих лимитов + # вовсе нет (стоковый shared_buffers=128MB на базе ~15 ГБ, wal_compression + # off). Осознанно НЕ трогается в этой правке (#2989 — только initdb-мина, + # добавление лимитов сервису без них — отдельная задача с отдельным риском + # для текущего прода). См. описание PR. + - ${GENDESIGN_PG_INITDB_DIR:-./backend/db/init}:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"] interval: 5s diff --git a/tradein-mvp/docker-compose.prod.yml b/tradein-mvp/docker-compose.prod.yml index a3258f9d..9e229983 100644 --- a/tradein-mvp/docker-compose.prod.yml +++ b/tradein-mvp/docker-compose.prod.yml @@ -123,13 +123,28 @@ services: # без свапа (shared_buffers не должны уходить в swap-thrash). # ревью #2214: 3g (idle 656MiB — БД дороже всего при ложном OOM; work_mem-спайки # тяжёлых PostGIS-сортировок бэктеста/эстиматора + autovacuum) - mem_limit: 3g - memswap_limit: 3g - # ⚠️ При переезде на выделенный сервер (#2989) mem_limit поднимать ДО правки - # shared_buffers, а не после: лимит контейнера срабатывает РАНЬШЕ postgresql.conf, - # и shared_buffers=16GB при mem_limit=3g даёт OOM-kill на старте. Целевое на - # 64 ГБ — 40-48g, а не 64g: нужен запас под page cache ВНЕ контейнера. - shm_size: 512m + # + # ── Переезд на выделенный сервер (#2989, репетиция 2026-08-23 на Poincare: + # 12 потоков / 62 ГиБ / NVMe RAID1) ───────────────────────────────────────── + # TRADEIN_PG_MEM_LIMIT — ОДНА переменная на mem_limit И memswap_limit + # намеренно: связка «без свапа» (memswap == mem, см. обоснование выше) обязана + # держаться и после правки, а не только на дефолте. Дефолт = ровно текущее + # прод-значение Beget (3g) — до задания переменной поведение стека НЕ меняется. + # ⚠️ mem_limit — потолок КОНТЕЙНЕРА, он срабатывает РАНЬШЕ postgresql.conf. + # На новом хосте поднимать TRADEIN_PG_MEM_LIMIT ДО правки + # TRADEIN_PG_SHARED_BUFFERS ниже — иначе shared_buffers=12GB при mem_limit=3g + # убьёт контейнер OOM-kill'ом на самом старте. Целевое на 62 ГиБ — 44g, а + # не 64g: нужен запас под page cache ВНЕ контейнера (это то, что реально + # отражает effective_cache_size — не shared_buffers). + mem_limit: ${TRADEIN_PG_MEM_LIMIT:-3g} + memswap_limit: ${TRADEIN_PG_MEM_LIMIT:-3g} + # TRADEIN_PG_SHM_SIZE: дефолт = текущее прод-значение (512m). Сюда ложатся + # только DSM-сегменты параллельных планов (не буферный кеш), поэтому рост + # /dev/shm не следует из тех же расчётов, что shared_buffers ниже — трогать + # только если после переезда параллельные PostGIS-сортировки начнут падать + # с «could not resize shared memory segment» (аналог #2812 в корневом + # docker-compose.prod.yml). + shm_size: ${TRADEIN_PG_SHM_SIZE:-512m} # /dev/shm по умолчанию 64 МБ. Параллельные воркеры кладут туда shared memory # segments; на тяжёлых PostGIS-сортировках это «could not resize shared memory». # @@ -138,23 +153,39 @@ services: # 169 млрд blks_read за 91,75 сут ≈ 175 МБ/с мимо кеша при shared_buffers=128 МБ. # # Значения подобраны под ТЕКУЩИЙ сервер и НЕ выходят за mem_limit=3g. - # После переезда пересчитать под 64 ГБ. + # + # ── Параметризация под переезд (#2989, репетиция 2026-08-23 на Poincare) ── + # Пять параметров ниже вынесены в переменные ИМЕННО потому что они зависят + # от размера хоста; остальные (checkpoint_timeout, wal_compression, + # random_page_cost, pg_stat_statements и пр. ниже) от размера хоста НЕ + # зависят — оставлены как есть. Дефолты = буквально текущие прод-значения + # на Beget, так что до задания переменных поведение НЕ меняется ни на бит. + # Целевые значения для нового хоста (62 ГиБ) — задавать в окружении деплоя, + # НЕ здесь и НЕ на Beget (там 11 ГиБ, контейнер просто не поднимется): + # TRADEIN_PG_MEM_LIMIT=44g (сначала он — см. предупреждение выше) + # TRADEIN_PG_SHARED_BUFFERS=12GB + # TRADEIN_PG_EFFECTIVE_CACHE_SIZE=36GB + # TRADEIN_PG_WORK_MEM=64MB + # TRADEIN_PG_MAINTENANCE_WORK_MEM=2GB + # TRADEIN_PG_MAX_WAL_SIZE=16GB + # Порядок обязателен: сначала TRADEIN_PG_MEM_LIMIT (контейнерный потолок, + # см. выше), потом эти пять — иначе OOM-kill на старте. command: - postgres # Память. 768MB shared_buffers — 6× от стоковых 128MB, с запасом внутри 3g # (idle-замер контейнера был 292 МБ, work_mem-спайки и autovacuum сверху). - -c - - shared_buffers=768MB + - shared_buffers=${TRADEIN_PG_SHARED_BUFFERS:-768MB} # effective_cache_size — подсказка планировщику, НЕ аллокация. На хосте # MemAvailable 6,1 ГБ, поэтому 6GB честно отражает доступный page cache. - -c - - effective_cache_size=6GB + - effective_cache_size=${TRADEIN_PG_EFFECTIVE_CACHE_SIZE:-6GB} - -c - - work_mem=16MB + - work_mem=${TRADEIN_PG_WORK_MEM:-16MB} # maintenance_work_mem: autovacuum по listings идёт 193 раза в сутки, # с 64MB каждый проход перечитывает индексы лишними итерациями. - -c - - maintenance_work_mem=256MB + - maintenance_work_mem=${TRADEIN_PG_MAINTENANCE_WORK_MEM:-256MB} # Чекпойнты. ГЛАВНОЕ: 288 чекпойнтов в сутки — это ровно 24ч/288 = 5 минут, # то есть дефолтный checkpoint_timeout, а НЕ max_wal_size. При WAL 7 ГБ/сут # лимит в 1 ГБ дал бы 7 чекпойнтов, а не 288. Поэтому поднимаем ИМЕННО @@ -168,7 +199,7 @@ services: # растёт до этого значения. При timeout=30min и 7 ГБ/сут между чекпойнтами # накапливается ~0,15 ГБ, так что 4GB — потолок с большим запасом. - -c - - max_wal_size=4GB + - max_wal_size=${TRADEIN_PG_MAX_WAL_SIZE:-4GB} - -c - min_wal_size=1GB # Самая дешёвая победа при доле FPI 55-86%. zstd доступен с PG15, у нас 16. @@ -196,7 +227,17 @@ services: POSTGRES_PASSWORD: ${TRADEIN_POSTGRES_PASSWORD:?required} volumes: - tradein-postgres-data:/var/lib/postgresql/data - - ./backend/data/sql:/docker-entrypoint-initdb.d:ro + # TRADEIN_PG_INITDB_DIR (#2989): на СВЕЖЕМ томе postgres прогоняет ВСЁ из + # этого каталога как initdb-миграции ДО восстановления дампа — 249 миграций + # отработают первыми и засеют данные (`scrape_schedules` 157 строк, + # `tradein_users` 13, `deals` 80 — `003_seed_deals`/`193_tradein_users_seed`), + # а дамп идёт с `--clean --if-exists` и ляжет поверх (см. репетицию переноса + # 2026-08-23, vault fixes/Bug_Migration_Window_Operational_Blockers_OPEN п.2). + # На первом старте нового хоста задать переменную на ПУСТОЙ каталог — схема + # тогда приезжает восстановлением дампа, а не initdb-цепочкой; после + # restore переменную снять, дальше работает как обычно. + # Дефолт = текущий прод-путь, поведение Beget не меняется. + - ${TRADEIN_PG_INITDB_DIR:-./backend/data/sql}:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U ${TRADEIN_POSTGRES_USER:-tradein}"] interval: 10s