Skip to content

v3: harden ORM migrations and align Rails history - #28118

Open
medvednikov wants to merge 18 commits into
masterfrom
agent/fix-v3-migration-safety-and-rails-compat
Open

v3: harden ORM migrations and align Rails history#28118
medvednikov wants to merge 18 commits into
masterfrom
agent/fix-v3-migration-safety-and-rails-compat

Conversation

@medvednikov

@medvednikov medvednikov commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

This follow-up addresses the seven remaining migration concerns identified after #28099:

  • verify PostgreSQL advisory-lock and MySQL named-lock ownership after callbacks, and reject PostgreSQL inspections inside caller-owned transactions
  • reject MySQL change_column until the ORM can preserve the complete existing column definition
  • document and test actual SQLite foreign-key enforcement
  • create Rails-compatible version-only schema_migrations tables while continuing to read and write legacy V metadata tables
  • require migrate_to to name an exact registered nonzero version
  • allow each migration to override transaction mode
  • require safety-critical dialect/type fields and reject duplicate migration names

Compatibility

Existing V history tables containing version, name, and applied_at remain supported. Newly created history tables use Rails' version-only shape, and the migrator derives display names from registered migrations when metadata columns are absent.

Validation

  • focused regression coverage for every behavior above
  • vlib/v3/migrations/migrations_test.v
  • vlib/v3/tests/migrations_public_api_test.v
  • v vet vlib/v3/migrations
  • v check-md vlib/v3/migrations/README.md

Draft while the branch-applied patch and normal PR checks complete.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

Copy link
Copy Markdown
Member Author

@codex Please finish this draft PR on its current branch. Decode .github/agent_fix_v3_migrations.py.gz.b64 with base64 --decode | gzip -d, run the resulting Python patch from the repository root, format the three changed V files, run the migration tests and checks listed in the PR body, then commit the validated source changes and remove both .github/agent_fix_v3_migrations.py.gz.b64 and .github/workflows/agent-fix-v3-migrations.yml. Keep the branch scoped to the seven fixes described in the PR body.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits. You can see your limits in the Codex usage dashboard.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@github-actions

Copy link
Copy Markdown

Master CI failures for 4a8793cbfcf7e16d88618d37d841ac2c1d41cf4d

Found 1 failed workflow runs. Detailed diagnostic comments follow.

  • V3 CI — run 32050000953

@github-actions

Copy link
Copy Markdown

Failed workflow: V3 CI

Run 32050000953; 1 failed job(s).

v3 — job 95446831348

Could not download this job log.

@medvednikov
medvednikov marked this pull request as ready for review August 18, 2026 13:49
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@medvednikov

Copy link
Copy Markdown
Member Author

Review verdict: Request changes

Reviewed current head abd34fb56362ca3707c313f24b6de8f8a74931a1. CI status and logs were deliberately excluded.

1. Blocker — commit the implementation as an ordinary source diff

At the reviewed head, vlib/v3/migrations/migrations.v is byte-for-byte the same blob as on the base branch. The advertised implementation instead lives inside a compressed/base64 Python payload; a workflow decodes it, edits the source tree, deletes itself and the payload, creates a commit, and pushes that commit using contents: write. Consequently, the SHA under review does not contain the migration implementation described by the PR, and merging this SHA would not land those source changes.

The generated .v, test, and README changes should be committed directly, with the patch payload and self-applying workflow removed. Reviewers should not have to execute opaque code with repository write credentials to discover the proposed diff.

2. P1 — the lock probe cannot prove uninterrupted or exact ownership

The decoded patch’s verify_migration_lock_ownership() only checks:

  • whether the current PostgreSQL backend has some matching advisory lock in pg_locks; or
  • whether MySQL’s IS_USED_LOCK() returns the current connection.

That is insufficient for the stated safety guarantee:

  • A callback can acquire the same lock a second time. Both probes pass, and the final single unlock succeeds, but one acquisition remains held indefinitely.
  • A callback can release and reacquire the lock. The probe passes even though another migrator could have entered during the unlocked interval.
  • The PostgreSQL query does not require mode = 'ExclusiveLock', so a callback could replace the exclusive lock with a shared lock and still pass the probe.

PostgreSQL session-level advisory acquisitions stack and require an equal number of unlocks. MySQL likewise permits a session to acquire the same named lock repeatedly and requires all acquisitions to be released. ([PostgreSQL]1)

This can make a migration report success while leaving the connection permanently blocking every other migrator. A robust implementation needs a dedicated lock-holding session that is never exposed through Context; an existence query on the callback’s own connection cannot establish uninterrupted ownership or the acquisition count. Regression tests should cover recursive acquisition, release/reacquire, and PostgreSQL shared-lock substitution.

3. P1 — aliased Rails version strings can execute the same down migration twice

The version-only history implementation parses each textual version into an i64, but deletion reconstructs the key using version.str(). Because the new history table has a textual primary key, rows such as '1' and '01' can coexist, yet both parse to numeric version 1.

With those rows present, rollback(2) sees two applied migrations with version 1, invokes the same down callback twice, and issues DELETE ... WHERE version = '1' both times. The '01' row remains, so later rollbacks can invoke the callback yet again. This is a destructive failure mode rather than merely a status-display issue. V’s integer parser accepts signed decimal representations and does not enforce a canonical source representation.

applied() should reject noncanonical strings, nonpositive versions, and duplicate parsed versions before any migration callback runs. Preserving the raw version for deletion is also advisable, but duplicate semantic versions should still fail closed.

4. P2 — arbitrary query failures permanently poison history-shape detection

The decoded applied() implementation attempts the legacy three-column query and catches every error as evidence that the table is version-only. It sets:

m.history_shape_resolved = true
m.history_has_metadata = false

before the fallback SELECT version ... has succeeded.

Therefore, a timeout, cancellation, lost connection, permission error, or other transient failure against a legacy table can permanently classify that Migrator instance as version-only. Even when the fallback also fails and applied() returns an error, the incorrect cached state remains. A later retry can then omit name and applied_at during inserts and use the wrong deletion form, while the original database error has been discarded.

History shape should be determined through dialect-specific schema introspection, or at minimum fallback only for a recognized missing-column error and update the cache only after the fallback query succeeds.

The lock and history-shape observations refer to the code contained in the encoded patch: [decoded payload with line numbers](sandbox:/mnt/data/pr28118_decoded_payload_numbered.txt). The exact-target validation, per-migration transaction override, duplicate-name validation, MySQL change_column rejection, required fields, and SQLite foreign-key test otherwise looked internally consistent, but the source-level review should be repeated after the implementation is materialized in the PR tree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant