Синхронизация ролевой модели между окружениями: как мы избавились от «работает только на проде»
На проекте было три окружения: тест, препрод и прод. Ролевую модель вели поддержка и менеджеры только на проде — остальные окружения оставались с неполной моделью. В итоге повторить некоторые сценарии на тесте или препроде было невозможно. Мы построили систему синхронизации: добавляем и удаляем сущности, ставим флаги игнорирования, синхронизируем только из прода, запускаем по cron и по кнопке из админки, сверяем по slug. Ролевая модель теперь успешно синхронизируется между окружениями.
Проблема: «работает только на проде»
Напомним контекст. В статье о ролевой модели мы рассказывали, как внедрили гибкую систему доступов: роль + организация + кастомные доступы, сетка разрешений, интерфейс управления и история изменений.
Но у этой системы была одна особенность, которая со временем стала проблемой.
Как это работало:
-
На проекте было три окружения: тест, препрод и прод.
-
Ролевую модель вели поддержка и менеджеры только на прод окружении.
-
Тест и препрод получали неполную ролевую модель — после того как её дополняли на проде.
Что это давало на практике:
-
Повторить некоторые сценарии из продакшена на тесте или препроде было невозможно.
-
Причины — различия окружений: данные на проде отличались от тестовых.
-
Тестирование ролевой модели превращалось в лотерею: «а как оно там, на проде?»
Классическая ситуация: «У меня локально работает, а на проде — нет». Только в масштабе ролевой модели целого проекта.
Решение: синхронизация ролевой модели
Мы провели анализ и построили систему синхронизации. Вот из чего она состоит.
1. Синхронизируем и удаления, и добавления
Синхронизация должна отражать полное состояние ролевой модели: если на проде роль добавлена — она появляется на тесте и препроде. Если удалена — удаляется.
Это важно, потому что иначе окружения накапливают «мусор»: сущности, которых на проде уже нет.
2. Флаги игнорирования для ролей, доступов и групп
Для сущностей ролей, доступов и групп мы поставили параметр для игнорирования — как добавления, так и удаления.
Зачем это нужно:
-
Не удалить нужные сущности при синхронизации. Например, на тесте есть сущность, которая нужна для тестирования, но на проде её нет. Флаг игнорирования защищает её от удаления.
-
Не добавить лишних сущностей. Если сущность помечена как игнорируемая, она не будет синхронизирована.
Это гибкий механизм: можно точечно управлять тем, что синхронизируется, а что остаётся локальной особенностью окружения.
3. Тест и препрод синхронизируются только из прода. Прод синхронизироваться не может из другой среды.
Направление синхронизации — одностороннее: из прода в тест и препрод.
Почему так:
-
Прод — источник правды для ролевой модели.
-
Тест и препрод — получатели.
-
Обратная синхронизация (из теста в прод) создала бы риск: тестовые изменения могли бы попасть в продакшен.
4. Синхронизация: cron + кнопка в админке
Мы реализовали два способа запуска синхронизации:
-
Фоново по расписанию: планировщик cron + schedule Laravel. Запуск каждый час в рабочее время — с 09:00 по 18:00.
-
По кнопке из админ-панели: кнопка присутствует только на тест и препрод окружениях, регулируется конфигурацией.
Почему оба способа:
-
Cron — для регулярной синхронизации без участия человека.
-
Кнопка — для случаев, когда нужно синхронизировать «здесь и сейчас»: например, после срочного изменения на проде.
5. Сверка данных по slug
Сверка данных идёт по slug, а не по UUID.
Почему:
-
UUID уникален, но по нему нет возможности искать записи — он не предназначен для поиска.
-
Slug — человекочитаемый идентификатор, по которому можно сопоставить сущности между окружениями.
6. Защита API: белый список IP и доступы API-пользователя
API-метод прода имел ограничение по белому списку IP-адресов. Кроме того, проверялись доступы API-пользователя: не все API-пользователи имели доступ к данному методу.
Зачем:
-
Белый список IP — защита от несанкционированных вызовов извне.
-
Доступы API-пользователя — дополнительный уровень контроля: только уполномоченные пользователи могут запрашивать данные ролевой модели.
7. Отдельные пользователи для теста и прода с bearer-токенами
Для теста и прода мы завели отдельных пользователей, которые работали по bearer-токену со сроком жизни сутки.
Почему:
-
Отдельные пользователи — изоляция: если токен теста скомпрометирован, это не влияет на прод.
-
Bearer-токен со сроком жизни сутки — баланс между безопасностью и удобством: токен не нужно передавать при каждом запросе, но он автоматически истекает.
Как это работает: полный цикл
Опишем, как происходит синхронизация.
-
На проде поддержка или менеджер добавляет новую роль, доступ или группу.
-
Cron (или кнопка в админке) запускает синхронизацию на тесте и препроде.
-
Запрос уходит на API прода — с bearer-токеном, с IP из белого списка.
-
API прода проверяет доступы API-пользователя и возвращает данные ролевой модели.
-
Тест и препрод сверяют данные по slug — определяют, что добавить, что удалить.
-
Учитываются флаги игнорирования — сущности с флагом не синхронизируются.
-
Изменения применяются — ролевая модель на тесте и препроде обновляется.
Результат: синхронизация работает
Мы реализовали всё из списка — и синхронизация сущностей ролевой модели успешно работает между окружениями.
Стек
-
Backend: Laravel (PHP)
-
Планировщик: cron + Laravel Schedule
-
API: bearer-токены, белый список IP
-
Сверка данных: по slug
Что мы вынесли из этой истории
1. Окружения должны быть сопоставимы
Если тест и препрод не отражают прод — тестирование теряет смысл. Синхронизация ролевой модели сделала окружения сопоставимыми.
2. Односторонняя синхронизация — безопаснее
Синхронизация только из прода в тест и препрод исключает риск, что тестовые изменения попадут в продакшен.
3. Флаги игнорирования — гибкость без хаоса
Не всё нужно синхронизировать. Флаги игнорирования позволяют сохранить локальные особенности окружений, не ломая общую картину.
4. Безопасность API — обязательна
Белый список IP и bearer-токены с ограниченным сроком жизни — минимальные меры для API, который отдаёт данные ролевой модели.
Заключение
Эта история — про то, что ролевая модель — это не только интерфейс и сетка доступов. Это ещё и инфраструктура: синхронизация между окружениями, защита API, механизмы сверки.
Мы построили систему, которая избавила команду от «работает только на проде». Теперь тест и препрод отражают реальную ролевую модель — и тестирование стало предсказуемым.