fix(bookings): исправлена генерация demodb и добавлен seed-дамп

- Зачем:
  - генерация с нуля занимала часы, boarding_passes были пустыми, данные
    пропадали после docker compose down/up.
- Что:
  - обновлён demodb до коммита 866e56f, добавлен патч install_connstr_no_hardcode.
  - BOOKINGS_INIT_DAYS увеличен до 60, BOOKINGS_JOBS по умолчанию 2.
  - добавлен seed-дамп bookings/seed/demo.sql.xz (42 MB, xz вместо 7z).
  - make bookings-init восстанавливает из дампа (~18 сек) и применяет GUC из .env.
  - make bookings-generate — генерация с нуля для разработчиков.
  - generate_next_day.sql: COMMIT после continue(), pg_sleep(3) для jobs>1, synchronous_commit=on + CHECKPOINT.
  - bookings-check-jobs: добавлена валидация нечисловых значений BOOKINGS_JOBS.
  - bookings-init теперь вызывает bookings-check-jobs как prerequisite.
  - синхронизированы внутренние документы (коммит demodb, init_days=60).
- Проверка:
  - make bookings-init && make bookings-generate-day BOOKINGS_JOBS=2.
  - make test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 15:01:03 +03:00
co-authored by Claude Opus 4.6
parent ae4aee3f66
commit 78fb8e372c
15 changed files with 222 additions and 68 deletions
+4 -4
View File
@@ -22,10 +22,10 @@ BOOKINGS_DB_NAME=bookings
BOOKINGS_DB_PORT=5434 BOOKINGS_DB_PORT=5434
# Начальная дата модельного времени для генерации демобазы # Начальная дата модельного времени для генерации демобазы
BOOKINGS_START_DATE=2017-01-01 BOOKINGS_START_DATE=2017-01-01
# Количество дней для первой генерации (держим малым, чтобы быстрее увидеть данные) # Количество дней для генерации с нуля (make bookings-generate); для make bookings-init не используется
BOOKINGS_INIT_DAYS=1 BOOKINGS_INIT_DAYS=60
# Количество джобов генератора. В учебном стенде поддерживается только значение 1. # Количество параллельных джобов генератора (1 = синхронно без dblink, 2+ = через dblink)
BOOKINGS_JOBS=1 BOOKINGS_JOBS=2
# Greenplum Configuration # Greenplum Configuration
GP_USER=gpadmin GP_USER=gpadmin
+108 -10
View File
@@ -2,14 +2,16 @@ SHELL := /bin/bash
UV := uv UV := uv
PYTHON_VERSION := 3.11 PYTHON_VERSION := 3.11
DEMODB_REPO := https://github.com/postgrespro/demodb.git DEMODB_REPO := https://github.com/postgrespro/demodb.git
DEMODB_COMMIT := d68de192850237719f09b47688d5f3fc94653ca6 DEMODB_COMMIT := 866e56f7fe54596a1d2a88f5f32f4aa3b2698121
BOOKINGS_JOBS ?= 1 BOOKINGS_JOBS ?= 2
BOOKINGS_START_DATE ?= 2017-01-01 BOOKINGS_START_DATE ?= 2017-01-01
BOOKINGS_INIT_DAYS ?= 1 BOOKINGS_INIT_DAYS ?= 60
.PHONY: up stop down clean airflow-init logs gp-psql ddl-gp \ .PHONY: up stop down clean airflow-init logs gp-psql ddl-gp \
bookings-check-jobs bookings-clone-demodb bookings-init bookings-psql bookings-generate-day \ bookings-check-jobs bookings-clone-demodb bookings-init bookings-generate bookings-dump \
bookings-psql bookings-generate-day \
dev-setup dev-sync dev-lock test lint fmt clean-venv build e2e-smoke e2e-etl dev-setup dev-sync dev-lock test lint fmt clean-venv build e2e-smoke e2e-etl
BOOKINGS_SEED := bookings/seed/demo.sql.xz
SHELL := /bin/bash SHELL := /bin/bash
up: up:
@@ -52,25 +54,35 @@ bookings-clone-demodb:
# Патчим generate/continue: при jobs=1 запускаем process_queue синхронно, без dblink # Патчим generate/continue: при jobs=1 запускаем process_queue синхронно, без dblink
if ! grep -q "Job 1 (local): ok" bookings/demodb/engine.sql; then \ if ! grep -q "Job 1 (local): ok" bookings/demodb/engine.sql; then \
if ! patch -d bookings/demodb -p1 --forward < bookings/patches/engine_jobs1_sync.patch; then \ if ! patch -d bookings/demodb -p1 --forward < bookings/patches/engine_jobs1_sync.patch; then \
echo "Не удалось применить патч engine_jobs1_sync.patch. Удалите bookings/demodb и повторите make bookings-init." >&2; \ echo "Не удалось применить патч engine_jobs1_sync.patch. Удалите bookings/demodb и повторите make bookings-generate." >&2; \
exit 1; \ exit 1; \
fi; \ fi; \
fi fi
# Делаем установку идемпотентной и принудительной: DROP DATABASE IF EXISTS demo WITH (FORCE) # Делаем установку идемпотентной и принудительной: DROP DATABASE IF EXISTS demo WITH (FORCE)
if ! grep -q "DROP DATABASE IF EXISTS demo WITH (FORCE);" bookings/demodb/install.sql; then \ if ! grep -q "DROP DATABASE IF EXISTS demo WITH (FORCE);" bookings/demodb/install.sql; then \
if ! patch -d bookings/demodb -p1 --forward < bookings/patches/install_drop_if_exists.patch; then \ if ! patch -d bookings/demodb -p1 --forward < bookings/patches/install_drop_if_exists.patch; then \
echo "Не удалось применить патч install_drop_if_exists.patch. Удалите bookings/demodb и повторите make bookings-init." >&2; \ echo "Не удалось применить патч install_drop_if_exists.patch. Удалите bookings/demodb и повторите make bookings-generate." >&2; \
exit 1; \
fi; \
fi
# Убираем хардкод gen.connstr без credentials — иначе VACUUM падает
if grep -q "gen.connstr = 'dbname=demo'" bookings/demodb/install.sql; then \
if ! patch -d bookings/demodb -p1 --forward < bookings/patches/install_connstr_no_hardcode.patch; then \
echo "Не удалось применить патч install_connstr_no_hardcode.patch. Удалите bookings/demodb и повторите make bookings-generate." >&2; \
exit 1; \ exit 1; \
fi; \ fi; \
fi fi
bookings-check-jobs: bookings-check-jobs:
@if [ "$(BOOKINGS_JOBS)" != "1" ]; then \ @case "$(BOOKINGS_JOBS)" in \
echo "Поддерживается только BOOKINGS_JOBS=1. Измените .env и повторите команду." >&2; \ ''|*[!0-9]*) echo "BOOKINGS_JOBS должен быть целым числом >= 1. Текущее значение: '$(BOOKINGS_JOBS)'." >&2; exit 1;; \
esac; \
if [ "$(BOOKINGS_JOBS)" -lt 1 ]; then \
echo "BOOKINGS_JOBS должен быть >= 1. Текущее значение: $(BOOKINGS_JOBS)." >&2; \
exit 1; \ exit 1; \
fi fi
bookings-init: bookings-check-jobs bookings-clone-demodb bookings-generate: bookings-check-jobs bookings-clone-demodb
docker compose -f docker-compose.yml up -d bookings-db docker compose -f docker-compose.yml up -d bookings-db
docker compose -f docker-compose.yml exec bookings-db bash -lc '\ docker compose -f docker-compose.yml exec bookings-db bash -lc '\
until PGPASSWORD="$$POSTGRES_PASSWORD" pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -h localhost; do \ until PGPASSWORD="$$POSTGRES_PASSWORD" pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -h localhost; do \
@@ -89,10 +101,96 @@ bookings-init: bookings-check-jobs bookings-clone-demodb
ALTER DATABASE demo SET bookings.init_days = '\''$(BOOKINGS_INIT_DAYS)'\''; \ ALTER DATABASE demo SET bookings.init_days = '\''$(BOOKINGS_INIT_DAYS)'\''; \
ALTER DATABASE demo SET bookings.jobs = '\''$(BOOKINGS_JOBS)'\'';" \ ALTER DATABASE demo SET bookings.jobs = '\''$(BOOKINGS_JOBS)'\'';" \
' '
# Генерируем первый день данных, чтобы база не оставалась пустой # Генерируем данные за $(BOOKINGS_INIT_DAYS) дней
docker compose -f docker-compose.yml exec bookings-db bash -lc '\ docker compose -f docker-compose.yml exec bookings-db bash -lc '\
PGPASSWORD="$$POSTGRES_PASSWORD" psql -v ON_ERROR_STOP=1 -U "$$POSTGRES_USER" -d demo -f /bookings/generate_next_day.sql \ PGPASSWORD="$$POSTGRES_PASSWORD" psql -v ON_ERROR_STOP=1 -U "$$POSTGRES_USER" -d demo -f /bookings/generate_next_day.sql \
' '
# Валидация: ключевые таблицы не должны быть пустыми
@docker compose -f docker-compose.yml exec bookings-db bash -lc '\
PGPASSWORD="$$POSTGRES_PASSWORD" psql -v ON_ERROR_STOP=1 -U "$$POSTGRES_USER" -d demo -tAc " \
SELECT format(E'\''%-25s %s'\'', t, cnt) \
FROM ( \
SELECT '\''bookings.bookings'\'' AS t, count(*) AS cnt FROM bookings.bookings \
UNION ALL \
SELECT '\''bookings.tickets'\'', count(*) FROM bookings.tickets \
UNION ALL \
SELECT '\''bookings.flights'\'', count(*) FROM bookings.flights \
UNION ALL \
SELECT '\''bookings.boarding_passes'\'', count(*) FROM bookings.boarding_passes \
) s ORDER BY t; \
" \
'
@docker compose -f docker-compose.yml exec bookings-db bash -lc '\
PGPASSWORD="$$POSTGRES_PASSWORD" psql -v ON_ERROR_STOP=1 -U "$$POSTGRES_USER" -d demo -tAc " \
DO \$$\$$ \
DECLARE v_cnt bigint; \
BEGIN \
SELECT count(*) INTO v_cnt FROM bookings.bookings; \
IF v_cnt = 0 THEN \
RAISE EXCEPTION '\''bookings.bookings пуста — генерация не сработала. Проверьте логи: SELECT * FROM gen.log ORDER BY at DESC LIMIT 10;'\''; \
END IF; \
SELECT count(*) INTO v_cnt FROM bookings.flights; \
IF v_cnt = 0 THEN \
RAISE EXCEPTION '\''bookings.flights пуста — попробуйте увеличить BOOKINGS_INIT_DAYS.'\''; \
END IF; \
END \$$\$$; \
" \
'
bookings-init: bookings-check-jobs
@if [ ! -f $(BOOKINGS_SEED) ]; then \
echo "Файл $(BOOKINGS_SEED) не найден. Используйте make bookings-generate для генерации с нуля." >&2; \
exit 1; \
fi
docker compose -f docker-compose.yml up -d bookings-db
docker compose -f docker-compose.yml exec bookings-db bash -lc '\
until PGPASSWORD="$$POSTGRES_PASSWORD" pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -h localhost; do \
echo "Waiting for bookings-db to become ready..."; \
sleep 1; \
done \
'
docker compose -f docker-compose.yml exec bookings-db bash -lc '\
PGPASSWORD="$$POSTGRES_PASSWORD" psql -v ON_ERROR_STOP=1 -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" \
-c "DROP DATABASE IF EXISTS demo WITH (FORCE);" \
'
@echo "Восстановление из дампа (~42 MB)..."
xz -dc $(BOOKINGS_SEED) | docker compose -f docker-compose.yml exec -T bookings-db bash -lc '\
PGPASSWORD="$$POSTGRES_PASSWORD" psql -v ON_ERROR_STOP=1 -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" \
'
# Применяем настройки из текущего окружения поверх дампа
docker compose -f docker-compose.yml exec bookings-db bash -lc '\
CONNSTR="dbname=demo user=$$POSTGRES_USER password=$$POSTGRES_PASSWORD"; \
PGPASSWORD="$$POSTGRES_PASSWORD" psql -v ON_ERROR_STOP=1 -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -c "\
ALTER DATABASE demo SET gen.connstr='\''$$CONNSTR'\''; \
ALTER DATABASE demo SET bookings.start_date = '\''$(BOOKINGS_START_DATE)'\''; \
ALTER DATABASE demo SET bookings.init_days = '\''$(BOOKINGS_INIT_DAYS)'\''; \
ALTER DATABASE demo SET bookings.jobs = '\''$(BOOKINGS_JOBS)'\'';" \
'
@echo "Восстановление завершено. Проверяем данные..."
@docker compose -f docker-compose.yml exec bookings-db bash -lc '\
PGPASSWORD="$$POSTGRES_PASSWORD" psql -U "$$POSTGRES_USER" -d demo -tAc " \
SELECT format(E'\''%-25s %s'\'', t, cnt) \
FROM ( \
SELECT '\''bookings.bookings'\'' AS t, count(*) AS cnt FROM bookings.bookings \
UNION ALL \
SELECT '\''bookings.tickets'\'', count(*) FROM bookings.tickets \
UNION ALL \
SELECT '\''bookings.flights'\'', count(*) FROM bookings.flights \
UNION ALL \
SELECT '\''bookings.boarding_passes'\'', count(*) FROM bookings.boarding_passes \
) s ORDER BY t; \
" \
'
bookings-dump:
@echo "Создание дампа demo → $(BOOKINGS_SEED)..."
mkdir -p bookings/seed
docker compose -f docker-compose.yml exec bookings-db bash -lc '\
PGPASSWORD="$$POSTGRES_PASSWORD" pg_dump -U "$$POSTGRES_USER" -d demo --format=plain --create \
' > /tmp/demo_dump_$$$$.sql
xz -9 < /tmp/demo_dump_$$$$.sql > $(BOOKINGS_SEED)
rm -f /tmp/demo_dump_$$$$.sql
@echo "Дамп сохранён: $$(ls -lh $(BOOKINGS_SEED) | awk '{print $$5}')"
bookings-psql: bookings-psql:
docker compose -f docker-compose.yml exec bookings-db bash -lc 'PGPASSWORD="$$POSTGRES_PASSWORD" psql -U "$$POSTGRES_USER" -d demo' docker compose -f docker-compose.yml exec bookings-db bash -lc 'PGPASSWORD="$$POSTGRES_PASSWORD" psql -U "$$POSTGRES_USER" -d demo'
+4 -3
View File
@@ -61,7 +61,8 @@ make up
3) Инициализируйте демо‑БД bookings: 3) Инициализируйте демо‑БД bookings:
```bash ```bash
make bookings-init make bookings-init # быстрое восстановление из seed-дампа (~18 сек), рекомендуется
# make bookings-generate # альтернатива: полная генерация с нуля (занимает часы)
``` ```
4) Подготовьте STG/ODS/DDS-объекты в Greenplum (выберите один вариант): 4) Подготовьте STG/ODS/DDS-объекты в Greenplum (выберите один вариант):
@@ -148,8 +149,8 @@ make clean # полный reset: удалить контейнер
| Проблема | Решение | | Проблема | Решение |
|----------|---------| |----------|---------|
| Airflow UI не открывается | Дождитесь сообщения `Listening at: http://0.0.0.0:8080` в логах (`make logs`) | | Airflow UI не открывается | Дождитесь сообщения `Listening at: http://0.0.0.0:8080` в логах (`make logs`) |
| `database "demo" does not exist` в bookingsDAG | Вы сделали reset с удалением volumes (`make clean` / `docker compose down -v`). Запустите `make bookings-init` и повторите DAG. | | `database "demo" does not exist` в bookingsDAG | Вы сделали reset с удалением volumes (`make clean` / `docker compose down -v`). Запустите `make bookings-init` (быстрое восстановление из дампа, ~18 сек) и повторите DAG. |
| `Поддерживается только bookings.jobs=1` или `BOOKINGS_JOBS=1` | В этом учебном стенде поддерживается только синхронный режим генерации. Установите `BOOKINGS_JOBS=1` и выполните `make bookings-init`. | | Ошибка `bookings.jobs должен быть >= 1` | Проверьте значение `BOOKINGS_JOBS` в `.env` — оно должно быть целым числом >= 1. По умолчанию `BOOKINGS_JOBS=2` (параллельная генерация через dblink). При `BOOKINGS_JOBS=1` генерация идёт синхронно. |
| Ошибка подключения к Greenplum | Убедитесь, что контейнер `greenplum` имеет статус `healthy` (`docker compose ps`) | | Ошибка подключения к Greenplum | Убедитесь, что контейнер `greenplum` имеет статус `healthy` (`docker compose ps`) |
| `protocol "pxf" does not exist` | Перезапустите `greenplum` и повторите `bookings_stg_ddl`/`make ddl-gp` — расширение `pxf` создаётся автоматически при старте контейнера. | | `protocol "pxf" does not exist` | Перезапустите `greenplum` и повторите `bookings_stg_ddl`/`make ddl-gp` — расширение `pxf` создаётся автоматически при старте контейнера. |
| DAG `bookings_to_gp_stage` ругается на отсутствующие таблицы stg | Запустите `bookings_stg_ddl` (или выполните `make ddl-gp`), затем повторите запуск | | DAG `bookings_to_gp_stage` ругается на отсутствующие таблицы stg | Запустите `bookings_stg_ddl` (или выполните `make ddl-gp`), затем повторите запуск |
+2 -2
View File
@@ -30,13 +30,13 @@
- `greenplum_conn` и `bookings_db` задаются через переменные `AIRFLOW_CONN_...` в docker-compose и могут не отображаться в списке, но `airflow connections get greenplum_conn` / `bookings_db` внутри контейнера должны отрабатывать без ошибок. - `greenplum_conn` и `bookings_db` задаются через переменные `AIRFLOW_CONN_...` в docker-compose и могут не отображаться в списке, но `airflow connections get greenplum_conn` / `bookings_db` внутри контейнера должны отрабатывать без ошибок.
- DAG `bookings_to_gp_stage` (полная проверка цепочки bookings → Greenplum STG): - DAG `bookings_to_gp_stage` (полная проверка цепочки bookings → Greenplum STG):
- предварительно выполнить один раз: `make bookings-init` (установка демобазы `demo` в контейнере `bookings-db`) и `make ddl-gp` (создаёт STG/ODS/DDS слои в Greenplum, включая внешние `*_ext` через PXF); - предварительно выполнить один раз: `make bookings-init` (быстрое восстановление демобазы `demo` из seed-дампа, ~18 сек) и `make ddl-gp` (создаёт STG/ODS/DDS слои в Greenplum, включая внешние `*_ext` через PXF);
- перед Trigger проверить, что в source реально есть данные (все значения должны быть `> 0`): - перед Trigger проверить, что в source реально есть данные (все значения должны быть `> 0`):
- `docker compose exec bookings-db psql -U bookings -d demo -At -c "SELECT COUNT(*) FROM bookings.bookings;"` - `docker compose exec bookings-db psql -U bookings -d demo -At -c "SELECT COUNT(*) FROM bookings.bookings;"`
- `docker compose exec bookings-db psql -U bookings -d demo -At -c "SELECT COUNT(*) FROM bookings.airports_data;"` - `docker compose exec bookings-db psql -U bookings -d demo -At -c "SELECT COUNT(*) FROM bookings.airports_data;"`
- `docker compose exec bookings-db psql -U bookings -d demo -At -c "SELECT COUNT(*) FROM bookings.airplanes_data;"` - `docker compose exec bookings-db psql -U bookings -d demo -At -c "SELECT COUNT(*) FROM bookings.airplanes_data;"`
- если хотя бы один `COUNT(*) = 0`, не запускать DAG: повторить `make bookings-init`; если после этого `bookings.bookings` всё ещё пустая, выполнить `make bookings-generate-day` и снова проверить `COUNT(*)`; - если хотя бы один `COUNT(*) = 0`, не запускать DAG: повторить `make bookings-init`; если после этого `bookings.bookings` всё ещё пустая, выполнить `make bookings-generate-day` и снова проверить `COUNT(*)`;
- важно: DAG `bookings_stg_ddl` **не** создаёт базу `demo` в `bookings-db`; если вы делали `docker compose down -v` / `make clean`, `make bookings-init` обязателен; - важно: DAG `bookings_stg_ddl` **не** создаёт базу `demo` в `bookings-db`; если вы делали `docker compose down -v` / `make clean`, `make bookings-init` обязателен (быстрое восстановление из seed-дампа);
- включить DAG `bookings_to_gp_stage` и запустить `Trigger DAG`; - включить DAG `bookings_to_gp_stage` и запустить `Trigger DAG`;
- убедиться, что все задачи завершились со статусом Success (включая загрузки справочников/транзакций и DQ); - убедиться, что все задачи завершились со статусом Success (включая загрузки справочников/транзакций и DQ);
- при желании проверить данные: в `bookings-db` появился новый день, а в Greenplum в `stg.bookings` — строки с актуальным `batch_id` (см. пример запросов в разделе 5). - при желании проверить данные: в `bookings-db` появился новый день, а в Greenplum в `stg.bookings` — строки с актуальным `batch_id` (см. пример запросов в разделе 5).
+2 -2
View File
@@ -28,7 +28,7 @@
+ ручное тестирование (make up, запуск DAG'ов, проверка данных). + ручное тестирование (make up, запуск DAG'ов, проверка данных).
- [ ] Протестировать полный ETL-цикл с нуля - [ ] Протестировать полный ETL-цикл с нуля
(make up → bookings-init → STG → ODS → DDS → DM) (make up → bookings-init (восстановление из дампа) → STG → ODS → DDS → DM)
- [ ] Прогнать инкремент (bookings-generate-day → повторный запуск DAG'ов) - [ ] Прогнать инкремент (bookings-generate-day → повторный запуск DAG'ов)
- [ ] Почистить код эталонного среза - [ ] Почистить код эталонного среза
- [ ] Актуализировать README и документацию - [ ] Актуализировать README и документацию
@@ -119,7 +119,7 @@
- диагностика текущего кейса: `docs/internal/pxf_bookings.md` (раздел «Известная проблема»). - диагностика текущего кейса: `docs/internal/pxf_bookings.md` (раздел «Известная проблема»).
- [x] Разобраться с генератором demodb: - [x] Разобраться с генератором demodb:
- после `make bookings-init` таблица `bookings.bookings` остаётся пустой; - после `make bookings-generate` таблица `bookings.bookings` остаётся пустой;
- патчи `bookings/patches/engine_jobs1_sync.patch` и `bookings/patches/install_drop_if_exists.patch` - патчи `bookings/patches/engine_jobs1_sync.patch` и `bookings/patches/install_drop_if_exists.patch`
падают при применении (hunk failed / garbage in patch); падают при применении (hunk failed / garbage in patch);
- из‑за этого DAG `bookings_to_gp_stage` валится на проверках (источник пустой). - из‑за этого DAG `bookings_to_gp_stage` валится на проверках (источник пустой).
+15 -6
View File
@@ -4,29 +4,38 @@
На первом этапе мы: На первом этапе мы:
- поднимаем отдельный контейнер `bookings-db` с Postgres; - поднимаем отдельный контейнер `bookings-db` с Postgres;
- устанавливаем в нём генератор демобазы `demodb` (репозиторий `postgrespro/demodb`); - инициализируем демобазу одним из двух способов (см. ниже);
- генерируем данные «день за днём» с помощью `make`‑команд. - генерируем данные «день за днём» с помощью `make`‑команд.
Основные команды см. в корневом `Makefile` (`bookings-init`, `bookings-generate-day`, `bookings-psql`) и в `README.md` проекта. ### Два способа инициализации
| Команда | Что делает | Время | Для кого |
|---------|-----------|-------|----------|
| `make bookings-init` | Быстрое восстановление из seed-дампа | ~18 сек | **Студенты** (рекомендуется по умолчанию) |
| `make bookings-generate` | Полная генерация с нуля через генератор demodb | часы | Разработчики, пересоздание дампа |
Основные команды см. в корневом `Makefile` (`bookings-init`, `bookings-generate`, `bookings-generate-day`, `bookings-psql`) и в `README.md` проекта.
## Источник и версия ## Источник и версия
- Репозиторий демобазы: `postgrespro/demodb`. - Репозиторий демобазы: `postgrespro/demodb`.
- Закреплённый коммит: `d68de192850237719f09b47688d5f3fc94653ca6` (см. `DEMODB_COMMIT` в корневом `Makefile`). - Закреплённый коммит: `866e56f7` (см. `DEMODB_COMMIT` в корневом `Makefile`).
## Что мы патчим в demodb ## Что мы патчим в demodb
- `install.sql`: `DROP DATABASE IF EXISTS demo WITH (FORCE)` — установка не падает, даже если демобазу держат активные сессии (например, из Airflow). - `install.sql`: `DROP DATABASE IF EXISTS demo WITH (FORCE)` — установка не падает, даже если демобазу держат активные сессии (например, из Airflow).
- `engine.sql`: два изменения в `engine_jobs1_sync.patch`: - `engine.sql`: два изменения в `engine_jobs1_sync.patch`:
- `busy()` игнорирует свой `pid`, чтобы не считать собственное подключение занятым; - `busy()` игнорирует свой `pid`, чтобы не считать собственное подключение занятым;
- `continue()` при `jobs=1` вызывает `process_queue` синхронно (без `dblink`), иначе генерация обрывается при выходе из `psql` и данных не появляется. - `continue()` при `jobs=1` вызывает `process_queue` синхронно (без `dblink`), иначе генерация обрывается при выходе из `psql` и данных не появляется.
- Режим эксплуатации в этом стенде: только `jobs=1` (`BOOKINGS_JOBS=1`). - `install.sql`: удалён хардкод `gen.connstr` без credentials (`install_connstr_no_hardcode.patch`).
- Патчи применяются автоматически в `make bookings-init`. Если что-то пошло не так, их можно накатить вручную: - Дефолт: `BOOKINGS_JOBS=2`. При `jobs=1` генерация синхронная (без dblink), при `jobs>1` — через dblink.
- Патчи применяются автоматически в `make bookings-generate` (генерация с нуля). Если что-то пошло не так, их можно накатить вручную:
``` ```
patch -d bookings/demodb -p1 --forward < bookings/patches/install_drop_if_exists.patch patch -d bookings/demodb -p1 --forward < bookings/patches/install_drop_if_exists.patch
patch -d bookings/demodb -p1 --forward < bookings/patches/engine_jobs1_sync.patch patch -d bookings/demodb -p1 --forward < bookings/patches/engine_jobs1_sync.patch
``` ```
## Быстрая проверка после init/обновления ## Быстрая проверка после init/обновления
- `make bookings-init` должен завершиться без ошибок; в `bookings.bookings` ожидаем >0 строк (примерно 15k). - `make bookings-init` (восстановление из дампа) должен завершиться без ошибок; в `bookings.bookings` ожидаем >0 строк (примерно 15k).
- `make bookings-generate` (генерация с нуля) тоже должен дать >0 строк, но занимает значительно больше времени.
- `make bookings-generate-day` добавляет следующий день после `max(book_date)`. - `make bookings-generate-day` добавляет следующий день после `max(book_date)`.
- Ручной вызов генерации из psql/DBeaver — только через DO-блок (подзапрос в аргументах `CALL` не работает): - Ручной вызов генерации из psql/DBeaver — только через DO-блок (подзапрос в аргументах `CALL` не работает):
```sql ```sql
+22 -4
View File
@@ -11,12 +11,11 @@ DECLARE
BEGIN BEGIN
-- Проверяем, что демобаза установлена -- Проверяем, что демобаза установлена
IF to_regclass('bookings.bookings') IS NULL THEN IF to_regclass('bookings.bookings') IS NULL THEN
RAISE EXCEPTION 'Таблица bookings.bookings не найдена. Сначала выполните make bookings-init.'; RAISE EXCEPTION 'Таблица bookings.bookings не найдена. Сначала выполните make bookings-init или make bookings-generate.';
END IF; END IF;
-- В учебном стенде поддерживается только jobs=1, иначе генерация нестабильна. IF v_jobs < 1 THEN
IF v_jobs <> 1 THEN RAISE EXCEPTION 'bookings.jobs должен быть >= 1. Текущее значение: %.', v_jobs;
RAISE EXCEPTION 'Поддерживается только bookings.jobs=1. Текущее значение: %. Установите BOOKINGS_JOBS=1 и выполните make bookings-init.', v_jobs;
END IF; END IF;
-- Ищем последнюю сгенерированную дату -- Ищем последнюю сгенерированную дату
@@ -39,6 +38,17 @@ BEGIN
CALL continue(v_end_date, v_jobs); CALL continue(v_end_date, v_jobs);
END IF; END IF;
-- continue() делает TRUNCATE gen.stat_jobs → AccessExclusiveLock.
-- Без COMMIT воркеры не могут INSERT INTO gen.stat_jobs → deadlock.
COMMIT;
-- При jobs>1 воркерам нужно время, чтобы подключиться через dblink и
-- выставить application_name='Airlines processor'. Без паузы busy()
-- сразу вернёт false (воркеры ещё не видны в pg_stat_activity).
IF v_jobs > 1 THEN
PERFORM pg_sleep(3);
END IF;
-- Ждём завершения фоновых джобов генератора, чтобы данные успели записаться -- Ждём завершения фоновых джобов генератора, чтобы данные успели записаться
WHILE busy() LOOP WHILE busy() LOOP
PERFORM pg_sleep(1); PERFORM pg_sleep(1);
@@ -50,4 +60,12 @@ BEGIN
IF v_bookings_cnt = 0 THEN IF v_bookings_cnt = 0 THEN
RAISE EXCEPTION 'Генератор demodb завершился, но bookings.bookings пустая. Проверьте применение патчей и логи генератора.'; RAISE EXCEPTION 'Генератор demodb завершился, но bookings.bookings пустая. Проверьте применение патчей и логи генератора.';
END IF; END IF;
-- Генерация закончена — возвращаем synchronous_commit = on и сбрасываем
-- буферы на диск. Без этого docker compose down может убить PostgreSQL
-- до записи WAL → данные пропадут (особенно на WSL2).
ALTER DATABASE demo SET synchronous_commit = on;
SET synchronous_commit = on;
END $$; END $$;
CHECKPOINT;
@@ -0,0 +1,14 @@
--- a/install.sql
+++ b/install.sql
@@ -24,8 +24,9 @@
-- use UTC to avoid daylight-saving problems
ALTER DATABASE demo SET timezone = 'Etc/UTC';
--- connection string for dblink
-ALTER DATABASE demo SET gen.connstr = 'dbname=demo';
+-- Строку подключения для dblink (gen.connstr) задаёт Makefile
+-- с credentials текущего пользователя БД. Не хардкодим здесь,
+-- иначе VACUUM и другие dblink-вызовы падают без пароля.
-- airlines company name
ALTER DATABASE demo SET gen.airlines_name = 'PostgresPro';
Binary file not shown.
+9 -9
View File
@@ -1,7 +1,7 @@
# Проблемы bookings-db (demodb) # Проблемы bookings-db (demodb)
> Дата обнаружения: 2026-03-08 > Дата обнаружения: 2026-03-08
> Зафиксированный коммит demodb: `d68de192850237719f09b47688d5f3fc94653ca6` > Зафиксированный коммит demodb: `866e56f7fe54596a1d2a88f5f32f4aa3b2698121`
--- ---
@@ -9,7 +9,7 @@
### Симптом ### Симптом
После `make bookings-init` таблицы в demo-базе существуют, но **пустые**. После `make bookings-generate` (генерация с нуля) таблицы в demo-базе существуют, но **пустые**.
Повторные `make bookings-generate-day` тоже дают 0 строк. Повторные `make bookings-generate-day` тоже дают 0 строк.
### Корневая причина ### Корневая причина
@@ -22,7 +22,7 @@ ALTER DATABASE demo SET gen.connstr = 'dbname=demo';
Без `user` и `password`. Makefile устанавливает правильный connstr Без `user` и `password`. Makefile устанавливает правильный connstr
(с `user=bookings password=bookings`) **после** `install.sql`, но при повторном (с `user=bookings password=bookings`) **после** `install.sql`, но при повторном
`make bookings-init` порядок тот же: install.sql перезаписывает → Makefile `make bookings-generate` порядок тот же: install.sql перезаписывает → Makefile
восстанавливает. Если что-то идёт не так между этими шагами, connstr остаётся восстанавливает. Если что-то идёт не так между этими шагами, connstr остаётся
без credentials. без credentials.
@@ -47,7 +47,7 @@ SELECT * FROM gen.log ORDER BY at DESC LIMIT 5;
### Воркэраунд ### Воркэраунд
```sql ```sql
-- Выполнить на bookings-db ПОСЛЕ install.sql: -- Выполнить на bookings-db ПОСЛЕ install.sql (актуально при make bookings-generate):
ALTER DATABASE demo SET gen.connstr = 'dbname=demo user=bookings password=bookings'; ALTER DATABASE demo SET gen.connstr = 'dbname=demo user=bookings password=bookings';
-- Затем переподключиться к demo и запустить генерацию заново. -- Затем переподключиться к demo и запустить генерацию заново.
``` ```
@@ -135,9 +135,9 @@ Boarding passes создаются при событиях CHECK-IN и BOARDING,
## Рекомендации (TODO) ## Рекомендации (TODO)
- [ ] Обновить demodb до последнего коммита — проверить, исправлены ли баги - [x] Обновить demodb до последнего коммита (`866e56f`) — upstream только README-правки, баги не исправлены
- [ ] Добавить патч для gen.connstr (если не исправлено upstream) - [x] Добавить патч для gen.connstr (`install_connstr_no_hardcode.patch`) — убирает хардкод без credentials
- [ ] Увеличить BOOKINGS_INIT_DAYS до 30-60 - [x] Увеличить BOOKINGS_INIT_DAYS до 60 (в Makefile и .env)
- [ ] Добавить валидацию после init (`make bookings-init` проверяет count > 0) - [x] Добавить валидацию после init (count > 0 для bookings, flights; вывод counts всех таблиц)
- [ ] Решить проблему UX с временем генерации (дамп или prebuilt image) - [ ] Решить проблему UX с временем генерации (дамп или prebuilt image)
- [ ] Проверить, появляются ли boarding_passes при init_days=90 + исправленном connstr - [ ] Проверить, появляются ли boarding_passes при init_days=60 + исправленном connstr (нужен запуск стенда)
+10 -6
View File
@@ -4,19 +4,23 @@ _Внутренний файл для наставника: поясняет, к
- Контейнер `bookings-db` — отдельный сервис Postgres из `docker-compose.yml`, база по умолчанию `demo` (из upstream demodb), без переименований. - Контейнер `bookings-db` — отдельный сервис Postgres из `docker-compose.yml`, база по умолчанию `demo` (из upstream demodb), без переименований.
- Доступ снаружи не блокируем (порт `5434` по умолчанию), чтобы позже читать через PXF и подключаться из Greenplum. - Доступ снаружи не блокируем (порт `5434` по умолчанию), чтобы позже читать через PXF и подключаться из Greenplum.
- Инициализация (`make bookings-init`): поднимает контейнер, клонирует demodb с закреплённым коммитом, накладывает патчи (`engine`: `jobs=1` синхронно + `busy()` игнорирует свой pid; `install.sql`: `DROP DATABASE IF EXISTS`), ждёт `pg_isready`, ставит `gen.connstr` и GUC `bookings.start_date/init_days/jobs`, затем запускает `/bookings/generate_next_day.sql` через `psql -f`. Значения по умолчанию: стартовая дата 2017-01-01, `init_days=1`, `jobs=1`. - Инициализация: два способа:
- `make bookings-init` (рекомендуется): быстрое восстановление из seed-дампа (~18 сек).
- `make bookings-generate` (для разработчиков): полная генерация с нуля — клонирует demodb с закреплённым коммитом, накладывает патчи (`engine`: `jobs=1` синхронно + `busy()` игнорирует свой pid; `install.sql`: `DROP DATABASE IF EXISTS`, `connstr` без хардкода), ждёт `pg_isready`, ставит `gen.connstr` и GUC `bookings.start_date/init_days/jobs`, затем запускает `/bookings/generate_next_day.sql` через `psql -f`. Значения по умолчанию: стартовая дата 2017-01-01, `init_days=60`, `jobs=2`.
- Генерация следующего дня: `make bookings-generate-day` прогоняет тот же SQL (читает GUC, вызывает `generate/continue`, ждёт `busy()`, закрывает dblink). При `jobs=1` всё синхронно, без dblink. - Генерация следующего дня: `make bookings-generate-day` прогоняет тот же SQL (читает GUC, вызывает `generate/continue`, ждёт `busy()`, закрывает dblink). При `jobs=1` всё синхронно, без dblink.
- Исходники demodb: клонируем по требованию с фиксированным хешем, кладём в `bookings/demodb/``.gitignore`), патчи лежат в `bookings/patches/` и применяются автоматически. - Исходники demodb: клонируем по требованию с фиксированным хешем, кладём в `bookings/demodb/``.gitignore`), патчи лежат в `bookings/patches/` и применяются автоматически при `make bookings-generate`.
- Документация: в README описаны команды (`bookings-init`, проверка данных, генерация дня), параметры `.env`; настройка PXF/ETL — следующий этап. - Документация: в README описаны команды (`bookings-init`, `bookings-generate`, проверка данных, генерация дня), параметры `.env`; настройка PXF/ETL — следующий этап.
## Текущее состояние ## Текущее состояние
- `Makefile` теперь автоматически применяет патчи (`engine_jobs1_sync.patch`, `install_drop_if_exists.patch`), ждёт готовности Postgres через `pg_isready`, запускает `install.sql`, выставляет `gen.connstr`/GUC и вызывает `generate_next_day.sql` через `psql -f`. - `make bookings-init` — быстрое восстановление из seed-дампа (~18 сек), рекомендуется для студентов.
- Дефолты: `BOOKINGS_START_DATE=2017-01-01`, `BOOKINGS_INIT_DAYS=1`, `BOOKINGS_JOBS=1`. При `jobs=1` генерация идёт синхронно без dblink, `busy()` не учитывает текущую сессию. - `make bookings-generate` — полная генерация с нуля: автоматически применяет патчи (`engine_jobs1_sync.patch`, `install_drop_if_exists.patch`), ждёт готовности Postgres через `pg_isready`, запускает `install.sql`, выставляет `gen.connstr`/GUC и вызывает `generate_next_day.sql` через `psql -f`.
- Дефолты: `BOOKINGS_START_DATE=2017-01-01`, `BOOKINGS_INIT_DAYS=60`, `BOOKINGS_JOBS=2`. При `jobs=1` генерация идёт синхронно без dblink, `busy()` не учитывает текущую сессию.
- `.env.example`/README обновлены под новые дефолты; каталог `bookings/demodb/` в `.gitignore`. - `.env.example`/README обновлены под новые дефолты; каталог `bookings/demodb/` в `.gitignore`.
- Патчи лежат в `bookings/patches/` и накладываются при `bookings-clone-demodb`. - Патчи лежат в `bookings/patches/` и накладываются при `bookings-clone-demodb`.
## Текущее состояние тестов/проблем ## Текущее состояние тестов/проблем
- Чистый прогон `make bookings-init` (после `docker compose down -v` и удаления `bookings/demodb`) проходит за ~1,5 минуты: база ставится, `busy()``f`, `bookings.bookings` от `2017-01-01 00:00:18` до `2017-01-01 23:59:59`. - Чистый прогон `make bookings-init` (восстановление из seed-дампа) проходит за ~18 секунд.
- Чистый прогон `make bookings-generate` (после `docker compose down -v` и удаления `bookings/demodb`) проходит за ~1,5 минуты: база ставится, `busy()``f`, `bookings.bookings` от `2017-01-01 00:00:18` до `2017-01-01 23:59:59`.
- Ранее зависание на `busy()` при `jobs=1` лечится патчем: `process_queue` теперь синхронный, а `busy()` игнорирует текущий backend. - Ранее зависание на `busy()` при `jobs=1` лечится патчем: `process_queue` теперь синхронный, а `busy()` игнорирует текущий backend.
- Данных пока только на 1 день по умолчанию, чтобы генерация не занимала много времени. - Данных пока только на 1 день по умолчанию, чтобы генерация не занимала много времени.
+2 -2
View File
@@ -131,8 +131,8 @@ make fmt
- `BOOKINGS_DB_USER`, `BOOKINGS_DB_PASSWORD` - `BOOKINGS_DB_USER`, `BOOKINGS_DB_PASSWORD`
- `BOOKINGS_DB_PORT` — внешний порт (по умолчанию `5434`) - `BOOKINGS_DB_PORT` — внешний порт (по умолчанию `5434`)
- `BOOKINGS_START_DATE` — стартовая дата модельного времени - `BOOKINGS_START_DATE` — стартовая дата модельного времени
- `BOOKINGS_INIT_DAYS` — сколько дней генерировать при первом `make bookings-init` - `BOOKINGS_INIT_DAYS` — сколько дней генерировать при `make bookings-generate` (генерация с нуля)
- `BOOKINGS_JOBS` — число джобов генератора; в учебном стенде поддерживается только `1` - `BOOKINGS_JOBS` — число джобов генератора (по умолчанию `2`)
### CSV pipeline (побочный пример) ### CSV pipeline (побочный пример)
+1 -1
View File
@@ -49,7 +49,7 @@
- логическая дата запуска DAG (`{{ ds }}`) не влияет на выбор дня генерации; - логическая дата запуска DAG (`{{ ds }}`) не влияет на выбор дня генерации;
- скрипт всегда смотрит на `max(book_date)` и добавляет **следующий** день (или несколько стартовых дней, если база пуста). - скрипт всегда смотрит на `max(book_date)` и добавляет **следующий** день (или несколько стартовых дней, если база пуста).
4. Сделайте вывод: генератор всегда «шагает» по датам вперёд от максимальной даты, поэтому: 4. Сделайте вывод: генератор всегда «шагает» по датам вперёд от максимальной даты, поэтому:
- при `make bookings-init` вы получаете `BOOKINGS_INIT_DAYS` дней начиная с `BOOKINGS_START_DATE`; - при `make bookings-init` вы получаете готовые данные из seed-дампа (при `make bookings-generate` генератор создаст `BOOKINGS_INIT_DAYS` дней начиная с `BOOKINGS_START_DATE`);
- при последующих вызовах (`make bookings-generate-day` или DAG) добавляется ровно один новый день. - при последующих вызовах (`make bookings-generate-day` или DAG) добавляется ровно один новый день.
--- ---
+17 -15
View File
@@ -2,18 +2,20 @@
## Контекст ## Контекст
В стенде используется демобаза bookings из репозитория `postgrespro/demodb`, закреплённая на коммите `d68de192850237719f09b47688d5f3fc94653ca6` (см. `DEMODB_COMMIT` в `Makefile`). В стенде используется демобаза bookings из репозитория `postgrespro/demodb`, закреплённая на коммите `866e56f7fe54596a1d2a88f5f32f4aa3b2698121` (см. `DEMODB_COMMIT` в `Makefile`).
Инициализация источника для ETL выполняется командой `make bookings-init`: Инициализация источника для ETL выполняется двумя способами:
- клонирует demodb в `bookings/demodb/`; - `make bookings-init` — быстрое восстановление из seed-дампа (~18 сек, рекомендуется для студентов);
- пытается применить патчи из `bookings/patches/`; - `make bookings-generate` — полная генерация с нуля (для разработчиков):
- запускает `install.sql` в контейнере `bookings-db`; - клонирует demodb в `bookings/demodb/`;
- выставляет GUC-параметры (`gen.connstr`, `bookings.start_date/init_days/jobs`); - пытается применить патчи из `bookings/patches/`;
- запускает `/bookings/generate_next_day.sql` (должен сгенерировать минимум 1 день данных). - запускает `install.sql` в контейнере `bookings-db`;
- выставляет GUC-параметры (`gen.connstr`, `bookings.start_date/init_days/jobs`);
- запускает `/bookings/generate_next_day.sql` (должен сгенерировать минимум 1 день данных).
## Симптомы (как в TODO) ## Симптомы (как в TODO)
- после `make bookings-init` таблица `bookings.bookings` остаётся пустой; - после `make bookings-generate` таблица `bookings.bookings` остаётся пустой;
- патчи `bookings/patches/engine_jobs1_sync.patch` и `bookings/patches/install_drop_if_exists.patch` падают при применении; - патчи `bookings/patches/engine_jobs1_sync.patch` и `bookings/patches/install_drop_if_exists.patch` падают при применении;
- из‑за этого DAG `bookings_to_gp_stage` валится на проверках (источник пустой). - из‑за этого DAG `bookings_to_gp_stage` валится на проверках (источник пустой).
@@ -24,11 +26,11 @@
2) `install_drop_if_exists.patch` устарел относительно закреплённого коммита demodb: в `install.sql` уже есть `DROP DATABASE IF EXISTS demo;`, поэтому hunk “не находится” и патч не накатывается. 2) `install_drop_if_exists.patch` устарел относительно закреплённого коммита demodb: в `install.sql` уже есть `DROP DATABASE IF EXISTS demo;`, поэтому hunk “не находится” и патч не накатывается.
3) Ошибки патча сейчас замаскированы в `Makefile` через `|| true`, поэтому `make bookings-init` может завершаться “успешно”, хотя критичные правки в demodb не применились. 3) Ошибки патча сейчас замаскированы в `Makefile` через `|| true`, поэтому `make bookings-generate` может завершаться “успешно”, хотя критичные правки в demodb не применились.
## Цель фикса ## Цель фикса
- `make bookings-init` воспроизводимо создаёт и наполняет `demo.bookings.bookings` (>0 строк). - `make bookings-init` (восстановление из дампа) и `make bookings-generate` (генерация с нуля) воспроизводимо создают и наполняют `demo.bookings.bookings` (>0 строк).
- Если патчи не применяются — процесс останавливается с понятным сообщением, что делать дальше. - Если патчи не применяются — процесс останавливается с понятным сообщением, что делать дальше.
- Патчи соответствуют закреплённому коммиту demodb и применяются идемпотентно. - Патчи соответствуют закреплённому коммиту demodb и применяются идемпотентно.
@@ -37,7 +39,7 @@
1) Чистое воспроизведение: 1) Чистое воспроизведение:
- `make clean` - `make clean`
- `rm -rf bookings/demodb` - `rm -rf bookings/demodb`
- `make bookings-init` - `make bookings-generate`
2) Проверка данных: 2) Проверка данных:
- `make bookings-psql` - `make bookings-psql`
@@ -67,12 +69,12 @@
- было: `DROP DATABASE IF EXISTS demo;` - было: `DROP DATABASE IF EXISTS demo;`
- стало: `DROP DATABASE IF EXISTS demo WITH (FORCE);` - стало: `DROP DATABASE IF EXISTS demo WITH (FORCE);`
### Шаг 2. Сделать `make bookings-init` fail-fast на проблемах с патчами ### Шаг 2. Сделать `make bookings-generate` fail-fast на проблемах с патчами
В `Makefile`: В `Makefile`:
- убрать `|| true` у применения патчей; - убрать `|| true` у применения патчей;
- при ошибке патча — завершать `make` с ненулевым кодом и короткой подсказкой: - при ошибке патча — завершать `make` с ненулевым кодом и короткой подсказкой:
- “удалите `bookings/demodb` и повторите `make bookings-init`”, - “удалите `bookings/demodb` и повторите `make bookings-generate`”,
- “если не помогло — проверьте, что `DEMODB_COMMIT` не менялся и патчи собраны под него”. - “если не помогло — проверьте, что `DEMODB_COMMIT` не менялся и патчи собраны под него”.
### Шаг 3. Добавить “защиту от тихого пустого результата” ### Шаг 3. Добавить “защиту от тихого пустого результата”
@@ -85,8 +87,8 @@
## Проверка (критерии готовности) ## Проверка (критерии готовности)
- `make clean && rm -rf bookings/demodb && make bookings-init` завершается без ошибок. - `make clean && rm -rf bookings/demodb && make bookings-generate` завершается без ошибок.
- `make bookings-psql``SELECT COUNT(*) FROM bookings.bookings;` возвращает `> 0`. - `make bookings-psql``SELECT COUNT(*) FROM bookings.bookings;` возвращает `> 0` (для обоих способов: `bookings-init` и `bookings-generate`).
- `make bookings-generate-day` добавляет следующий день: - `make bookings-generate-day` добавляет следующий день:
- `max(book_date)` сдвигается на +1 сутки. - `max(book_date)` сдвигается на +1 сутки.
- `./scripts/e2e_smoke.sh` проходит до проверки `stg.bookings` (или хотя бы DAG `bookings_to_gp_stage` перестаёт падать на “источник пустой”). - `./scripts/e2e_smoke.sh` проходит до проверки `stg.bookings` (или хотя бы DAG `bookings_to_gp_stage` перестаёт падать на “источник пустой”).
+12 -4
View File
@@ -14,12 +14,11 @@ DECLARE
BEGIN BEGIN
-- Проверяем, что демобаза установлена -- Проверяем, что демобаза установлена
IF to_regclass('bookings.bookings') IS NULL THEN IF to_regclass('bookings.bookings') IS NULL THEN
RAISE EXCEPTION 'Таблица bookings.bookings не найдена. Сначала выполните make bookings-init.'; RAISE EXCEPTION 'Таблица bookings.bookings не найдена. Сначала выполните make bookings-init или make bookings-generate.';
END IF; END IF;
-- В учебном стенде поддерживается только jobs=1, иначе генерация нестабильна. IF v_jobs < 1 THEN
IF v_jobs <> 1 THEN RAISE EXCEPTION 'bookings.jobs должен быть >= 1. Текущее значение: %.', v_jobs;
RAISE EXCEPTION 'Поддерживается только bookings.jobs=1. Текущее значение: %. Установите BOOKINGS_JOBS=1 и выполните make bookings-init.', v_jobs;
END IF; END IF;
-- Ищем последнюю сгенерированную дату -- Ищем последнюю сгенерированную дату
@@ -42,10 +41,19 @@ BEGIN
CALL continue(v_end_date, v_jobs); CALL continue(v_end_date, v_jobs);
END IF; END IF;
-- continue() делает TRUNCATE gen.stat_jobs → AccessExclusiveLock.
-- Без COMMIT воркеры не могут INSERT INTO gen.stat_jobs → deadlock.
COMMIT;
RAISE NOTICE 'Сгенерированы данные в bookings.bookings за интервал [% - %).', RAISE NOTICE 'Сгенерированы данные в bookings.bookings за интервал [% - %).',
date_trunc('day', v_start_date), date_trunc('day', v_start_date),
date_trunc('day', v_end_date); date_trunc('day', v_end_date);
-- При jobs>1 воркерам нужно время, чтобы подключиться через dblink
IF v_jobs > 1 THEN
PERFORM pg_sleep(3);
END IF;
-- Ждём завершения фоновых джобов генератора, чтобы данные успели записаться -- Ждём завершения фоновых джобов генератора, чтобы данные успели записаться
WHILE busy() LOOP WHILE busy() LOOP
PERFORM pg_sleep(1); PERFORM pg_sleep(1);