Repository navigation
ci: add per-database concurrency groups to database-migration migrate jobs - #1816
Conversation
… jobs The three `migrate-*` jobs had no concurrency group, so two dispatches for the same cloud and environment could run `migrate` concurrently against one schema_migrations table. Each job now takes a `db-migrate-<cloud>-<environment>` group. These jobs are not Terraform state writers: they run `terraform init` plus `terraform output -raw` to read a database endpoint and take no state lock, so reusing the `aws-tfstate-*` / `gcp-tfstate-*` / `azure-tfstate-*` groups from #1806 and #1801 would serialize migrations against unrelated deploys while still not identifying the database. The group keys on the resource that actually needs protecting instead, leaving different clouds and different environments free to run concurrently, including under `cloud=all`. `cancel-in-progress: false` throughout: cancelling mid-`migrate` leaves schema_migrations dirty and needs the CUDLY_FORCE_MIGRATION_VERSION recovery path. `inputs.environment` is a required `choice` on `workflow_dispatch` but a plain `type: string` on `workflow_call`, where GitHub does not enforce a non-empty value, so the group expression falls back to an `unset` sentinel rather than collapsing every environment into one group on an empty interpolation. `validate` and `summary` touch no database and are deliberately left without a group, as is the workflow top level, which would otherwise serialize the three clouds against each other. Refs #1593
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe database migration workflow adds separate concurrency controls for AWS, GCP, and Azure. Each control scopes migration runs by cloud and environment, preserves parallelism across clouds, and prevents cancellation of active migrations. ChangesMigration concurrency
Estimated code review effort: 2 (Simple) | ~10 minutes Mergeability Score: ⚪ Minimal · up to This change adds per-database concurrency to migration jobs while preserving safe parallelism across clouds and environments; no actionable merge-blocking risk remains after normal checks and review. Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Closes #1593
Why the diff is smaller than the issue title
#1593 names four workflows. Three of them are already covered on
main:rollback.ymlandcleanup-staging.ymlby #1806/#1801, anddestroy-fargate-dev.ymlby #1806. Enumerating every job onorigin/main(23ad4e4) confirms it, and shows
database-migration.ymlas the only onewith no group at the workflow level or on any of its five jobs:
rollback.ymlrollback-*;validate/summaryexempt)cleanup-staging.ymldestroy-*;guardexempt)destroy-fargate-dev.ymldestroy;guardexempt)database-migration.ymlSo this PR only touches the fourth.
The change
Job-level
concurrencyonmigrate-aws,migrate-gcpandmigrate-azure.Why not the
*-tfstate-*groups. These jobs are not Terraform statewriters. They run
terraform initplusterraform output -rawto read adatabase endpoint, which takes no state lock. Reusing
aws-tfstate-*/gcp-tfstate-*/azure-tfstate-*would serialize migrationsagainst unrelated deploys and still not identify the database. The group keys
on the resource that actually needs protecting,
db-migrate-<cloud>-<env>:concurrent, so
cloud=allstill fans out across the three clouds.cancel-in-progress: false. Cancelling mid-migrateleavesschema_migrationsdirty, which then needs theCUDLY_FORCE_MIGRATION_VERSIONrecovery path in
internal/database/postgres/migrations/migrate.go.Empty-group risk, handled explicitly.
environmentis a requiredchoiceconstrained to
dev|staging|prodonworkflow_dispatch, but on theworkflow_calltrigger it is a plaintype: string;required: truemakes acaller pass the key, but GitHub does not enforce a non-empty value. An empty
interpolation would collapse every environment into one group, so the
expression falls back to an
unsetsentinel.validaterejects an environmentoutside the allowlist before any
migrate-*job runs, so the sentinel should beunreachable today; it is there so the group cannot become silently ambiguous if
that ordering changes. There is currently no in-repo
uses:caller of thisworkflow.
validateandsummarytouch no database and get no group. No top-level groupeither, since that would serialize the three clouds against each other.
Verification
yaml.safe_load).migrate-*jobs have a group withcancel-in-progress: false, 0 uncovered,3 distinct groups, 0 non-
migrate-*jobs given a group, top-level stillNone.actionlintreports the identical 5 pre-existing shellcheckinfofindingsbefore and after (line numbers shift only); no new findings. Confirmed
non-vacuous by a negative control: swapping in a bogus
inputs.*referencemakes actionlint flag that exact
group:line, so it does type-check thekey. Note
actionlintis not wired into this repo's CI or pre-commit; thiswas a local run.
Runtime concurrency behaviour cannot be verified without dispatching real
migrations, so it is argued from the trigger definitions and GitHub's
documented semantics rather than observed.
Noted, not fixed here
Adding a group makes eviction possible: GitHub keeps one pending entry per
group, so a queued migration can be cancelled by a later dispatch. Unlike
rollback.yml, this workflow'ssummaryjob does not exit 1 on a non-successresult, so an evicted migration would leave the run green. That is a separate
concern from #1593 and is documented inline rather than changed here. LeanerCloud/cloud-commitments-platform#114
(
terraform initwith no-backend-config) is likewise out of scope.Summary by CodeRabbit