Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions docs/MIGRATION_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,11 @@ Source: code exploration 2026-06-24 on `deploy-test` @ `93f6f62`. Version **0.9.
- [x] Removed dead `serverless-wsgi` npm plugin + stale `requirements_*` scripts from `package.json`; `yarn install --mode update-lockfile` then `--immutable` clean (§4.6).
- [x] memory kept **2096 MB** (no evidence to change).

### Phase 5 — Cutover
- [ ] Deploy to test stage; **`cli_ab_test` prod-vs-new** differential validation (the built-in harness)
- [ ] Pre-stage rollback PR; promote `deploy-test → main`; ~30-min prod watch
- [ ] Post-healthy cleanup: delete legacy Flask/graphene + `handler.py`; file runbook feedback PR
### Phase 5 — Cutover 🟡 (pre-staged; deploy held for AWS creds + prod go-ahead)
- [x] Cutover plan written → `docs/PHASE5_CUTOVER.md` (in-place replacement, rollback, differential validation via `cli_ab_test`, promote + 30-min watch, post-healthy cleanup scope)
- [x] Differential tool confirmed: the built-in `cli_ab_test` covers the full migrated surface — reuse it (no `drive_live.py` needed); in-process parity already proven (`test_strawberry_parity.py`, 11 checks)
- [ ] Deploy to test; `cli_ab_test prod-vs-test`; pre-stage revert PR; promote; ~30-min watch **← needs creds + go-ahead**
- [ ] Post-healthy cleanup (delete graphene `schema.py`/Flask/`handler.py`, rename `strawberry_schema.py` → `schema.py`, drop legacy deps); file runbook-feedback PR

---

Expand Down Expand Up @@ -218,4 +219,10 @@ Source: code exploration 2026-06-24 on `deploy-test` @ `93f6f62`. Version **0.9.
- CI already on the `-uv` test + deploy workflows. Memory stays 2096 MB.
- **Next:** Phase 5 — deploy to test stage, run `cli_ab_test` (prod-vs-new differential), promote with a pre-staged revert, then post-healthy legacy cleanup (delete `schema/` graphene + Flask + `handler.py`, rename `strawberry_schema.py` → `schema.py`).

### 2026-06-24 — Phase 5 pre-staged (deploy held)
- Wrote `docs/PHASE5_CUTOVER.md`: in-place replacement (same stack/function/routes/URL), rollback (pre-staged revert PR + triggers; image-only — no new write shapes to reverse), differential validation via the **built-in `cli_ab_test`** (prod-vs-test; covers the exact migrated surface), promote + 30-min prod watch, and the post-healthy legacy-cleanup scope.
- **Key reuse:** solvis already has its cross-stage differential tester (`cli_ab_test`) — no need to build a `drive_live.py` like the Model pilot did. In-process parity is already locked by `test_strawberry_parity.py`.
- **Held:** the actual test-stage deploy + `cli_ab_test` + promote need AWS creds + an explicit prod go-ahead. Engineering (Phases 0–4) is complete and stacked as PRs #88–#93.
- **Cleanup ordering:** legacy graphene/Flask deletion must come **after** cutover — the differential tests and the `CompositeRuptureSections` delegation still import the graphene schema.

<!-- Append new dated entries above this line as the migration proceeds. -->
67 changes: 67 additions & 0 deletions docs/PHASE5_CUTOVER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Phase 5 — Cutover plan (`solvis-graphql-api` Graphene → Strawberry)

**In-place replacement.** Same CloudFormation stack, ECR function (`ecr-app`), routes
(`/{any+}` GET/POST `private: true`, OPTIONS), API-Gateway key, and URL. The container
image changes (Mangum entry + Strawberry schema); the deployed surface does not. Rollback =
redeploy the previous image (revert the merge).

Owner: @chrisbc · Epic: GNS-Science/nshm-toshi-api#359 · Tracking: #86 · Log: `MIGRATION_LOG.md`

> ⚠️ **Requires AWS credentials + an explicit prod go-ahead.** Everything below the "Deploy"
> heading is held until then. The engineering (Phases 0–4) is complete and merged-ready.

## §1 Validation strategy — reuse the built-in differential harness

Solvis already ships a cross-stage differential tester: **`cli_ab_test`**
(`solvis_graphql_api/scripts/cli_ab_test.py` + `ab_test/`). Its checks cover the exact
migrated surface: `about`, `color_scale`, `get_parent_fault_names`, `get_location_list`,
`get_radii_set`, `filter_ruptures`, `filter_rupture_sections`, `locations_by_id`,
`composite_rupture_detail`. **Reuse it — don't build a `drive_live.py`.**

In-process parity is already proven by `tests/test_strawberry_parity.py` (11 differential
checks, byte-identical `data`). `cli_ab_test` extends that proof to the live deployed stages.

## §2 Deploy to test

1. Capture a baseline: prod CloudWatch p50/p95 duration, error rate, `Max Memory Used` (confirm 2096 MB headroom). Screenshot it.
2. Merge the stack to `deploy-test` (see §5 on whether to land the cleanup before or after) → the `deploy-to-aws-uv.yml` workflow builds the image (uv → `requirements.txt` → Docker/ECR) and deploys. Deploy smoke `query QueryRoot{about}` must pass.
3. **`cli_ab_test WORK/ab.toml -A prod -B test -v`** — prod (legacy) vs test (new). **Every check must PASS.** (Keys/endpoints supplied out-of-band, never committed.)

## §3 Rollback

- **Pre-stage the revert PR** as a draft against `main`: title `revert: <promote-title>`, body
containing `git revert <merge-sha>`, plus the trigger: *publish if 5xx rate > X% sustained
> Y min, or `cli_ab_test` reports any mismatch.*
- Rollback = merge the revert → the workflow redeploys the previous image. Re-run
`cli_ab_test` (or the deploy smoke) to confirm legacy behaviour restored.
- **Stateful note:** the migration adds **no new write shapes** — the `BinaryLargeObject`
store is unchanged (pydantic+boto3 writes the same item/JSON). Rollback is image-only; no
data migration to reverse.

## §4 Promote & watch

1. Promote `deploy-test → main` via PR (`release: promote solvis strawberry to prod`) — only after §2 passes.
2. `cli_ab_test ... -A prod -B test` once more post-promote isn't meaningful (both become new);
instead run the deploy smoke + watch prod ~30 min: `aws logs tail /aws/lambda/<fn> --follow`
+ CloudWatch error rate / p95 vs the §2 baseline.
3. Comms at: deploy start, test validated, promote PR open, prod deploy, "healthy" (30 min), or rollback.

## §5 Post-healthy legacy cleanup (after prod is confirmed healthy)

Behaviour-neutral; do as its own validated PR. **Must come AFTER cutover** — the differential
tests + `CompositeRuptureSections` delegation still import the graphene schema, so it can't be
deleted until the new path is proven live.

- **Delete:** `solvis_graphql_api/schema.py` (graphene root) + `solvis_graphql_api.py` (Flask
app) + `handler.py` (serverless-wsgi workaround) + the graphene type modules' Graphene
classes (the **compute** helpers in `cached`, `color_scale`, `geojson_style`,
`composite_solution/*` stay — Strawberry resolvers reuse them).
- **Rewire** `CompositeRuptureSections` off the graphene-delegation (`_GrapheneSections`) onto
the retained compute helpers directly (or keep the helpers and drop only the graphene types).
- **Rename** `strawberry_schema.py` → `schema.py`; fix imports (`app.py`, tools, tests).
- **Drop deps:** `graphene`, `graphql-server`, `flask`, `flask-cors`, `serverless-wsgi`
(python). **Keep** `graphql-relay` (global-id encoding) + the compute libs.
- **Tests:** `test_strawberry_parity.py` loses its legacy comparator → convert to assert against
recorded expected values (or fold into the `cli_ab_test` corpus); `test_corpus_replay.py`
already targets the Strawberry schema.
- Re-run `cli_ab_test` (prod vs the cleaned test stage) before promoting the cleanup.
Loading