Перейти к содержанию

Перенос схемы и атомарное переключение aliases

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

Полные команды находятся в manage.py: create, reindex, aliases и switch. Автор не выполнял их. Документация reindex и документация index aliases проверены как первичные источники API; ниже описан учебный протокол нашего проекта.

Приложение читает стабильное имя

Alias — имя, которое указывает на один или несколько физических индексов. Gateway ищет через library-read, поэтому ему не нужно знать, хранится ли текущая библиотека в v1 или v2. Отдельный library-write обозначает назначение записи и получает is_write_index: true. В нашем импортёре запись всё ещё направляется в явно выбранный физический индекс, чтобы оператор видел объект изменения.

Первоначально оба alias указывают на library-v1. Команда для читателя:

python manage.py aliases library-v1

После её ожидаемого успеха запрос POST /library-read/_search обращается к прежним документам. Физического копирования здесь нет. Если вы добавили к read-alias одновременно v1 и v2, одинаковые _id в разных индексах могут дать две карточки. Alias не выполняет автоматическое объединение редакций материала по нашему doc_id.

Имя чтения удобно сохранять при миграции, но само по себе оно не проверяет готовность нового индекса. Можно переключить его на пустую или неполную копию. Поэтому подготовка и проверка содержания должны завершиться до операции изменения aliases. Это разделение ответственности делает миграцию обозримой для сопровождающего проекта.

Новый индекс получает собственную схему

Изменим index.json, например добавив защищённое техническое обозначение в анализатор. Создадим library-v2 и только затем запустим перенос. Reindex читает сохранённые _source старого индекса и повторно индексирует их по настройкам назначения. Он не переносит автоматически желаемые mappings из исходника и не превращает ошибочную схему назначения в правильную.

Команды учебной миграции для читателя:

python manage.py create library-v2
python manage.py reindex library-v1 library-v2

Вторая команда синхронно запрашивает перенос и проверяет явные failures и timed_out. При отказе она не переключает aliases. Часть документов уже могла оказаться в v2, поскольку reindex тоже не является атомарной заменой всего корпуса. Оператор должен исследовать назначение и решить, повторять перенос или пересоздавать его в отдельной учебной среде.

Наша команда создаёт v2 тем же файлом схемы, который вы предварительно отредактировали. В архиве не лежит заранее объявленная «улучшенная» схема с выдуманным качеством. Изменение должен иметь предметную причину и контрольный запрос. Так читатель связывает migration с конкретным полем, а не выполняет лишнюю копию только ради имени v2.

Копирование не отслеживает редактора непрерывно

Предположим, reindex уже прочитал старую редакцию article-107, а редактор затем изменил Markdown и импортировал новую запись в v1. V2 не обязан автоматически получить это изменение. Если после копирования сразу переключить чтение, пользователь увидит прежнюю редакцию. Атомарность aliases не исправляет разницу содержимого индексов.

Для учебного корпуса выберем понятный протокол: приостановить редакционные изменения и все импортёры, подготовить новую схему, выполнить перенос, затем синхронизировать v2 непосредственно из полного Markdown-корпуса. В v2 ещё нет локального манифеста, поэтому sync отправит полные проекции. Это также применит текущие правила подготовки корпуса, если они изменились отдельно от mapping.

Команда sync library-v2 не удалит произвольную лишнюю запись, перенесённую reindex и отсутствующую в новом корпусе: новый манифест не знает её идентификатор. Поэтому в учебном протоколе перед переносом v1 должен уже соответствовать проверенному корпусу. Для миграции из заведомо загрязнённого источника безопаснее создать пустое назначение и наполнить его только из Markdown.

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

Переключение одним запросом

После проверки v2 удаляем обе связи с v1 и добавляем связи с v2 в одном _aliases-запросе. Ниже полный объект действий для выбранной пары индексов:

{"actions":[
  {"remove":{"index":"library-v1","alias":"library-read"}},
  {"remove":{"index":"library-v1","alias":"library-write"}},
  {"add":{"index":"library-v2","alias":"library-read"}},
  {"add":{"index":"library-v2","alias":"library-write","is_write_index":true}}
]}

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

Команда проекта требует --writers-paused и --checked. Это явные утверждения оператора о выполненных действиях, а не автоматическая проверка отсутствия всех возможных writers. В README описано, что именно нужно проверить. Нельзя поставить эти флаги по привычке и считать миграцию согласованной только потому, что парсер принял аргументы.

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

Возврат к старой копии

Старый индекс не удаляем сразу после переключения. Он может пригодиться для сравнения и осознанного возврата. Но после возобновления записи v2 начинает получать новые изменения, которых v1 не знает. Простое обратное переключение тогда вернёт устаревшую библиотеку. План возврата должен учитывать эти редакции, а не только обратный набор aliases-действий.

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

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

Оглавление серии