Loading
docs(plans): amend S04 Step 3 to the background-migration model
What
Amend Step 3 of the database composition-root plan so it specifies the design the step builds: a background migration runner gated through /-/readiness, a constructor-injection dependency seam, and a tier-orchestrated composition root. The amendment is confined to Step 3 (its Scope, Approach, Test seam, Files, and Acceptance entries) and the research bullets it rests on.
Why
The merged Step 3 plan specifies two designs that are unsafe or awkward to build against:
- Synchronous, boot-aborting migrations. Running migrations as a critical component before the HTTP and probe listeners bind crash-loops a rolling deploy whenever a migration is slow or lock-contended: the startup probe is refused past its grace window and Kubernetes kills the pod before the migration can finish.
- A type-assertion OCI seam. Gating the OCI mount on a runtime
*postgres.Clienttype assertion couples the format tier to the concrete client type and hides the DB-less unit-test path behind an implicit contract.
The plan is the approval-gate artifact for the implementation MR. This amendment brings it in line with the S04 spec and the buildable design so the two no longer contradict.
What the amended Step 3 specifies
- Background migration + readiness gating. The runner is wrapped in an
asyncMigrationRunnerwhoseStartreturns immediately./-/livenessstays up;/-/readinessreports themigrationscheck at 503 until the pass completes, then 200. A failed pass cancels the app context (context.WithCancelCause) and exits non-zero, so Kubernetes restarts and retries. - Constructor-injection seam.
assembleAppbuildsdefaultBootDeps()itself and accepts variadicbootOverridemutators; production passeswithFatal(...), tests pass stubs.fatalis a generic process-abort hook that defaults to a no-op. - Client-returning tier orchestration.
newAppDatabasesurfaces the concrete*postgres.Clientas a distinct return value (nil under the stub).wireDatabasereturns it; a siblingwireServicesmounts the OCI tier on it only when non-nil — no type assertion. - File layout. The async component lives in
migration_runner.goand the injection seam pluswireServices/wireDatabaseinwire_database.go, keepingcmd/artifact-registry/main.goscoped to the composition root and CLI action. - Scope. Only this hookup's own two checks (
migrations,database) are readiness-gated; the broader dependency-aware readiness model (#58) stays out of scope.
The S04 spec is amended in lockstep in the implementation MR.
Edited by Suleimi Ahmed