Как гарантировать бесшовность при обновлении схемы базы данных и сервиса?
Что проверяют
Понимаете ли вы, что при rolling-деплое старая и новая версии сервиса какое-то время работают с одной базой. Отсюда вся задача: схема должна подходить обеим версиям сразу. Следом обычно просят разобрать конкретный случай, например переименование колонки без простоя.
Как отвечать
Каркас ответа это паттерн expand/contract: ломающее изменение раскладывается на несколько совместимых релизов.
- expand: добавляете новое, ничего не удаляя. Колонка nullable или с дефолтом, новая таблица, новый индекс. Старый код их не замечает;
- двойная запись: новая версия сервиса пишет и в старое, и в новое место, а читает пока из старого;
- перенос данных: бэкфилл пачками фоновой задачей, а не одним
UPDATEна миллионы строк, который держит блокировки; - переключение чтения: следующий релиз читает из нового места, часто за флагом через
Microsoft.FeatureManagement, чтобы откатиться без деплоя; - contract: когда старых версий в работе не осталось, отдельным релизом удаляете старую колонку.
С EF Core важно, как применяются миграции. Database.Migrate() при старте опасен: несколько подов стартуют одновременно и гоняются за одну миграцию. Надёжнее отдельный шаг пайплайна до выката кода: скрипт dotnet ef migrations script --idempotent или бандл dotnet ef migrations bundle. Операции, которые нельзя выполнять в транзакции, вроде CREATE INDEX CONCURRENTLY в PostgreSQL, пишут через migrationBuilder.Sql(..., suppressTransaction: true).
API меняют так же: новый контракт рядом со старым, версионирование через Asp.Versioning, удаление старого после ухода клиентов.
Где спотыкаются
Переименовывают колонку одной миграцией. EF Core сгенерирует RenameColumn, а иногда удаление с добавлением, что ещё хуже, и старые поды упадут на первом же запросе, пока идёт выкатка.
Надеются на Down-миграцию как на план отката. После того как новая версия записала данные, откат схемы их теряет. Практичнее откатывать код, а схему держать совместимой и чинить вперёд.
Забывают про блокировки. Индекс или колонка с пересчётом на большой таблице могут заблокировать запись на минуты, и это тот же простой, только без ошибок в логах деплоя.