From 85d2776b44e0c2f98a47f5e7231c92d5ff092e5d Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Mon, 9 Mar 2026 16:30:13 +0300 Subject: [PATCH] =?UTF-8?q?fix(bookings):=20=D1=83=D1=81=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=BE=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20=D0=BF=D1=80=D0=B8=20jobs>1=20=D0=B8=20=D1=83?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=BD=20VACUUM=20=D0=B8=D0=B7=20=D0=B8=D0=BD?= =?UTF-8?q?=D0=BA=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - 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 --- bookings/generate_next_day.sql | 26 +++++++++++++------- sql/src/bookings_generate_day_if_missing.sql | 20 +++++++++------ 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/bookings/generate_next_day.sql b/bookings/generate_next_day.sql index db8ea42..a3ee380 100644 --- a/bookings/generate_next_day.sql +++ b/bookings/generate_next_day.sql @@ -35,6 +35,12 @@ BEGIN 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; @@ -42,17 +48,19 @@ BEGIN -- Без COMMIT воркеры не могут INSERT INTO gen.stat_jobs → deadlock. COMMIT; - -- При jobs>1 воркерам нужно время, чтобы подключиться через dblink и - -- выставить application_name='Airlines processor'. Без паузы busy() - -- сразу вернёт false (воркеры ещё не видны в pg_stat_activity). + -- Ждём завершения каждого воркера через 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 - PERFORM pg_sleep(3); + 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; - - -- Ждём завершения фоновых джобов генератора, чтобы данные успели записаться - WHILE busy() LOOP - PERFORM pg_sleep(1); - END LOOP; PERFORM dblink_disconnect(unnest(dblink_get_connections())); -- Если данных нет, останавливаемся с понятной ошибкой diff --git a/sql/src/bookings_generate_day_if_missing.sql b/sql/src/bookings_generate_day_if_missing.sql index 58e03b3..4e1da9a 100644 --- a/sql/src/bookings_generate_day_if_missing.sql +++ b/sql/src/bookings_generate_day_if_missing.sql @@ -38,6 +38,12 @@ BEGIN 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; @@ -49,14 +55,14 @@ BEGIN date_trunc('day', v_start_date), date_trunc('day', v_end_date); - -- При jobs>1 воркерам нужно время, чтобы подключиться через dblink + -- Ждём завершения каждого воркера через dblink_is_busy() (см. комментарий + -- в bookings/generate_next_day.sql — busy() через pg_stat_activity ненадёжен). IF v_jobs > 1 THEN - PERFORM pg_sleep(3); + 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; - - -- Ждём завершения фоновых джобов генератора, чтобы данные успели записаться - WHILE busy() LOOP - PERFORM pg_sleep(1); - END LOOP; PERFORM dblink_disconnect(unnest(dblink_get_connections())); END $$;