Процедура миграции доменов

Старт миграции (вьюшка MigrationStartView).

По POST она дёргает PddDomainMigration.create_migration_task и отдаёт её результат, как id миграции.

По GET она отдаёт статус миграции по её id.

При создании задания на миграцию

  1. Выбирается шард для организации.

  2. Создаётся запись в таблице migrations со статусом check, only_check_conditions=False и рандомным id.

    При этом, в settings прописывается словарь с входными параметрами метода:

    settings = {
        'admin': admin,
        'domain': domain,
        'deproot': deproot,
        'orgname': orgname,
        'tld': tld,
    }
    

Дальше в дело вступают воркеры.

Worker condition_check

Этот воркер перебирает все записи из migrations, у которых status=='check'.

  1. Для каждого из них он берёт lock.

  2. Проверяет, возможна ли миграция. На этом этапе функция проверки migration_status_and_data может вернуть разные ошибки, в том числе и in_progress. И этот in_progress будет означать, что в метабазе уже есть запись про организацию с таким же label, как для этого домена и ready=False.

    В этом случае воркер ищет уже идущую миграцию, и прописывает её id, как в поле migration_id атрибута data у текущей миграции.

  3. Если миграция разрешена, то её статус переводится в setup.

По итогу, миграция переходит в состояние setup, если всё хорошо, in_progress, если уже идёт миграция этого домена или в какую-то ошибку, если дальнейшее продолжение миграции невозможно.

Worker setup_migration

Этот воркер обрабатывает все записи из migration, что в состоянии setup.

  1. На каждую запись берётся лок.

  2. Запускается PddDomainMigration.setup_migration.

    При подготовке миграции проходят следующие шаги:

    1. Сначала проверяется, что свободен аккаунт заданный в переменной deproot, если нет, то возвращается исключение root_maillist_exists.

    2. Дальше получаем список ящиков и рассылок на домене, а так же информацию об админе из паспорта.

    3. Заводим запись про организацию в метабазе и основной базе.

    4. Создаём рассылку для корневого отдела.

    5. Сохраняем алиасы домена из ПДД в Директорию.

    6. Создаём саппортного робота для Ямба.

  3. Если всё проходит хорошо, то миграция переводится в состояние migration

  4. Если случилось исключение, то в rollback с code: unknown.

Есть странный коммент. Я его не понимаю. Зачем табличку migrations обновлять дважды:

MigrationModel(main_connection).update_one(
    migration_id=task_id,
    status=status,
    data=data
)
# запомним ид заготовки организации
MigrationModel(main_connection).update(
    update_data={'org_id': migrator.org_id},
    filter_data={'id': task_id},
)

Update. Выяснили с Наилем, что особого смысла в этом нет, можно было бы одним update обойтись.

Worker migrate_worker

Этот занимается тем, что мигрирует один ящик.

  1. Он достаёт из таблички первую запись, у которой migrated is NULL.

  2. Берёт лок на её id

  3. Запускает PddDomainMigrationEmail.migrate_email.

  4. Тот в свою очередь, достаёт данные по id из таблицы migrate_email и создаёт по ним пользователя в Директории.

  5. Подписывает пользователя на корневой отдел.

  6. Синхронно активирует для него b2b диск.

  7. В конце - создаёт в migrate_log две записи, про то, что пользователя создали и про то, что подписали его на корневую рассылку.

  8. Если в процессе случается PddException, то ошибка логгируется в migrate_log.

    Note

    Прочие ошибки в migrate_log не попадают, однако всё равно вызывают откат миграции. Надо это поправить!

Воркер migrate_finalizer

Этот воркер выбирает все записи со статусом migration. Потом считает все migrate_email, относящиеся к этой организации. Если у них у всех поле migrated=True, то воркер создаёт лок по id организации. Дальше:

  1. В табличке migrations для миграции проставляется статус finalization.

  2. Вызывается PddDomainMigration.finalize_migration, который:

    • по domain_id проставляет в паспорте признак воркспейсности, через указание для домена organization_name.

    • дёргает ручку ws_update в ПДД.

  3. Если случается ошибка, то миграция переводится в статус rollback, а для всех записей в таблице migrate_email, проставляется флаг migrated=False.

  4. Если ошибки не было, то:

    • организации в метабазе проставляется признак ready=True;

    • все записи в migrate_email, относящиеся к организации, удаляются;

    • генерится действие organization_migration;

    • отправляется письмо админу организации;

    • отправляются письма всем пользователям организации;

Note

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

Воркер migrate_rollback

Этот воркер вынимает из таблицы migrations все записи со статусом rollback. И для каждой из них поочереди берёт лок, и дальше делает следующее:

  1. Если org_id в записи нет, то сразу проставляется статус rollback_completed.

  2. Если к организации привязаны записи в табличке migrate_email, то на каждую такую запись берётся лок по id и если хотя бы для одной лок взять не получилось, то решаем, что всё ещё идёт миграция (что странно, ведь миграция в статусе rollback) и просто делаем выход из процедуры migrate_rollback.

    Note

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

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

  3. Если на все записи из migrate_email удалось взять лок, или их уже нет, то пробуем достать из базы Директории master_domain. Если его почему-то нет, то делаем continue и откат миграции зависает. Возможно именно это произошло с 45 организациями, у которых сейчас ready=False.

  4. Если все предыдущие пункты прошли, то запускаем PddDomainMigrationRollback.rollback:

    • Удаляются рассылки, созданные для Отделов.

    • Деактивируются b2b пользователи в Диске.

    • Удаляется признак воркспейсности домена в паспорте (через сброс organization_name).

    • Дожидаемся, пока у всех пользователей пропадёт атрибут 1011.

    • Делаем downgrade домена в ПДД.

    • Дёргаем ручку Abook workspace/rollback для отката общей адресной книги.

    • Записываем действие organization_delete.

    • Удаляем пользователей из мета-базы Директории.

    • Сбрасываем руководителей отделов и авторов Команд в None.

    • Удаляем через ПДД аккаунт каждого робота, созданного для сервисов.

    • Чистим RobotServiceModel.

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

      Note

      но кстати, почему-то migrate_log табличка не очищается.

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

  5. Если метод rollback отработал корректно, то миграции проставляется статус rollback_completed. Если произошло исключение PddException, то статус error и в data пишется код из исключения. В случае любого другого исключения типа Exception, просто в статус error переходит миграция.

Переходы статусов

check
|- in_progress, если уже есть параллельная такая же миграция (здесь
|  возможна ошибка, потому что получается две записи в таблице migration).
|- error, или другие ошибки если миграция не возможна
|- setup
   |- ???
|- rollback
   |- rollback_completed
   |- error