{"record":{"id":"82a92f6ca7c20727","repo":"gastownhall/beads","slug":"schema-acquire-migration-lock-w-w","errorCode":null,"errorMessage":"schema: acquire migration lock: %w: %w","messagePattern":"schema: acquire migration lock: %w: %w","errorType":"exception","errorClass":"ErrMigrationLockUnavailable","httpStatus":null,"severity":"warning","filePath":"internal/storage/schema/lock.go","lineNumber":352,"sourceCode":"\tvar ancestorCount int\n\tif err := conn.QueryRowContext(ctx,\n\t\t\"SELECT COUNT(*) FROM dolt_log WHERE commit_hash = ?\", c.initialHead,\n\t).Scan(&ancestorCount); err != nil {\n\t\treturn false\n\t}\n\tif ancestorCount != 1 {\n\t\treturn false\n\t}\n\n\treturn c.consumed.CompareAndSwap(false, true)\n}\n\n// AcquireMigrationLock acquires the named schema migration lock on the pinned\n// connection's current Dolt/MySQL session.\nfunc AcquireMigrationLock(ctx context.Context, conn *sql.Conn, lockName string) error {\n\tvar locked sql.NullInt64\n\tif err := conn.QueryRowContext(ctx, \"SELECT GET_LOCK(?, ?)\", lockName, migrationLockAcquireTimeoutSeconds).Scan(&locked); err != nil {\n\t\treturn fmt.Errorf(\"schema: acquire migration lock: %w: %w\", ErrMigrationLockUnavailable, err)\n\t}\n\tif !locked.Valid {\n\t\treturn fmt.Errorf(\"schema: acquire migration lock: %w: returned NULL\", ErrMigrationLockUnavailable)\n\t}\n\tif locked.Int64 != 1 {\n\t\treturn fmt.Errorf(\"schema: acquire migration lock: %w: timeout\", ErrMigrationLockUnavailable)\n\t}\n\treturn nil\n}\n\n// ReleaseMigrationLock releases the named schema migration lock from the same\n// pinned Dolt/MySQL session used to acquire it.\nfunc ReleaseMigrationLock(conn *sql.Conn, lockName string) error {\n\tcleanupCtx, cancel := context.WithTimeout(context.Background(), migrationLockCleanupTimeout)\n\tdefer cancel()\n\n\tvar released sql.NullInt64\n\tif err := conn.QueryRowContext(cleanupCtx, \"SELECT RELEASE_LOCK(?)\", lockName).Scan(&released); err != nil {","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/lock.go#L334-L370","documentation":"GET_LOCK itself errored at the driver level while acquiring the database-scoped schema migration lock. The error wraps both the sentinel ErrMigrationLockUnavailable (making it retryable via IsMigrationLockError) and the underlying driver error.","triggerScenarios":"conn.QueryRowContext(\"SELECT GET_LOCK(?, ?)\") fails: connection broken/closed, context cancelled or deadline exceeded during the up-to-5s lock wait, server restart.","commonSituations":"Heavily contended shared Dolt server (many bd processes); idle connection reaped by the server; caller's ctx deadline shorter than the 5-second lock timeout.","solutions":["Treat as retryable: check schema.IsMigrationLockError(err) and retry with backoff within your retry budget.","Ensure the caller's context outlives the 5-second lock acquire timeout.","Verify the pinned connection is alive (ping) before attempting migration.","Reduce contention: fewer concurrent initializers, or stagger bd process startup on shared rigs."],"exampleFix":"// before\napplied, err := schema.MigrateUpWithLock(ctx, conn, db) // fails on flaky conn\n// after\nif err := conn.PingContext(ctx); err != nil {\n    conn, err = pool.Conn(ctx)\n}\napplied, err := schema.MigrateUpWithLock(ctx, conn, db)\nif schema.IsMigrationLockError(err) { /* retry with backoff */ }","handlingStrategy":"retry","validationCode":"if err := conn.PingContext(ctx); err != nil { return err }\nctx, cancel := context.WithTimeout(ctx, 10*time.Second) // must exceed the 5s lock timeout\ndefer cancel()","typeGuard":"func isLockUnavailable(err error) bool { return errors.Is(err, schema.ErrMigrationLockUnavailable) }","tryCatchPattern":"applied, err := schema.MigrateUpWithLock(ctx, conn, db)\nif schema.IsMigrationLockError(err) {\n    time.Sleep(backoff)\n    return retryOpen(ctx) // retry with exponential backoff, within retry budget\n}","preventionTips":["Give the context a deadline comfortably longer than the 5-second lock wait.","Ping or refresh the pinned connection before migration.","Use schema.IsMigrationLockError to gate retries.","Reduce concurrent initializers on shared Dolt servers."],"tags":["go","database","dolt","locking","retryable"],"backgroundTag":"migration-lock-unavailable","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}