Если удаленный профиль появляется снова после очередной синхронизации, система воспринимает отсутствие записи не как окончательное удаление, а как недостающие данные, которые нужно восстановить из CRM, identity provider, мобильного клиента, очереди или реплики.
Повторное ручное удаление дает только временный результат. Нужно определить источник истины, построить полный timeline событий и передавать по всем контурам явный факт удаления с версией, который имеет приоритет над устаревшими обновлениями.
Как проявляется восстановление профиля
Сначала зафиксируйте, что именно вернулось: учетная запись для входа, карточка клиента, публичный профиль, настройки, файлы или только поисковый индекс.
- Профиль исчезает сразу после удаления, но появляется после планового обмена.
- Основная запись не возвращается, однако имя и контакты снова видны в поиске или CRM.
- После входа со старого телефона сервер заново создает аккаунт с прежними данными.
- Удаленный пользователь получает письмо, push или может восстановить сессию.
- После восстановления базы из резервной копии профиль снова становится активным.
- Возвращается новая запись с другим ID, но тем же внешним идентификатором.
Не проверяйте проблему только по интерфейсу. Сравните внутренний user ID, external IDs, статус удаления, время изменения и источник последней записи во всех связанных системах.
Почему синхронизация возвращает удаленные данные
Не определен главный источник данных
Сайт считает основной свою базу, CRM — собственную карточку, а identity provider — активную учетную запись. Если каждый коннектор создает отсутствующий объект, физическое удаление в одной системе запускает восстановление из другой.
Удаление не передается как отдельное событие
Обычный sync получает только актуальные записи. Отсутствие пользователя в выгрузке неоднозначно: он мог быть удален, скрыт фильтром или не попасть в текущую страницу API. Поэтому коннектор не понимает, что локальную копию тоже нужно удалить или обезличить.
Старое обновление приходит позже удаления
Очередь может доставить события не по порядку. Например, ProfileDeleted обработан первым, а задержанное ProfileUpdated — позже. Если обработчик сравнивает только время получения, устаревшее обновление создаст профиль заново.
- У события нет версии или sequence number конкретного профиля.
- Часы сервисов расходятся, а updated_at используется как единственный приоритет.
- Retry старой задачи выполняется после успешного удаления.
- Два коннектора обновляют один объект независимо друг от друга.
Offline-клиент отправляет старую локальную копию
Телефон или desktop-приложение могло долго находиться без сети. После подключения клиент видит, что серверной записи нет, и выполняет create вместо проверки tombstone и версии удаления.
Удалена только основная таблица
Контакты могут оставаться в CRM, поисковом индексе, аналитическом хранилище, кеше, таблице подписок, файлах и журнале рассылки. Обратный импорт из любой такой копии способен создать новую карточку.
Восстановление из бэкапа не учитывает последующие удаления
Резервная копия по определению содержит состояние на прошлую дату. Если после restore приложение сразу открывает доступ и запускает двустороннюю синхронизацию, удаленные после даты бэкапа профили возвращаются в рабочий контур.
Пошаговая диагностика
- Выберите один профиль и соберите его внутренний ID, внешние ID, tenant ID и безопасные fingerprints контактных данных.
- Зафиксируйте точное время удаления, request ID, инициатора и результат операции.
- Постройте timeline всех ProfileUpdated, ProfileDeleted, import и sync jobs до момента восстановления.
- Найдите INSERT или UPSERT, который создал новую запись, и сервисную учетную запись, выполнившую запрос.
- Проверьте CRM, IdP, мобильные клиенты, очереди, search index, рассылки и другие источники по тому же external ID.
- Сравните version, sequence, deleted_at и source у последнего обновления и удаления.
- Проверьте правила conflict resolution: кто побеждает при отсутствии записи и при разных версиях.
- Повторите сценарий в тестовом окружении с задержанным событием, offline-клиентом и восстановлением из бэкапа.
В диагностические логи не нужно копировать имя, телефон, email, документы и токены. Используйте внутренние ID, хешированный идентификатор, версию события, источник и причину отказа.
Tombstone: явный признак удаления
В распределенной системе физическое отсутствие строки не несет достаточной информации. Tombstone фиксирует, что объект существовал, был удален в определенной версии и не должен создаваться из более старых данных.
- Храните минимальный стабильный subject key или внешний ID, а не полный удаленный профиль.
- Записывайте deleted_at, версию события и источник удаления.
- Определите срок хранения tombstone в соответствии с архитектурой и принятой политикой данных.
- Запрещайте create и update, если их версия ниже или равна версии удаления.
- Обрабатывайте повторное удаление идемпотентно.
Tombstone не должен превращаться в скрытую копию персональных данных. Состав и срок хранения нужно минимизировать и документировать с учетом реальной схемы обмена и требований организации.
Как исправить синхронизацию
- Временно остановите создание профилей из подозрительного источника, не отключая журналирование событий.
- Назначьте для каждого поля и жизненного цикла профиля один авторитетный источник.
- Сохраняйте удаление и событие для интеграций атомарно через transactional outbox или эквивалентный механизм.
- Передавайте ProfileDeleted с subject ID и монотонной версией во все зависимые системы.
- На стороне consumer проверяйте tombstone до INSERT, UPSERT и merge.
- Отклоняйте устаревшие update-события и действия offline-клиента, показывая необходимость обновить локальное состояние.
- Отзывайте сессии, refresh tokens и права доступа одновременно с переходом профиля в удаленное состояние.
- Очищайте либо обезличивайте производные копии по явной карте данных, не используя обратный импорт как источник восстановления.
- После restore из бэкапа применяйте журнал удалений новее точки копии до открытия системы и запуска исходящих интеграций.
Как безопасно исправить уже восстановленные профили
Перед массовой обработкой сделайте резервную копию и сформируйте отчет: какие профили восстановлены, из какого источника, какие зависимые записи появились и какие бизнес-операции успели выполниться.
- Сначала остановите источник повторного создания, затем корректируйте данные.
- Отделите ошибочно восстановленные профили от пользователей, которые зарегистрировались заново осознанно.
- Отзовите активные сессии и отмените запланированные уведомления до удаления карточки.
- Используйте контролируемую задачу с dry-run, ограничением выборки и журналом каждой операции.
- Повторно создайте tombstone и дождитесь подтверждения от всех интеграций.
- Не редактируйте резервные копии вручную; защищайте рабочее восстановление повторным применением журнала удалений.
Как проверить результат
- Несколько полных циклов синхронизации не создают профиль заново.
- Задержанное ProfileUpdated с прежней версией отклоняется.
- Offline-клиент после подключения получает удаленное состояние и не выполняет create.
- В CRM, IdP, search index, рассылках и прикладной базе нет активной копии профиля.
- Старые access и refresh tokens больше не открывают пользовательский доступ.
- Повтор запроса удаления безопасен и не создает новые побочные операции.
- Тестовый restore применяет последующий журнал удалений до открытия системы.
- Новая осознанная регистрация обрабатывается по отдельному согласованному сценарию, а не как случайный sync.
Типичные ошибки
- Удалить строку только в основной базе и не уведомить интеграции.
- Считать отсутствие записи командой на восстановление из любого источника.
- Сравнивать события только по updated_at при несинхронных часах.
- Удалить tombstone раньше, чем истекли максимальные задержки очередей и offline-клиентов.
- Хранить в tombstone весь прежний профиль вместо минимального технического идентификатора.
- Очистить интерфейсный кеш, не найдя реальный INSERT или UPSERT.
- Запустить массовое удаление без dry-run, резервной копии и аудита.
- Восстановить бэкап прямо в production и сразу включить двустороннюю синхронизацию.
Как предотвратить повторение
- Поддерживайте карту всех систем, где создаются и копируются данные профиля.
- Опишите state machine профиля и приоритет удаления над устаревшим обновлением.
- Используйте versioned events, idempotency и transactional outbox.
- Добавьте contract-тесты удаления для каждого коннектора и offline-клиента.
- Алертируйте создание профиля с subject ID, для которого существует tombstone.
- Регулярно сверяйте основную базу, CRM, IdP и производные хранилища.
- Тестируйте процедуру восстановления из бэкапа вместе с повторным применением удаления.
- Ограничивайте персональные данные в логах и технических реестрах минимально необходимым набором.
Когда нужна помощь
Если удаленный профиль возвращается после синхронизации, я построю timeline по ID и версиям, найду источник повторного создания, исправлю порядок событий, tombstone и правила conflict resolution. Затем проверю CRM, IdP, offline-клиенты и восстановление из бэкапа, чтобы данные не появлялись снова из устаревшей копии.