Сверка вместо «удалить всё и вставить заново»: идемпотентный мост 1С ↔ legacy для платежей
- 1С
- интеграция
- HMAC
- идемпотентность
- PHP
- MariaDB
- архитектура
- case-study
Прямой обмен между 1С и легаси веб-системой — частый источник тихих потерь данных. Классическая схема «выгрузка за дату» работает так: для выбранной даты в учётной таблице сайта удаляются все строки, затем заливаются заново из 1С. Пока система одна и оператор один — терпимо. Как только в одной таблице оказываются платежи нескольких юрлиц и несколько касс — начинаются коллизии, задвоения и ручные обходы.
Мы заменили этот обмен на транзакционную сверку (reconcile) по HTTP+HMAC. Ниже — почему старая схема опасна, как устроена новая и как мы доказали безопасность на боевой записи.
Почему «удалить всё и вставить заново» — мина
Старый обмен шёл через ODBC напрямую в БД сайта и удалял строки по грубому ключу — по сути «первые 16 символов типа документа + номер + год». Отсюда три беды:
- Грубый ключ. Сверка по году, а не по дню; без точного номера счёта и типа кассы → коллизии разных платежей, которые потом правили руками.
- Накопление. Схема не удаляла «лишнее» аккуратно — повторные прогоны, другие кассы и другие юрлица накапливались в общей таблице вперемешку.
- Общая партиция на несколько источников. Несколько баз 1С писали в одну таблицу под пересекающимися именами. Любое «удалить всё за дату» рисковало снести данные соседа.
«Ошибки, которые терпели» — это не фигура речи, а реальные строки, которые бухгалтерия исправляла вручную.
Идея: не «переписать», а «свести»
Новый обмен — это не прямой доступ к БД, а HTTP-эндпоинты на самом легаси-сервере (переиспользуем боевой код подключения к БД, подписи и пересчёта итогов). 1С отправляет набор платежей за дату, сервер сводит его с тем, что уже есть в партиции (база, дата), по точному бизнес-ключу (номер счёта + номер документа + тип кассы + день) и применяет минимальные изменения:
- совпало по ключу и значениям → noop (ничего);
- изменилось → replace (старую версию помечаем удалённой, пишем новую);
- нет в наборе 1С → soft-delete;
- новый документ → insert.
Наши вставки помечаются тегом провенанса (видно, что строка пришла через мост), удаление — append-only: ничего не затирается физически, остаётся аудит-след. Подпись — HMAC-SHA256 от тела запроса плюс отметка времени против повторного воспроизведения.
Результат — идемпотентность: повторный прогон без изменений в 1С не меняет в БД ничего. Это убирает задвоения как класс.
Три предохранителя сверки
Удаление — самая опасная операция, поэтому вокруг неё три защиты.
1. dry-run. Сервер умеет вернуть план (вставлю N, заменю M, удалю K) без записи. Любой первый прогон на новой дате смотрим в dry-run.
2. Гард пустого набора. Если источник прислал ноль платежей, мы НЕ удаляем партицию целиком — это почти всегда сбой выгрузки, а не «реально 0 оплат». Массовое удаление при пустом вводе блокируется (снять можно только явным флагом). Этот гард уже сработал в бою: пустой прогон вместо «удалить 101 строку» удалил ноль.
3. Скоуп по серии документа — ключевое для общей таблицы. У каждой базы/организации свой префикс номера документа (своя «серия»). Сверка удаляет «отсутствующие» строки только в тех сериях, которые реально прислал источник. Прогон одной организации физически не может тронуть строки другой в той же партиции.
Как доказали, что не теряем данные
Тестировали не «на словах», а прогоном на копии боевой базы с проверкой каждой строки до и после.
- Идемпотентность: прогон без изменений →
noopпо всем строкам, ноль записи. - Изменение: правим два документа →
replace(старые версии → в архив, новые → с тегом моста). - Создание: добавляем документ →
insert. - Защита соседей: в каждом прогоне строки чужих серий помечены
protected— ни одна не ушла в удаление. - Краевой случай: документ, ошибочно созданный «не той датой», в выгрузку за нужную дату не попал (дата-скоуп корректен); после исправления даты — попал и добавился. Никаких фантомных строк.
Итог: партиция до и после боевой записи совпадает по всем «чужим» строкам, наши изменения помечены тегом, аудит-след полон, потерь — ноль.
Выводы
- Сверка по точному ключу вместо delete-all+insert убирает коллизии и задвоения и делает обмен идемпотентным.
- Append-only + тег провенанса дают честный аудит: видно, что и когда пришло из 1С.
- Скоуп удаления по серии безопасно решает мультиарендность, когда несколько источников делят одну таблицу — без миграции схемы.
- dry-run и гард пустого набора — дешёвые предохранители, которые ловят дорогие ошибки до записи.
Если у вас обмен 1С с веб-системой всё ещё работает по принципу «снести и залить заново» — это хороший кандидат на замену сверкой. Как мы делали это для оператора речных круизов →