# Подход expand/contract при обновлении системы

Подход **expand/contract** используется для безопасного обновления системы, когда изменение затрагивает структуру данных, существующие записи или бизнес-логику, работающую с этими данными.

Основная идея подхода — не выполнять несовместимое изменение за один шаг. Сначала система расширяется новой структурой данных и кодом, который сохраняет совместимость со старым состоянием. Затем выполняется подготовка или перенос данных. После проверки результата система переключается на новую структуру, а устаревшие элементы удаляются.

## Когда применяется expand/contract

Подход expand/contract применяется, когда изменение нельзя безопасно выполнить одним обновлением без риска нарушить работу системы.

Обычно подход используется, если:

- меняется структура хранения данных;
- добавляются новые атрибуты, таблицы, связи или служебные признаки;
- выполняется перенос данных из старой структуры в новую;
- меняется бизнес-логика, зависящая от подготовленных данных;
- требуется обработать большой объём существующих записей;
- подготовка данных может выполняться долго или создавать нагрузку на базу данных;
- нельзя гарантировать, что все данные будут подготовлены в рамках одного обновления.

Подход становится обязательным, если новая версия кода не может безопасно работать со старым состоянием данных или если перенос данных нельзя гарантированно выполнить в рамках одного быстрого обновления.

Признаки, что изменение нужно выполнять через expand/contract:

- новая бизнес-логика требует данные, которых ещё нет в существующей базе;
- нужно перенести или пересчитать большой объём существующих записей;
- старый атрибут, таблицу или связь нельзя удалить сразу, потому что на них ещё опирается код;
- подготовка данных может выполняться дольше обычного обновления;
- часть данных после установки релиза может остаться в старом состоянии.

## Общая схема

Обновление по подходу expand/contract состоит из двух фаз и подготовки данных между ними.

1. **Фаза 1 — expand**  
   В систему добавляется новая структура данных. Старая структура сохраняется. Бизнес-логика адаптируется к будущей структуре, но продолжает поддерживать старое состояние данных.

2. **Подготовка и проверка данных между фазами**  
   После первой фазы выполняется подготовка или перенос данных, а затем проверяется готовность данных к переключению на новую структуру. Для подготовки могут использоваться скрипты миграции, обычные DataInstall-скрипты, отложенные DataInstall-скрипты, пакетная обработка или другой регламентированный механизм.

3. **Фаза 2 — contract**  
   После завершения и проверки подготовки данных выполняется новое обновление. Система переключается на новую структуру. Устаревшая структура и временная совместимость удаляются.

## Основные правила проектирования

При проектировании изменения по подходу expand/contract необходимо заранее определить:

- какие изменения структуры относятся к фазе expand;
- какие данные нужно подготовить между фазами;
- каким механизмом будет выполняться подготовка данных;
- от каких данных зависит бизнес-логика;
- как система будет работать в переходном состоянии;
- каким способом будет проверена готовность к фазе contract;
- какие элементы будут удалены на фазе contract.

Изменения, затрагивающие существующие данные, проектируются так, чтобы установка релиза не зависела от длительной обработки данных. Если подготовка данных выполняется быстро и её результат нужен сразу после установки релиза, такая подготовка может выполняться в составе обычного обновления.

Если подготовка данных занимает длительное время, создаёт существенную нагрузку на базу данных, блокирует рабочие таблицы или затрагивает большой объём данных, она выносится из основного обновления и выполняется отдельно.

Бизнес-логика должна зависеть только от данных, которые гарантированно подготовлены к моменту её запуска. Зависимость бизнес-логики допускается только от результата быстрой подготовки данных, которая выполняется в составе обычного обновления и гарантированно завершается до запуска новой логики. Не допускается обязательная зависимость бизнес-логики от результата отложенного или длительного скрипта, так как такой результат может появиться позже основного обновления и не гарантирован на момент запуска системы.

Если зависимость бизнес-логики от результата длительного или отложенного скрипта неизбежна, в прикладном коде необходимо учитывать оба состояния данных:

- состояние до выполнения скрипта;
- состояние после его успешного выполнения.

В переходный период бизнес-логика должна корректно работать с данными, которые ещё не были подготовлены или перенесены. Например, если часть записей уже заполнена по новой структуре, а часть остаётся в старом состоянии, код должен обработать оба варианта.

Вариативность бизнес-логики может быть реализована разными способами:

- чтение из новой структуры при наличии подготовленных данных;
- чтение из старой структуры, если новые данные ещё не заполнены;
- явная проверка признака или состояния подготовки данных;
- временная поддержка старого и нового алгоритма обработки;
- ограничение новой функциональности до завершения подготовки данных.

Если результат длительного или отложенного скрипта критичен для работы определённой функциональности, это нужно указать в описании релиза. В таком случае нужно описать, какие функции будут недоступны, ограничены или будут работать с неполными данными до завершения подготовки данных.

## Фаза 1 — expand

На первой фазе в систему добавляются элементы, необходимые для перехода на новую модель данных:

- новые атрибуты;
- новые таблицы;
- новые связи;
- новые состояния;
- новые служебные признаки;
- код, который поддерживает переходное состояние данных.

Старая структура на этой фазе сохраняется. Удаление старых элементов выполняется на фазе contract после завершения и проверки подготовки данных.

Бизнес-логика на фазе expand должна работать со старым состоянием данных и быть готовой к появлению нового состояния. Код не должен требовать, чтобы все данные уже были перенесены.

До завершения подготовки данных система должна поддерживать одно из двух поведений:

- продолжать работать по старому сценарию;
- корректно работать с обоими состояниями данных.

Поддерживаемые состояния:

- состояние до подготовки или переноса данных;
- состояние после успешной подготовки или переноса данных.

## Подготовка данных между фазами

Между первой и второй фазой выполняется подготовка или перенос данных.

Для подготовки данных может использоваться:

- обычный скрипт миграции;
- обычный DataInstall-скрипт;
- отложенный DataInstall-скрипт;
- пакетная обработка данных;
- отдельный регламентированный механизм переноса данных.

При выборе способа подготовки данных учитываются:

- длительность выполнения;
- объём обрабатываемых данных;
- нагрузка на базу данных;
- влияние на пользовательские операции;
- возможность повторного запуска;
- способ контроля результата;
- наличие бизнес-логики, которая зависит от результата подготовки данных.

Если подготовка данных выполняется в составе обычного обновления, она должна быть быстрой и завершаться до запуска новой логики.

Если подготовка данных выполняется отдельно от основного обновления, новая логика должна учитывать переходное состояние данных до завершения подготовки.

## Проверка готовности перед фазой contract

Перед выполнением фазы contract нужно проверить, что подготовка данных завершена корректно. Проверка нужна для того, чтобы второе обновление не было установлено на базу, где данные ещё не подготовлены для новой структуры.

Проверка должна подтвердить, что данные находятся в состоянии, с которым может работать новая бизнес-логика. Если проверка не пройдена, фаза contract не выполняется: сначала устраняются ошибки подготовки данных, затем проверка выполняется повторно.

Способы проверки готовности данных описаны в разделе [Способы проверки готовности данных](#способы-проверки-готовности-данных).

## Фаза 2 — contract

Фаза contract выполняется после того, как данные переведены в новое состояние, новая бизнес-логика готова работать с новой структурой, а результат подготовки данных проверен.

На этой фазе выполняется полное переключение на новую структуру. После переключения удаляются:

- устаревшие атрибуты;
- временные признаки;
- старые таблицы или связи;
- совместимость со старым способом чтения данных;
- временный код, который поддерживал переходное состояние.

Старая структура не удаляется в том же обновлении, где впервые добавляется новая структура и запускается подготовка данных. Фаза contract выполняется только после того, как подготовка данных завершена и готовность данных подтверждена выбранным способом проверки.

## Способы проверки готовности данных

Способ проверки готовности данных определяется при проектировании изменения. Проверка должна быть описана так, чтобы перед фазой contract было понятно:

- какие данные проверяются;
- где проверяется результат подготовки;
- какое состояние считается успешным;
- что делать, если подготовка не завершена или завершилась с ошибкой;
- можно ли устанавливать фазу contract после проверки.

Проверка готовности может выполняться автоматически, в прикладном коде, вручную по инструкции в описании релиза или комбинированно.

### Автоматическая проверка при установке обновления

Автоматическая проверка используется, если фаза contract удаляет старую структуру или новая версия больше не поддерживает старое состояние данных.

В этом варианте разработчик добавляет проверку, которая выполняется перед установкой фазы contract. Проверка должна остановить обновление, если база ещё не готова к переключению на новую структуру.

Порядок действий:

1. Определите условие готовности данных. Например: все документы нужного типа должны иметь заполненный новый ссылочный атрибут.
2. Добавьте проверочный скрипт или проверку в механизм установки обновления.
3. В проверке найдите данные, которые остались в старом состоянии.
4. Если такие данные есть, сгенерируйте ошибку и остановите установку обновления.
5. В тексте ошибки укажите, какая подготовка не завершена и что нужно сделать перед повторной установкой.

Пример условия проверки:

```sql
select count(*)
from Doc_Order d
where d.idProcessRule is null
  and d.sOldProcessCode is not null;
```

Если запрос возвращает значение больше `0`, данные не готовы к фазе contract.

```{admonition} Пример сообщения об ошибке
Невозможно установить обновление: подготовка данных для перехода на новую структуру не завершена.  
Не для всех документов заполнено правило обработки.  
Завершите скрипт подготовки данных и повторите установку обновления.
```

Успешный результат проверки — запрос не находит данные в старом состоянии, которые должны быть перенесены перед фазой contract.

### Проверка в прикладном коде

Проверка в прикладном коде используется, если новая функциональность может быть запущена до завершения подготовки данных, но должна корректно обработать переходное состояние.

В этом варианте код сам определяет, какие данные доступны: новые, старые или оба варианта. Если данные ещё не подготовлены, код либо использует старую логику, либо завершает операцию с понятной ошибкой.

Порядок действий:

1. Определите, какая функция зависит от подготовленных данных.
2. Перед выполнением функции проверьте, заполнены ли новые данные.
3. Если новые данные есть, выполните новую логику.
4. Если новых данных нет, выполните старую логику или остановите операцию с понятной ошибкой.
5. Удалите временную проверку на фазе contract, если после переключения старая структура больше не поддерживается.

Пример логики:

```text
Если у документа заполнено новое правило обработки:
    использовать новое правило обработки.
Иначе:
    использовать старый признак обработки или вывести ошибку, если старая логика уже недоступна.
```

```{admonition} Пример ошибки
Операция недоступна: данные документа ещё не подготовлены для новой схемы обработки.  
Завершите подготовку данных и повторите действие.
```

Проверка в прикладном коде не заменяет проверку перед фазой contract, если фаза contract удаляет старую структуру. В этом случае перед установкой обновления всё равно нужна проверка готовности базы.

### Ручная проверка перед установкой

Ручная проверка используется, если автоматическая блокировка установки не реализована. В этом случае порядок проверки должен быть явно указан в описании релиза.

Порядок действий:

1. Перед установкой фазы contract откройте место, где контролируется выполнение подготовки данных.
2. Проверьте, что скрипт или обработка завершились успешно.
3. Проверьте, что нет ошибок выполнения.
4. Проверьте контрольным запросом или другим способом, что данные больше не находятся в старом состоянии.
5. Если проверка не пройдена, не устанавливайте фазу contract.
6. После устранения ошибок повторите проверку.

Для отложенных DataInstall-скриптов текущий доступный вариант контроля — ручная проверка состояния скрипта перед установкой фазы contract. Нужно проверить, что нужный скрипт отсутствует в очереди отложенного выполнения и не завершился с ошибкой. Если скрипт остаётся в очереди или содержит ошибку, устанавливать фазу contract нельзя.

Технические детали проверки отложенных DataInstall-скриптов описаны в разделе [Работа механизма DataInstall](https://help.globalerp.ru/books/GlobalServerAppGuide/SNAPSHOT/html/080_addition/230_%D0%B4%D0%B0%D1%82%D0%B0%D0%B8%D0%BD%D1%81%D1%82%D0%B0%D0%BB.html#id9).

В описании релиза достаточно указать:

- какой скрипт или механизм подготовки должен быть выполнен;
- где проверить результат выполнения;
- какое состояние считается успешным;
- что делать, если подготовка не завершена или завершилась с ошибкой.

```{admonition} Пример пометки в описании релиза
Перед установкой релиза необходимо проверить, что скрипт подготовки данных <наименование скрипта> выполнен успешно.  
Если скрипт не выполнен или завершился с ошибкой, устанавливать релиз запрещено.
```

Успешный результат ручной проверки — подготовительный скрипт выполнен, ошибок нет, контрольная проверка не находит данные, которые должны были быть подготовлены до фазы contract.

### Комбинированная проверка

Комбинированная проверка используется для критичных изменений, где ошибка подготовки данных может привести к неработоспособности новой версии.

В этом варианте ручная проверка описывается в релизе, но дополнительно реализуется техническая защита: проверочный скрипт или прикладная проверка, которая не даёт установить или использовать новую функциональность на неподготовленных данных.

Порядок действий:

1. В описании релиза укажите ручную проверку, которую нужно выполнить перед установкой фазы contract.
2. Добавьте автоматическую проверку, которая блокирует установку или выполнение критичной функции, если данные не готовы.
3. В тексте ошибки укажите, что именно не подготовлено.
4. После завершения подготовки повторите ручную и автоматическую проверку.

Комбинированный вариант предпочтителен, если фаза contract удаляет старую структуру, а ошибка проверки может привести к неработающей бизнес-логике.

## Использование DataInstall при expand/contract

DataInstall может использоваться в подходе expand/contract как один из механизмов подготовки системы к новой структуре данных.

На фазе expand обычный DataInstall может применяться для быстрой установки служебных данных, которые нужны сразу после обновления: состояний, признаков, типов объектов, связей или небольших наборов преднастроенных данных.

Между фазами отложенный DataInstall-скрипт может применяться для длительной подготовки или переноса данных, если такую обработку нельзя выполнять в составе обычного обновления. В этом случае бизнес-логика должна учитывать переходное состояние данных до завершения подготовки.

DataInstall не является обязательной частью подхода expand/contract. Для подготовки данных могут использоваться и другие механизмы: скрипты миграции, пакетные обработки или отдельные регламентированные процедуры.

Технические детали работы обычных и отложенных DataInstall-скриптов описаны в разделе [Работа механизма DataInstall](https://help.globalerp.ru/books/GlobalServerAppGuide/SNAPSHOT/html/080_addition/230_%D0%B4%D0%B0%D1%82%D0%B0%D0%B8%D0%BD%D1%81%D1%82%D0%B0%D0%BB.html#id9).

## Пример

Пример изменения: для большой таблицы документов нужно перейти от старого текстового признака обработки к новой ссылочной структуре.

В старой версии признак обработки хранится в текстовом атрибуте документа. В новой версии добавляется отдельный справочник правил обработки и ссылочный атрибут на правило. Таблица документов содержит большой объём записей, поэтому перенос нельзя безопасно выполнить одним быстрым скриптом в процессе установки релиза. С документами продолжают работать пользователи, из-за чего длительный скрипт может конфликтовать с пользовательскими операциями и блокировками.

**Фаза 1 — expand**

В систему добавляются:

- новый справочник правил обработки;
- новый ссылочный атрибут документа;
- служебные данные для новых правил;
- код, который умеет работать с новым ссылочным атрибутом, но продолжает поддерживать старый текстовый признак.

Старая структура на этой фазе сохраняется. Пользователи продолжают работать с документами. Если для документа уже заполнен новый ссылочный атрибут, бизнес-логика использует его. Если новый атрибут ещё не заполнен, бизнес-логика использует старый текстовый признак.

**Подготовка данных между фазами**

После установки первой фазы запускается подготовка данных. Скрипт или регламентированная обработка постепенно заполняет новый ссылочный атрибут для существующих документов.

Так как таблица большая и с ней работают пользователи, подготовка может выполняться не за один проход. Обработка должна учитывать, что часть документов уже переведена на новую структуру, а часть остаётся в старом состоянии.

На этом этапе контролируется результат подготовки:

- сколько документов уже обработано;
- остались ли документы без нового ссылочного атрибута;
- были ли ошибки при обработке;
- завершились ли отложенные или фоновые скрипты, если они использовались.

**Проверка готовности**

Перед установкой второй фазы выполняется проверка готовности. Проверка должна подтвердить, что все документы, которые должны работать по новой структуре, получили новый ссылочный атрибут.

Если остаются документы в старом состоянии или подготовка завершилась с ошибкой, установка второй фазы запрещается. В этом случае нужно завершить подготовку данных, исправить ошибки и повторить проверку.

**Фаза 2 — contract**

После успешной проверки устанавливается второе обновление. Бизнес-логика переключается на новую ссылочную структуру. Старый текстовый признак и временная поддержка старого способа обработки удаляются.

## Недопустимые варианты

Не допускается выпускать изменение, при котором:

- новая бизнес-логика сразу требует результат длительной подготовки данных;
- длительный скрипт выполняется в составе обычного обновления;
- старая структура удаляется до завершения переноса данных;
- отсутствует способ проверить результат подготовки данных;
- не описаны действия при ошибке или неполном выполнении;
- данные, необходимые для старта системы, устанавливаются отложенным механизмом.