diff --git a/tradein-mvp/docker-compose.prod.yml b/tradein-mvp/docker-compose.prod.yml index c68d74c2..2f9914bd 100644 --- a/tradein-mvp/docker-compose.prod.yml +++ b/tradein-mvp/docker-compose.prod.yml @@ -125,6 +125,70 @@ services: # тяжёлых 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 + # /dev/shm по умолчанию 64 МБ. Параллельные воркеры кладут туда shared memory + # segments; на тяжёлых PostGIS-сортировках это «could not resize shared memory». + # + # ── Конфигурация Postgres (#2991) ───────────────────────────────────────── + # До этого контейнер шёл на ПОЛНОСТЬЮ стоковых настройках. Замер прода 2026-08-20: + # 169 млрд blks_read за 91,75 сут ≈ 175 МБ/с мимо кеша при shared_buffers=128 МБ. + # + # Значения подобраны под ТЕКУЩИЙ сервер и НЕ выходят за mem_limit=3g. + # После переезда пересчитать под 64 ГБ. + command: + - postgres + # Память. 768MB shared_buffers — 6× от стоковых 128MB, с запасом внутри 3g + # (idle-замер контейнера был 292 МБ, work_mem-спайки и autovacuum сверху). + - -c + - shared_buffers=768MB + # effective_cache_size — подсказка планировщику, НЕ аллокация. На хосте + # MemAvailable 6,1 ГБ, поэтому 6GB честно отражает доступный page cache. + - -c + - effective_cache_size=6GB + - -c + - work_mem=16MB + # maintenance_work_mem: autovacuum по listings идёт 193 раза в сутки, + # с 64MB каждый проход перечитывает индексы лишними итерациями. + - -c + - maintenance_work_mem=256MB + # Чекпойнты. ГЛАВНОЕ: 288 чекпойнтов в сутки — это ровно 24ч/288 = 5 минут, + # то есть дефолтный checkpoint_timeout, а НЕ max_wal_size. При WAL 7 ГБ/сут + # лимит в 1 ГБ дал бы 7 чекпойнтов, а не 288. Поэтому поднимаем ИМЕННО + # timeout — иначе правка max_wal_size ничего бы не изменила. + # Реже чекпойнты → реже full-page images (сейчас 55-86% всего объёма WAL). + - -c + - checkpoint_timeout=30min + - -c + - checkpoint_completion_target=0.9 + # max_wal_size=4GB, а не 8GB: на диске сейчас всего 38 ГБ свободно, а pg_wal + # растёт до этого значения. При timeout=30min и 7 ГБ/сут между чекпойнтами + # накапливается ~0,15 ГБ, так что 4GB — потолок с большим запасом. + - -c + - max_wal_size=4GB + - -c + - min_wal_size=1GB + # Самая дешёвая победа при доле FPI 55-86%. zstd доступен с PG15, у нас 16. + - -c + - wal_compression=zstd + # Диск NVMe, а стоковый random_page_cost=4 — настройка под HDD: планировщик + # систематически недооценивает индексные сканы и уходит в Seq Scan. + - -c + - random_page_cost=1.1 + - -c + - effective_io_concurrency=200 + # pg_stat_statements: расширение есть в образе, но не подключено. Без него + # следующий разбор снова придётся вести по косвенным признакам. + # shared_preload_libraries требует рестарта — поэтому именно здесь. + - -c + - shared_preload_libraries=pg_stat_statements + - -c + - pg_stat_statements.max=5000 + - -c + - pg_stat_statements.track=top logging: *default-logging environment: POSTGRES_DB: tradein