Если удаленная запись возвращается после очередной синхронизации, один из источников продолжает считать ее актуальной или pipeline переносит только вставки и обновления. Удаление должно быть отдельным событием с понятным приоритетом.
Выберите один конкретный ID и проследите его версии, updated_at и deleted_at во всех системах. Не запускайте полную перезапись, пока не определен источник истины.
Коротко: что сделать
- Определить system of record
- Сравнить запись по одному ID во всех системах
- Проверить soft delete и tombstone
- Проверить watermark инкрементальной загрузки
- Проверить направление и правила конфликтов
Почему возникает проблема
Обычный upsert не умеет понять отсутствие строки как удаление, особенно если выгрузка передает только измененные активные записи.
- Источник ставит deleted_at, а приемник его игнорирует
- Полная выгрузка не удаляет отсутствующие строки
- Старая система имеет более новый updated_at из-за часов
- Двунаправленная синхронизация возвращает запись обратно
- CDC не передает delete event
- Tombstone удаляется раньше, чем все потребители его прочитали
Пошаговая диагностика
Проверку лучше проводить на одном воспроизводимом примере и фиксировать результат каждого шага. Так можно быстро отделить первопричину от побочных ошибок и не менять несколько компонентов одновременно.
- Построить timeline выбранного ID
- Проверить логи delete и последующего upsert
- Сверить time zone и точность timestamp
- Проверить очередность batch и CDC
- Проверить повторный запуск одного окна
- Проверить правила очистки tombstone
Как исправить
Удаление нужно представить явным состоянием и распространить по тем же надежным правилам, что и обновление.
- Передавать deleted_at или delete event
- Хранить tombstone до подтверждения всех потребителей
- Определить приоритет источника и версий
- Сделать обработку удаления идемпотентной
- Исправить watermark с перекрывающимся окном
- Добавить reconciliation для поиска воскресших записей
Как проверить результат
- Удалить тестовую запись и пройти полный цикл
- Повторить тот же batch
- Запустить синхронизацию с задержанным потребителем
- Проверить двунаправленный обмен
- Убедиться, что связанные данные обрабатываются по правилам
Как не допустить повторения
Модель синхронизации должна явно описывать создание, изменение, удаление и разрешение конфликтов.
- Документировать source of truth
- Мониторить количество delete и resurrect
- Хранить audit timeline
- Тестировать повторный и запоздалый batch
Чего не стоит делать
- Не удалять физически данные до распространения события
- Не считать отсутствие строки однозначным удалением без правил
- Не сравнивать только updated_at при несинхронных часах
- Не запускать полную очистку production без сверки
Что подготовить для диагностики
- Проблемный record ID
- Записи из всех систем
- Логи ETL/CDC
- Правила soft delete
- Watermark и расписание pipeline
Частые вопросы
Что такое tombstone?
Это маркер удаления, который сообщает потребителям, что запись нужно удалить или скрыть.
Почему upsert возвращает запись?
Upsert видит строку в старом источнике и не знает, что в другой системе она была удалена.
Можно ли просто удалять отсутствующие записи?
Только при полной и авторитетной выгрузке. Для частичных выборок это опасно.
Когда стоит обратиться за помощью
Нужна помощь, если синхронизация двунаправленная, затрагивает несколько потребителей или уже появились массовые расхождения и нельзя выбрать источник истины автоматически.
Итог
Удаление должно быть явным, версионированным и идемпотентным событием. Спроектировать ETL и безопасно убрать воскресшие записи можно через @rabotator_support.