- Зачем:
- make bookings-generate-day зависал на 35+ мин в WHILE busy() LOOP при BOOKINGS_JOBS=2,
а VACUUM ANALYZE всей БД внутри process_queue() добавлял минуты к каждому инкременту.
- Что:
- заменён опрос busy() (pg_stat_activity) на dblink_is_busy() — прямая проверка
состояния каждого dblink-соединения, без зависимости от application_name и state.
- перед continue() удаляются VACUUM-ивенты из gen.events — они бессмысленны для +1 дня.
- Проверка:
- make bookings-init && make bookings-generate-day BOOKINGS_JOBS=2 (~5-8 мин, без зависания).
- DAG bookings_to_gp_stage: все 20 тасков success.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
80 lines
4.3 KiB
SQL
80 lines
4.3 KiB
SQL
-- Если база пуста, берём стартовую дату из GUC bookings.start_date (по умолчанию: 2017-01-01).
|
|
DO $$
|
|
DECLARE
|
|
v_max_book_date timestamptz;
|
|
v_start_date timestamptz;
|
|
v_end_date timestamptz;
|
|
v_bookings_cnt bigint;
|
|
v_jobs integer := COALESCE(current_setting('bookings.jobs', true), '1')::integer;
|
|
v_init_days integer := COALESCE(current_setting('bookings.init_days', true), '1')::integer;
|
|
v_start_cfg text := COALESCE(current_setting('bookings.start_date', true), '2017-01-01');
|
|
BEGIN
|
|
-- Проверяем, что демобаза установлена
|
|
IF to_regclass('bookings.bookings') IS NULL THEN
|
|
RAISE EXCEPTION 'Таблица bookings.bookings не найдена. Сначала выполните make bookings-init или make bookings-generate.';
|
|
END IF;
|
|
|
|
IF v_jobs < 1 THEN
|
|
RAISE EXCEPTION 'bookings.jobs должен быть >= 1. Текущее значение: %.', v_jobs;
|
|
END IF;
|
|
|
|
-- Ищем последнюю сгенерированную дату
|
|
SELECT max(book_date) INTO v_max_book_date FROM bookings.bookings;
|
|
|
|
IF v_max_book_date IS NULL THEN
|
|
-- База пустая: берём стартовую дату из конфигурации (или дефолтную)
|
|
v_start_date := date_trunc('day', v_start_cfg::timestamptz);
|
|
ELSE
|
|
-- Продолжаем с дня, следующего за максимальной датой
|
|
v_start_date := date_trunc('day', v_max_book_date) + interval '1 day';
|
|
END IF;
|
|
|
|
-- Первая генерация вызывает generate(), последующие — continue()
|
|
IF v_max_book_date IS NULL THEN
|
|
v_end_date := v_start_date + (v_init_days || ' days')::interval;
|
|
CALL generate(v_start_date, v_end_date, v_jobs);
|
|
ELSE
|
|
v_end_date := v_start_date + interval '1 day';
|
|
|
|
-- Убираем VACUUM-ивенты из очереди: генератор demodb кладёт
|
|
-- VACUUM ANALYZE всей БД каждую неделю модельного времени.
|
|
-- На 500k+ строках это занимает минуты и бессмысленно для +1 дня.
|
|
DELETE FROM gen.events WHERE type = 'VACUUM';
|
|
|
|
CALL continue(v_end_date, v_jobs);
|
|
END IF;
|
|
|
|
-- continue() делает TRUNCATE gen.stat_jobs → AccessExclusiveLock.
|
|
-- Без COMMIT воркеры не могут INSERT INTO gen.stat_jobs → deadlock.
|
|
COMMIT;
|
|
|
|
-- Ждём завершения каждого воркера через dblink_is_busy().
|
|
-- Раньше опрашивали busy() (pg_stat_activity + application_name),
|
|
-- но это ненадёжно: воркер может обрабатывать VACUUM ANALYZE (десятки минут),
|
|
-- или зависнуть в пустой очереди — а busy() не отличает «полезную работу»
|
|
-- от «бесконечного pg_sleep(1) при пустом gen.events».
|
|
-- dblink_is_busy() проверяет состояние конкретного dblink-соединения напрямую.
|
|
IF v_jobs > 1 THEN
|
|
FOR i IN 1 .. v_jobs LOOP
|
|
WHILE dblink_is_busy('job' || i) = 1 LOOP
|
|
PERFORM pg_sleep(1);
|
|
END LOOP;
|
|
END LOOP;
|
|
END IF;
|
|
PERFORM dblink_disconnect(unnest(dblink_get_connections()));
|
|
|
|
-- Если данных нет, останавливаемся с понятной ошибкой
|
|
SELECT COUNT(*) INTO v_bookings_cnt FROM bookings.bookings;
|
|
IF v_bookings_cnt = 0 THEN
|
|
RAISE EXCEPTION 'Генератор demodb завершился, но bookings.bookings пустая. Проверьте применение патчей и логи генератора.';
|
|
END IF;
|
|
|
|
-- Генерация закончена — возвращаем synchronous_commit = on и сбрасываем
|
|
-- буферы на диск. Без этого docker compose down может убить PostgreSQL
|
|
-- до записи WAL → данные пропадут (особенно на WSL2).
|
|
ALTER DATABASE demo SET synchronous_commit = on;
|
|
SET synchronous_commit = on;
|
|
END $$;
|
|
|
|
CHECKPOINT;
|