Сохранение консистентности при интеграции систем
Немного информации на тему интеграции с сохранением консистентности данных на стороне потребителя.
Потоковая интеграция
Если вы используете брокер сообщений для интеграции, например Kafka, то сохранить консистентность на стороне потребителя можно, но требуется выполнить несколько условий:
- Использовать в топиках только 1 партицию.
- Передать во все топики финальное сообщение, сигнализирующее об окончании пакета изменений – кадра (см. пояснения ниже), даже если пакет был пустой.
» Потребителю нужно дождаться финальных сообщений во всех топиках. После этого данные можно перекладывать в предметные таблицы или завершать транзакцию, если запись была непосредственно в них. - Для чтения данных можно использовать только одну сессию.
- Перед чтением необходимо открыть транзакцию с уровнем изоляции Repeatable Read.
» Это условие актуально, если чтение данных производится из нескольких предметных таблиц. Если чтение делается из логов, размеченных кадром, то это условие можно не учитывать. - Если переносится инкремент (CDC), то он должен быть перенесен целиком.
» Это условие актуально, если чтение данных производится из предметных таблиц. - При работе с брокером нужно использовать транзакцию, чтобы гарантировать целостность кадра.
Очевидно, что дизайн решения непростой и есть ограничение по производительности. Потоковая интеграция плохо подходит для строгого сохранения консистентности, но это в принципе достижимо.
Оптимально иметь общий кадрированный лог, это позволит минимизировать операции чтения, упростить дизайн и качественно обрабатывать технический долг (после длительного простоя source коннектора).
Прямой запрос к БД
При прямом обращении к БД также можно реализовать перенос данных с сохранением консистентности, но требуется выполнить условия:
- Использовать только 1 сессию к БД-источнику.
- Перед чтением необходимо открыть транзакцию с уровнем изоляции Repeatable Read.
» Это условие актуально, если чтение данных производится из нескольких таблиц. - Если переносится инкремент (CDC), то он должен быть перенесен целиком.
Учтите вот что » Данные вы конечно увидите консистентные, но вы не увидите изменения, сделанные другими незавершенными транзакциями до старта вашей транзакции. Поэтому нужно перечитывать данные (сдвигать порог чтения назад на максимально возможную продолжительность транзакции), чтобы захватить пропущенные (в прошлый сеанс) изменения.
Увы, но консистентность и многопоточность несовместимы. Если интеграционный адаптер остановится на длительное время, то перенос накопившегося инкремента целиком может стать проблемой.
Чтение из лога
Иногда требуется организовать перенос данных из журнала/ов, в котором записаны изменения строк. Эта информация относится к источнику данных и подготовке данных для передачи с сохранением консистентности. Лог хорошо подходит для передачи инкремента в брокер или с помощью REST API.
Для инкрементного вычитывания данных из лога необходимо отслеживать значение id (идентификатор строки, порождаемый генератором). Вычитывать данные из лога нужно в порядке возрастания id, но … так как некоторые записи (с меньшими id) могут быть еще недоступны для чтения (из-за незавершенных транзакций), то одного id мало, тут требуется дополнительное действие – раскадровка журнала. Сразу оговорюсь, есть случаи, когда раскадровка может быть не нужна:
- если есть возможность удалять строки из лога при вычитывании.
- если данные пишутся в лог монопольно, т.е. всего одним процессом, и строго последовательно.
- если есть другой способ правильно отслеживать инкремент.
Разметка кадра выполняется примерно так …
Размечаем новые строки лога идентификатором кадра. Значения кадра можно брать из того же генератора, что используется для id. Если логов несколько, то разметку нужно делать единым идентификатором с уровнем изоляции транзакции Repeatable Read.
Крайне важно! Разметка должна выполняться строго в отдельной транзакции. Разметку можно выполнять по регламенту (job), например раз в 5 секунд.
Вычитывать данные из лога нужно в порядке возрастания идентификаторов (ORDER BY frame_id , id), отслеживать тоже нужно оба значения.
Кадр должен быть целиком передан потребителю и атомарно применен, это будет главное условие сохранения консистентности.
Для ускорения записи и чтения сделайте всего 1 BRIN-индекс для поля frame_id.
Так ли нужна консистентность?
- Иногда это просто необоснованное требование со стороны заказчика.
- Иногда нарушение консистентности будет иметь временный эффект (Eventual Consistency), целостность восстановится в следующей итерации.
- Несогласованность ссылочной целостности можно закрыть с помощью специальной доработки.