golden-migration

Міграція Golden

Новий family-golden, family-application-api, Gateway та Electron вводяться поруч із чинними stack-golden і Golden Electron. Production перемикається частинами, без big bang.

Незмінні правила

  1. Legacy collections, queues та endpoints не змінюємо до переведення відповідної логіки.
  2. Нові consumers використовують окремі versioned queues та commands.
  3. Read-only sync можна виконувати паралельно для порівняння з legacy.
  4. Partner-write для конкретної TU та операції виконує лише legacy або нова система, але не обидві.
  5. Workers мають idempotency, lock і явний owner.
  6. Rollback перемикає routing назад; canonical дані не видаляються.

Одиниця міграції

Мігруємо не весь stack-golden одним релізом і не окремий endpoint без його залежностей. Одиниця міграції — завершена capability: frontend route, canonical data, partner operation, background writer, logs і post-effects.

Для capability, яку вже переключили:

  1. Frontend звертається лише до family-application-api.
  2. Canonical collection має одного owner-а запису для конкретних полів та операцій.
  3. Старий публічний route вимкнений або тимчасово проксіює команду у V2; він не виконує власний write.
  4. Ще не перенесений legacy-код читає canonical collection через один compatibility repository.
  5. Старі довільні Mongoose models не направляємо на нову collection: назви полів, шифрування та семантика query несумісні.
  6. Після parity, pilot і rollback period legacy collection та bridge цієї capability видаляються.

Постійного двостороннього sync між legacy і canonical collections не робимо. Тимчасова проєкція допустима лише як короткий rollback-bridge з метрикою lag і явною датою видалення.

Співіснування V2 і legacy

flowchart LR
    Front["Frontend"] -->|"перенесені routes"| V2["family-application-api"]
    Front -->|"решта routes"| Stack["stack / stack-golden"]
    V2 --> Family["family-golden"]
    Family --> Partner["Golden partner API"]
    V2 --> Canonical[("family_admins / family_tus")]
    Family --> Canonical
    Stack --> Compat["LegacyGolden compatibility repositories"]
    Compat --> Canonical
    V2 -->|"лише неперенесені post-effects"| LegacyEffects["versioned legacy commands"]
    LegacyEffects --> Stack

Compatibility repository є anti-corruption layer: він робить конкретні canonical query, розшифровує secret на межі та повертає стару форму тільки legacy consumer-у. Його не імпортуємо хаотично по файлах — старі сервіси отримують вузькі методи на кшталт findActiveAdmins(), findTUCredentials() і findTUsByPartnerIds().

Допускається кілька writer-ів однієї collection лише для різних, явно зафіксованих полів. Наприклад, поки TU Card живе у Stack, Stack володіє зв’язком KM, а V2 refresh — partner profile/status. Два сервіси не можуть одночасно змінювати одне assignment або partner password.

Середовища

КонтурХто працюєДані й доступ
Legacy productionЧинні Stack, Golden service та ElectronПоточні endpoints, queues і collections
V2 testНові family-application-api, Gateway, Golden service та ElectronTest endpoints, окремі queues/collections, test operators і TU
V2 shadowНові server services без live-writeЧитають production-compatible дані й порівнюють результат із legacy
V2 pilotAllowlist операторів/TULive-write дозволений лише для явно переведеного scope
V2 productionНові servicesLegacy лишається rollback-контуром до завершення міграції

Test Electron має окремі appId, product name, user data directory, API origin, socket namespace та update channel. Його можна встановити поруч із production Electron.

Етапи

ЕтапРезультатУмова переходу далі
01Packages і tooling@it-monkeys/contracts, @it-monkeys/backend-common та family-platformPackages збираються через link, pack і ручний release
02Каркасиfamily-application-api, Gateway і family-golden розгорнуті без production routingHealthcheck, auth, RMQ і observability працюють
03Admins і TU read modelНовий Golden читає partner API та пише canonical Admin/TUПовторний sync і backfill не створюють дублікати
04Shadow comparisonLegacy і V2 результати порівнюються автоматичноУзгоджені counts, status, assignments і profile fields
05Test ElectronНова програма працює на V2 test із тестовими TULogin, startup, reconnect, sync та workspace проходять сценарії
06Production pilotКілька операторів/TU переведені через allowlistНемає подвійних writes, втрати actions/tasks і деградації partner API
07Вертикальна міграціяФічі переходять по одній із власним flag і rollbackКожна фіча пройшла parity та pilot
08РозширенняAllowlist поступово збільшуєтьсяМетрики й support стабільні на кожному кроці
09Вимкнення legacyСтарі workers/routes зупинені, repository архівується пізнішеПовний scope працює на V2 і минув rollback period

Фактичний стан на 2026-08-27

ЧастинаУже єЩо блокує production cutover
Packagescontracts, backend-common, canonical schemas/repositoriesRelease packages і точні production versions
Deployablesfamily-application-api, family-golden, healthcheck, Stack JWT, internal HTTPProduction config, secrets, network policy та telemetry
Adminsmigration, list/resolve/create/update, Golden credentials validationLegacy admin compatibility repository, TU count, routing flag
TU catalogmigration, Golden list adapter, compare/refresh, canonical updateRead endpoints, shadow/dry-run, legacy TU compatibility reads
TU passwordGolden partner mutation і encrypted canonical passwordLegacy Electron/workspace повинні читати canonical credentials
Assignmentssupervisor/operator batch flow, stale-state check, user/TU updateTasks, Electron effects, canonical logs і production parity
KMservice method синхронізації зі Stack TU CardВизначити Stack command; refresh не має знімати KM
Logsтимчасовий спільний writerНові collections і fields від власника log migration

Поточні migration endpoints є apply-командами, а не безпечним shadow: повторний запуск переносить значення зі старих collections і може перезаписати нові canonical зміни. Перед production вони мають отримати dry-run/compare, deployment guard і бути вимкнені одразу після cutover.

Перший production vertical slice

frontend Admin/TU routes
  → family-application-api
  → internal HTTP
  → family-golden
  → Golden partner API
 
family-application-api ↔ canonical family_admins / family_tus
stack-golden → compatibility repositories → ті самі canonical collections

На цьому тижні реалістичний production scope:

ЧергаCapabilityРезультатЧи можна закрити повністю
0Dark deploy двох сервісівHealth, auth, internal network, secrets та індекси перевірені без frontend traffic
1AdminsFrontend CRUD працює через V2; family_admins — єдине джерело; legacy лише читає через adapter
2TU catalog/readBackfill, read API та shadow comparison працюють на family_tus; legacy Electron читає canonical TU✅ після compatibility reads
3TU passwordV2 є єдиним writer partner/canonical password; legacy workspace читає розшифровані canonical credentials✅ після credentials adapter
4TU refresh writeСтарий LadySyncService вимкнений; V2 володіє profile/status/admin✅ лише після detach post-effects
5Manual assignmentsДо cutover legacy flow пише assignments у canonical TU через compatibility repository; після cutover усі TU/user/log/task/Electron effects переходять у V2❌ V2 не вмикати до parity
6Electron workspace, statistics, favorites, senderЗалишаються у stack-golden і старому Electronокремі наступні vertical slices

Отже, самі сервіси можна безпечно залити цього тижня. Frontend routing треба вмикати окремими flags: admins, tu-read, tu-password, tu-refresh, tu-assignments. Один загальний goldenV2=true для всього сервісу не використовуємо.

Міграція Admins

family_admins стає єдиним source of truth. Старий golden_admins не можна просто замінити імпортом schema: legacy очікує id_api, відкритий password, isBlocked і підтримує довільні query, а canonical document має login, passwordEncrypted і status.

У stack-golden є 13 файлів із прямим import AdminModel, плюс залежності від legacy IAdmin. Вони покривають CRUD, sync/statistics, Electron/API operations і тести. Для завершеного cutover:

  1. Додати один LegacyGoldenAdminRepository поверх FamilyAdminRepository.
  2. Реалізувати лише фактично потрібні typed methods; не переносити legacy endpoint із довільним Mongo query.
  3. Замінити прямі imports AdminModel у runtime-коді на repository.
  4. У коротке maintenance-вікно зупинити legacy Admin writes, виконати backfill зі збереженням _id і звірити count/login/status/hash credentials.
  5. Переключити frontend Admin routes на V2; старий Admin CRUD вимкнути або зробити proxy без власного write.
  6. Вимкнути Admin migration endpoint і залишити golden_admins тільки як rollback snapshot на обмежений строк.

Compatibility spike вже підтвердив роботу @it-monkeys/contracts і @it-monkeys/backend-common з legacy Mongoose 6.2.2. Не можна неявно оновлювати Mongoose у stack-golden: підняття lock-версії вже спричиняло сторонні type errors.

Міграція TU

TU складніша за Admin: GoldenLadyModel читають і змінюють Electron, favorites, metrics, statistics, workspace, sync та assignment flows. Тому підміна model на family_tus небезпечна.

Робимо один LegacyGoldenTURepository з вузькими projections:

  • знайти TU/credentials за partner ID;
  • знайти canonical admin і partner admin ID;
  • отримати TU за списком partner ID або Stack Card ID;
  • оновити лише ті legacy-owned поля, власник яких зафіксований у migration matrix.

Порядок cutover:

  1. Додати GET /v2/tus і, за потреби frontend, GET /v2/tus/{id} з role scope.
  2. Додати dry-run TU migration та shadow compare без canonical write.
  3. Перевести legacy runtime reads, необхідні Electron/workspace, на LegacyGoldenTURepository.
  4. Зупинити старий TU sync, виконати фінальний backfill зі збереженням _id, звірити profile/status/admin/assignments/password presence.
  5. Увімкнути V2 read і password окремими flags.
  6. Увімкнути V2 refresh write лише після того, як видалення/деактивація виконує всі operator detach effects.
  7. Manual assignment routes залишити legacy до готовності tasks, Electron SET_LADY/DROP_LADY та canonical logs; потім перемкнути всією capability за один cutover.

Поки manual assignments належать legacy, вони вже повинні змінювати family_tus.assignments через compatibility repository, а не продовжувати запис у golden_ladies. Інакше V2 read одразу стане застарілим.

KM не входить у системний detach: його canonical зв’язок оновлюється тільки під час зміни TU Card у Stack.

Shadow comparison

Поточний POST /v2/tus/refresh є writer і не використовується як shadow. Потрібна окрема read-only команда, яка отримує partner snapshot, викликає TUSyncService.compareSnapshot, повертає diff і нічого не змінює у Mongo, users, logs або partner API.

Для кожного shadow run зберігаємо або рахуємо:

  • кількість записів legacy/V2;
  • пропущені та зайві external IDs;
  • відмінності status і assignments;
  • hash нормалізованих важливих полів;
  • час і кількість partner requests;
  • помилки та повторні спроби.

Shadow не виконує password change, assignment, sender, task close, media upload, online ping або іншу partner mutation.

Production cutover checklist

  1. Зафіксувати package versions; не використовувати локальні links.
  2. Перевірити однаковий cipher key у family-application-api, family-golden і legacy compatibility layer.
  3. Запустити db:indexes:sync як окремий deployment job після перегляду diffIndexes; application instances не синхронізують indexes на startup.
  4. Закрити internal Family API від зовнішньої мережі.
  5. Обмежити або вимкнути apply-migration endpoints; роль director не повинна мати постійного доступу до повторного production backfill.
  6. Поставити feature flags у стан off, розгорнути обидва сервіси й перевірити health/partner test calls.
  7. Виконати dry-run, maintenance freeze, Admin backfill, потім TU backfill.
  8. Перевірити counts, IDs, status, admin relations, assignment relations і можливість розшифрувати вибіркові credentials без їх логування.
  9. Вмикати flags у порядку admins → tu-read → tu-password → tu-refresh; tu-assignments окремо.
  10. Для кожного flag мати owner, метрики, allowlist і одну дію rollback без видалення canonical data.

Перемикання Electron

Stack після login повертає, який workspace доступний оператору: legacy, v2-test або v2. TU, передана у V2 pilot, не повинна одночасно видаватися для роботи legacy Electron.

Gateway додатково тримає Redis lease на family + tuId. Lease захищає V2 від двох нових sessions, але головний захист від legacy/V2 конфлікту — routing і allowlist у Stack.

Перед розширенням pilot перевіряємо:

  1. Login, update і reconnect.
  2. Одна активна session на TU.
  3. Startup data та локальна БД.
  4. Chat/mail send із partner confirmation.
  5. Tasks, actions, online і sender sync з record-level ACK.
  6. Відновлення після падіння Electron, Gateway, API та Family service.

Перемикання server-фічі

Кожна фіча має окремий flag із scope family, user або tuId:

legacy read/write
  → V2 shadow read
  → V2 read + legacy write
  → V2 read/write для pilot
  → V2 read/write для Family

Не всі фічі потребують проміжного V2 read + legacy write. Його використовуємо лише там, де V2 може коректно прочитати legacy результат.

Rollback

  1. Заборонити нові V2 sessions або requests для pilot scope.
  2. Дочекатися ACK активних sync batches і зупинити V2 writers/workers.
  3. Повернути Stack routing та frontend feature flag на legacy.
  4. Перепідключити оператора до legacy Electron.
  5. Зберегти V2 collections і telemetry для аналізу; не робити rollback через видалення даних.

Після rollback новий writer не запускається повторно, доки не перевірено незавершені tasks, sender operations і partner mutations.