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
Ларчик просто открывался :)