Skip to content

perf(db): close panel database connections after a minute idle - #120

Merged
agentHits merged 1 commit into
AgentHits-Devfrom
perf/db-idle-connections
Oct 28, 2026
Merged

agentHits merged 1 commit into
AgentHits-Devfrom
perf/db-idle-connections

Conversation

@agentHits

Copy link
Copy Markdown
Collaborator

Зачем

Каждое соединение с базой — отдельный процесс Postgres со своей памятью, 2–10 МБ. Пул postgres.js держит открытое соединение 30–60 минут (max_lifetime по умолчанию), даже если запросов нет. Панель почти всё время простаивает, но после любой пачки запросов (открыли список проектов, прошёл деплой) Postgres держал до 20 таких процессов. На рабочей панели в простое было 10 соединений, около 45 МБ памяти Postgres.

Что изменилось

  • packages/server/src/db/index.ts: пул создаётся с idle_timeout: 60. Соединение, которое минуту простояло без запросов, закрывается.
  • Тест apps/dokploy/__test__/db/pool-options.test.ts проверяет, что пул создаётся с этим параметром.

Healthcheck образа раз в 30 секунд держит одно соединение открытым, поэтому в простое остаётся одно соединение. Первый запрос после простоя открывает новое соединение заново: это TCP и вход по SCRAM, единицы миллисекунд.

Проверки

  • vitest нового теста: проходит, а на коде без правки падает.
  • Полный vitest: 4294 прошли, 5 упали в vector-validate.test.ts. Они падают и без правки: на Mac Colima не монтирует временную папку для docker run.
  • tsc --noEmit в apps/dokploy и packages/server и biome check проходят без ошибок.
  • Замеры: тот же образ AgentHits-Dev с PGIDLE_TIMEOUT=60 и без него. postgres.js читает эту переменную окружения и применяет ту же настройку, что и правка в коде.
    • Стенд изолированный: Docker-in-Docker на Mac, образы собраны нативно под arm64. Абсолютные цифры на сервере будут другими.
    • Прогоны чередовались с другими вариантами. В каждом прогоне сценарий повторяется три раза, после него идёт пачка из 120 одновременных запросов и две минуты простоя. В таблице медиана по прогонам.
Без правки (3 прогона) С правкой (2 прогона)
Шаги сценария без ошибок 90 из 90 60 из 60
Соединений Postgres под нагрузкой 20 20
Соединений Postgres после 2 минут простоя 20 1
Память процессов Postgres под нагрузкой 107 МиБ 103 МиБ
Память процессов Postgres после 2 минут простоя 107 МиБ 14 МиБ
Память сервера панели в простое / пик 234 / 532 МиБ 237 / 526 МиБ

Скорость API и страниц в прогонах одного варианта гуляет на ±15%: Mac делит процессор с другой работой. Разница между вариантами меньше этого разброса.

Сценарий: вход; пять страниц панели; 10 запросов project.all подряд; 1000 запросов к API по 10 одновременно и 200 к странице проектов по 5; проект; деплой Compose со сборкой и живым логом; логи и терминал контейнера; деплой из образа; добавление сервера и SSH-терминал.

С соседним PR про общий пул соединений эффекты складываются: под нагрузкой 10 соединений и 88 МиБ, после простоя одно соединение и 15 МиБ.

Что проверить вручную на dev

  • Открыть панель после нескольких минут простоя: первая страница открывается без ошибок и без заметной задержки.
  • Деплой и логи деплоя.

🤖 Generated with Claude Code

Что:
- Пул postgres.js панели закрывает соединение, которое простояло без запросов 60 секунд (`idle_timeout: 60`). Раньше соединение жило 30–60 минут (`max_lifetime` по умолчанию), даже если запросов не было.
- Тест `__test__/db/pool-options.test.ts` проверяет, что пул создаётся с этим параметром.
Зачем:
- Каждое соединение — отдельный процесс Postgres со своей памятью, 2–10 МБ. Панель почти всё время простаивает, а после любой пачки запросов пул держал до 20 таких процессов.
- На изолированном стенде память процессов Postgres после двух минут простоя упала со 107 до 14 МБ, соединений осталось одно вместо двадцати.
Риски:
- Первый запрос после минуты простоя открывает соединение заново (TCP и вход по SCRAM, единицы миллисекунд) и заново готовит свои запросы. Healthcheck образа раз в 30 секунд держит одно соединение открытым.
Проверки:
- Команды и результаты: `vitest run __test__/db/pool-options.test.ts` проходит, на коде без правки падает; полный `vitest run`: 4294 прошли, 5 упали в `vector-validate.test.ts` (на Mac Colima не монтирует временную папку `docker run`, без правки так же); `tsc --noEmit` в `apps/dokploy` и `packages/server` без ошибок; `biome check` без замечаний. Стенд: нативные arm64-образы `AgentHits-Dev` в Docker-in-Docker, сценарий из входа, страниц, нагрузки на API, деплоев, логов и терминалов, по 2–3 прогона на вариант вперемешку.
- Ограничения: стенд на Mac, а не на сервере; по скорости разница между прогонами одного варианта (±15%) больше, чем между вариантами.

What:
- The panel's postgres.js pool closes a connection after 60 seconds without queries (`idle_timeout: 60`). Before, a connection lived 30–60 minutes (the default `max_lifetime`) even with no queries.
- The test `__test__/db/pool-options.test.ts` checks that the pool is created with this option.
Why:
- Every connection is a separate Postgres process with its own memory, 2–10 MB. The panel is idle most of the time, yet after any burst of queries the pool kept up to 20 such processes.
- On the isolated bench, Postgres process memory after two idle minutes fell from 107 to 14 MB, with one connection left instead of twenty.
Risks:
- The first query after a minute idle opens a connection again (TCP and SCRAM login, a few milliseconds) and prepares its statements again. The image healthcheck every 30 seconds keeps one connection open.
Checks:
- Commands and results: `vitest run __test__/db/pool-options.test.ts` passes and fails without the change; full `vitest run`: 4294 passed, 5 failed in `vector-validate.test.ts` (Colima on the Mac does not mount the `docker run` temp folder, same without the change); `tsc --noEmit` in `apps/dokploy` and `packages/server` clean; `biome check` clean. Bench: native arm64 images of `AgentHits-Dev` in Docker-in-Docker, a scenario of sign-in, pages, API load, deploys, logs and terminals, 2–3 interleaved runs per variant.
- Limitations: the bench runs on a Mac, not the server; for speed, runs of one variant differ more (±15%) than variants do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@agentHits
agentHits merged commit 6b64614 into AgentHits-Dev Oct 28, 2026
7 checks passed
@agentHits
agentHits deleted the perf/db-idle-connections branch October 29, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant