Skip to content

MySQL migration lock is skipped by void db reset and by Better Auth migrations #415

Closed

Description

@Brooooooklyn

Problem

PR #413 adds one lock per MySQL database for migrations. CLI runs hold GET_LOCK. Deploys to the Void platform write a lease row in _void_migration_lock. Two paths still change the database outside that lock:

void db reset / void db seed (MySQL)
  drop every base table          <- no lock; drops _void_migration_lock and _void_migrations too
  applyMySqlMigrations           <- takes the lock

hosted migration handler (void deploy)
  runMySqlMigrations             <- lease held
  lease.release()
  runAuthMigrations              <- no lease

vp dev (migration plugin)
  runMySqlMigrations             <- GET_LOCK, released when it returns
  runLocalVoidAuthMigrations     <- no lock

What can go wrong:

  • Reset during a deploy. void db reset drops the lease table while a deploy holds a lease. The deploy's next renew fails ("expired before this run finished"), or its next statement fails on a missing table. Reset then applies the files again under its own lock, so both runs can apply the same file.
  • Better Auth migrations at the same time. They read information_schema, then CREATE TABLE IF NOT EXISTS or ALTER TABLE ... ADD COLUMN. MySQL has no ADD COLUMN IF NOT EXISTS, so two runs at once can fail with "Duplicate column name".

void db reset asks for confirmation on a non-local host. The risk is highest when a shared database is reset during a deploy.

Repro

From the code (not run):

  1. Start a void deploy whose MySQL migrations take a while, so it holds the lease.
  2. Run void db reset --yes against the same database.
  3. Reset drops _void_migration_lock. The deploy loses its lease.

For Better Auth: run two deploys of different projects on one database, both with Better Auth and a new auth column.

Cause

  • packages/void/src/cli/db.ts:599-611 drops every table with no lock. The lock is taken only later, in applyMySqlMigrations (:616). void db seed calls the same function (:647).
  • packages/void/src/runtime/migration-handler-mysql.ts:25-28 runs runAuthMigrations() after runMySqlMigrations returned. That function released the lease in its finally (:113).
  • packages/void/src/plugin-migration.ts:75 runs the SQL migrations (lock released on return), then :108-111 runs the auth migrations.
  • packages/void/src/runtime/auth-migrations.ts:346 onward reads the tables, then creates or alters them; :198-199 adds no IF NOT EXISTS for MySQL.

Suggested fix

  • Reset: take the same lock (GET_LOCK plus the lease check) before it drops tables, and keep it until the migrations are applied.
  • Run Better Auth migrations before the lock or lease is released, in both runners.

Evidence

🤖 Generated with Claude Code

https://claude.ai/code/session_0123r5HnanmW2VUrZ7VWDufH

Activity

  1. added 2 commits that reference this issue on Oct 14, 2026
    8d66ca9
    84c8140
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions