Greenplum - пропадают данные?

Напрочь забросил этот блог, но периодически вспоминал, решил попробовать вернуться. С работой всё сложилось практически идеально, позже напишу.

На прошлой неделе столкнулся с интересной проблемой - есть таблица-источник в Greenplum, куда непрерывно льются данные. Есть даг в Airflow, который постоянно забирает добавившиеся данные из этой таблицы и сохраняет в другую таблицу, откуда они уже используются в разного рода отчетах. И вдруг выяснилось, что в целевой таблице меньше данных, чем на источнике.

Даг максимально простой: 1) находит максимальное значение id в данный момент (id - тип bigserial, автоматически генерируемая последовательность, постоянно растущая), 2) считывает из вспомогательной таблицы предыдущее значение max_id (до которого строки уже скопированы), 3) выполняет простейший sql-скрипт на вставку:

insert into dst_table 
    select * from src_table
        where id > old_max_id and id <= max_id;

Просмотрел логи - значения old_max_id из вспомогательной таблицы читается нормально, max_id сохраняется, вставка отрабатывает, но часть данных по прежнему не приходит. Причём именно часть - если запрос возвращает 100 тыс. записей, 90 тыс. в целевой таблице есть, а 10 тыс. хотя есть на источнике, в целевую таблицу не пришли.

Запустил “костыль” - даг работающий параллельно первому, ищущий пропуски и дозаписывающий их в целевую таблицу, но причину проблемы сходу не нашёл. Атомарность транзакций вроде не допускает того, чтобы запрос выполнялся частично. Было подозрение, что может commit в распределённой БД отрабатывает как то криво, но это разработчики наверняка тестируют по максимуму. Может bigserial как то не так работает? - но тоже базовая вещь, там все косяки должны уже давным-давно быть устранены…

Крутил в голове с неделю, и наконец сообразил - данные в источник льются одновременно несколькими транзакциями, и здесь то и кроется причина.

Создаю таблицу с последовательностью - create table test.big_serial (number int, id bigserial); - и начинаю добавлять строки с двух разных psql внутри транзакций. Для наглядности значения number совпадает с id.

Запускаю первый psql:

begin;
copy test.big_serial (number) from stdin; -- вставляю 1, 2 и заканчиваю copy

Запускаю второй psql:

begin;
copy test.big_serial (number) from stdin; -- вставляю 3, 4 и заканчиваю copy
end; -- делаю commit

Смотрю что в БД:

select max (id) from test.big_serial; -- 4
select count(*) from test.big_serial where id <= 4; -- 2

max_id равен 4, но строк всего две

Закрываю транзакцию в первом psql:

end;

Смотрю что в БД:

select max (id) from test.big_serial; -- 4
select count(*) from test.big_serial where id <= 4; -- 4

max_id всё также равен 4, но строк уже 4

Ларчик просто открывался :)

Written on June 27, 2020