{"record":{"id":"b016a3260ecdcf7f","repo":"gastownhall/beads","slug":"schema-release-migration-lock-w-w","errorCode":null,"errorMessage":"schema: release migration lock: %w: %w","messagePattern":"schema: release migration lock: %w: %w","errorType":"exception","errorClass":"ErrMigrationLockRelease","httpStatus":null,"severity":"warning","filePath":"internal/storage/schema/lock.go","lineNumber":372,"sourceCode":"\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 {\n\t\tdiscardConn(conn)\n\t\treturn fmt.Errorf(\"schema: release migration lock: %w: %w\", ErrMigrationLockRelease, err)\n\t}\n\tif !released.Valid {\n\t\tdiscardConn(conn)\n\t\treturn fmt.Errorf(\"schema: release migration lock: %w: returned NULL\", ErrMigrationLockRelease)\n\t}\n\tif released.Int64 != 1 {\n\t\tdiscardConn(conn)\n\t\treturn fmt.Errorf(\"schema: release migration lock: %w: returned %d\", ErrMigrationLockRelease, released.Int64)\n\t}\n\treturn nil\n}\n\nfunc discardConn(conn *sql.Conn) {\n\t_ = conn.Raw(func(driverConn any) error {\n\t\treturn driver.ErrBadConn\n\t})\n}\n","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/lock.go#L354-L390","documentation":"ReleaseMigrationLock executes RELEASE_LOCK and the query itself failed (returned a non-nil error); the sentinel ErrMigrationLockRelease is wrapped along with the driver error. The connection is discarded (marked driver.ErrBadConn) so it is not returned to the pool in an unknown state. This signals the lock release could not be confirmed, not necessarily that the lock is still held.","triggerScenarios":"Calling ReleaseMigrationLock when the pinned session has died, the network dropped, the cleanup timeout (migrationLockCleanupTimeout) expired, or the server rejects RELEASE_LOCK for the session.","commonSituations":"Long migrations exceeding the cleanup timeout; MySQL server gone away after an idle period; Dolt server restart mid-migration; firewall/load-balancer closing idle connections.","solutions":["Retry release on a fresh pinned connection to the same session/database if possible.","Rely on session teardown: closing the connection (or process exit) implicitly releases any GET_LOCK held by that session.","Increase migrationLockCleanupTimeout or keep-alive if long migrations routinely exceed it.","Check server logs/connectivity for the underlying driver error wrapped in the message."],"exampleFix":"// before\nif err := schema.ReleaseMigrationLock(conn, lockName); err != nil {\n    log.Fatal(err)\n}\n// after: release failure usually means dead session; close and continue\nif err := schema.ReleaseMigrationLock(conn, lockName); err != nil {\n    conn.Close() // session teardown releases session-scoped locks\n    log.Printf(\"release failed, connection discarded: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := schema.ReleaseMigrationLock(conn, name); err != nil {\n    if errors.Is(err, schema.ErrMigrationLockRelease) {\n        conn.Close() // session teardown implicitly releases session-scoped locks\n    }\n}","preventionTips":["Always release on the same pinned *sql.Conn used to acquire.","Keep the session alive across the migration (avoid long idle timeouts).","Remember closing the connection releases the lock implicitly — release errors are often benign after teardown.","Keep migrations short relative to migrationLockCleanupTimeout."],"tags":["database","locking","connection","dolt"],"backgroundTag":"lock-release-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}