Если удаленная запись возвращается после очередной синхронизации, один из источников продолжает считать ее актуальной или 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.