Files
airflow-greenplum/docs/internal/bookings_generation_benchmark.md
T
ddadminandClaude Opus 4.6 c81bdb86d9 perf(bookings): BOOKINGS_JOBS=1 по умолчанию (3× быстрее на инкрементах)
- Зачем:
  - бенчмарк на i7-11800H / WSL2 показал, что jobs=1 генерирует +1 день за ~3 мин,
    а jobs=2 — за ~9 мин из-за lock contention на gen.events FOR UPDATE SKIP LOCKED.
    Тюнинг PG (shared_buffers, work_mem) и synchronous_commit=off не влияют.
- Что:
  - дефолт BOOKINGS_JOBS изменён с 2 на 1 в Makefile и .env.example.
  - добавлен docs/internal/bookings_generation_benchmark.md с полной матрицей экспериментов.
  - обновлены комментарии и bookings/README.md.
- Проверка:
  - make bookings-init && time make bookings-generate-day (~3 мин).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:06:56 +03:00

76 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Бенчмарк генерации bookings: параллельность и тюнинг PostgreSQL
> Дата: 2026-03-09
> Железо: Intel Core i7-11800H @ 2.30GHz (8 ядер / 16 потоков), игровой ноутбук
> Среда: WSL2, Docker Desktop, PostgreSQL 16 в контейнере
> Данные: seed-дамп 60 дней (~500k bookings, ~180k boarding_passes, ~1.2M tickets)
## Контекст
Генератор demodb (`postgrespro/demodb`) использует очередь событий `gen.events` с обработкой через `process_queue()`. При `jobs>1` воркеры запускаются через `dblink_send_query` и конкурируют за очередь через `SELECT ... FOR UPDATE SKIP LOCKED`.
Задача: найти оптимальную конфигурацию для `make bookings-generate-day` (инкремент +1 день).
## Методика
- Между экспериментами: `make bookings-init BOOKINGS_JOBS=N` (восстановление из seed-дампа, ~18 сек).
- Замер: `time make bookings-generate-day BOOKINGS_JOBS=N`.
- Контроль: `max(book_date)` должен сдвинуться на +1 день.
- Тюнинг PG: `ALTER SYSTEM SET` + `docker compose restart bookings-db`.
Параметры тюнинга PostgreSQL:
```
shared_buffers = 512MB (дефолт: 128MB)
work_mem = 64MB (дефолт: 4MB)
maintenance_work_mem = 256MB (дефолт: 64MB)
effective_cache_size = 1GB (дефолт: 4GB)
wal_buffers = 16MB (дефолт: -1, авто)
checkpoint_completion_target = 0.9 (дефолт: 0.9)
random_page_cost = 1.1 (дефолт: 4.0)
```
## Результаты
| # | Тюнинг PG | sync_commit | Jobs | Время | vs baseline |
|---|-----------|-------------|------|--------|--------------|
| 1 | нет | on | 1 | 2m 48s | **baseline** |
| 2 | нет | on | 2 | 8m 36s | 3× хуже |
| 3 | да | on | 1 | 3m 01s | шум |
| 4 | да | on | 2 | 8m 42s | 3× хуже |
| 5 | да | off | 1 | 2m 50s | шум |
| 6 | да | off | 2 | 8m 39s | 3× хуже |
Повторный инкремент (прогретый кэш): 5m 03s (jobs=2), не замерялся (jobs=1).
## Выводы
### 1. jobs=1 оптимален для инкрементов
Для генерации +1 дня параллельность через dblink **контрпродуктивна**:
- Два воркера конкурируют за одну очередь `gen.events` через `FOR UPDATE SKIP LOCKED` — это row-level lock contention.
- Overhead: dblink-соединения, синхронизация через `gen.stat_jobs`, polling через `dblink_is_busy()`.
- На WSL2 дополнительно: виртуализированный I/O не масштабируется при параллельных записях.
При генерации с нуля (`make bookings-generate`, десятки тысяч событий) jobs>1 **может** давать прирост, но не тестировалось в этом бенчмарке.
### 2. Тюнинг PostgreSQL не влияет
Увеличение shared_buffers в 4 раза, work_mem в 16 раз и т.д. не дало измеримого эффекта. Узкое место — не буферы и не I/O, а сама логика генератора: последовательная обработка событий с COMMIT после каждого.
### 3. synchronous_commit=off не влияет
Генератор делает COMMIT после каждого события (~сотни раз за день). Ожидалось, что `synchronous_commit=off` ускорит запись WAL. Эффект не обнаружен — вероятно, WAL-буферы и так справляются при одном потоке.
### 4. VACUUM-ивенты — отдельная проблема
Генератор вставляет в очередь событие `VACUUM` каждую неделю модельного времени. Обработчик запускает `VACUUM ANALYZE` всей базы через `dblink_exec`. При 500k+ строках это занимает минуты. Решение: `DELETE FROM gen.events WHERE type = 'VACUUM'` перед `continue()` в инкрементальных скриптах.
## Итоговая конфигурация
```
BOOKINGS_JOBS=1 # синхронно, без dblink — ~3 мин на +1 день
BOOKINGS_INIT_DAYS=60 # seed-дамп покрывает ~34 дня бронирований + boarding_passes
```
Тюнинг PostgreSQL не требуется для текущих объёмов данных.