Can't acquire lock on "manual" migrations #297
Comments
Might be related to #274 . How do you run the tests? |
Migrations are basic create / drop table without transactions. Complete test code: func TestMigrate(t *testing.T) {
migrationsDir, cleanup := setupTestMigrations() // creates few up-down *.sql pairs
defer cleanup()
var err error
db, err = sql.Open("mysql", "root:@tcp(localhost:3306)/migrations_test?multiStatements=true")
if err != nil || db == nil {
t.Fatalf("failed to open database connection: %s", err)
}
dbdrv, err := mysql.WithInstance(db, &mysql.Config{})
if err != nil {
t.Fatalf("mysq.WithInstance error: %s", err)
}
fsrc, err := (&file.File{}).Open("file://" + migrationsDir)
if err != nil {
t.Fatalf("Failed to open migrations source: %s", err)
}
mg, err := migrate.NewWithInstance(
"file",
fsrc,
"mysql",
dbdrv,
)
if err != nil {
t.Fatalf("Failed to initialize Migrate: %s", err)
}
t.Log("mg.Version():")
t.Log(mg.Version())
err = mg.Migrate(1)
if err != nil {
t.Errorf("Migrate(1) err: %s", err)
}
t.Log("mg.Version():")
t.Log(mg.Version())
// up
err = mg.Migrate(2)
if err != nil {
t.Errorf("Migrate(2): %s", err)
}
t.Log("mg.Version():")
t.Log(mg.Version())
err = mg.Down()
if err != nil {
t.Errorf("Down err: %s", err)
}
} |
I believe this closes mattes#297 as well. I have been working on adding testing of migrations and it requires acquiring the lock in mysql multiple times to go up and down. After nailing this down to GET_LOCK returning a failure for every subsequent GET_LOCK call after the first, I decided it was time to rtfm and lo and behold: https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_release-lock RELEASE_LOCK will not work if called from a different thread than GET_LOCK. The simplest solution using the golang database/sql pkg appears to be to just get a single conn to use for every operation. since migrations are happening sequentially, I don't think this will be a performance hit (possible improvement). now calling Lock() and Unlock() multiple times will work; prior to this patch, every call to RELEASE_LOCK would fail. this required minimal changes to use the *sql.Conn methods instead of the *sql.DB methods. other changes: * upped time out to 10s on GET_LOCK, 1s timeout can too easily leave us in a state where we think we have the lock but it has timed out (during the operation). * fixes SetVersion which was not using the tx it was intending to, and fixed a bug where the transaction could have been left open since Rollback or Commit may never have been called. I added a test but it does not seem to come up in the previous patch, at least easily, I tried some shenanigans. At least, this behavior should be fixed with this patch and hopefully the test / comment is advisory enough. thank you for maintaining this library, it has been relatively easy to integrate with and most stuff works (pg works great :)
Any help with this? I am getting Killing the current app instance first and then running migrations on the new instance works fine. Possible workarounds? |
See my issue #296 the postgres driver is unable to unlock properly. What we have done is setup a different db conn just to migrate and close it after migration. Closing the connection releases the lock. |
Hopefully fixes "can't acquire lock" error when trying to migrate. See mattes/migrate#297 (comment)
I believe this closes mattes#297 as well. I have been working on adding testing of migrations and it requires acquiring the lock in mysql multiple times to go up and down. After nailing this down to GET_LOCK returning a failure for every subsequent GET_LOCK call after the first, I decided it was time to rtfm and lo and behold: https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_release-lock RELEASE_LOCK will not work if called from a different thread than GET_LOCK. The simplest solution using the golang database/sql pkg appears to be to just get a single conn to use for every operation. since migrations are happening sequentially, I don't think this will be a performance hit (possible improvement). now calling Lock() and Unlock() multiple times will work; prior to this patch, every call to RELEASE_LOCK would fail. this required minimal changes to use the *sql.Conn methods instead of the *sql.DB methods. other changes: * upped time out to 10s on GET_LOCK, 1s timeout can too easily leave us in a state where we think we have the lock but it has timed out (during the operation). * fixes SetVersion which was not using the tx it was intending to, and fixed a bug where the transaction could have been left open since Rollback or Commit may never have been called. I added a test but it does not seem to come up in the previous patch, at least easily, I tried some shenanigans. At least, this behavior should be fixed with this patch and hopefully the test / comment is advisory enough. thank you for maintaining this library, it has been relatively easy to integrate with and most stuff works (pg works great :)
I switched to https://github.com/db-journey/migrate (which is hard fork of this repo), primarily because it supported timestamp-based migrations versioning, where you don't need to ensure that your migration version is higher than current db schema version. But it evolved into major contribution, where I refactored code, removed all redundant complication with channels, simplified Driver interface and added few new features, including database locking during migrations. Now it's much more contribution-friendly, take a look @mattes |
I believe this closes mattes#297 as well. I have been working on adding testing of migrations and it requires acquiring the lock in mysql multiple times to go up and down. After nailing this down to GET_LOCK returning a failure for every subsequent GET_LOCK call after the first, I decided it was time to rtfm and lo and behold: https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_release-lock RELEASE_LOCK will not work if called from a different thread than GET_LOCK. The simplest solution using the golang database/sql pkg appears to be to just get a single conn to use for every operation. since migrations are happening sequentially, I don't think this will be a performance hit (possible improvement). now calling Lock() and Unlock() multiple times will work; prior to this patch, every call to RELEASE_LOCK would fail. this required minimal changes to use the *sql.Conn methods instead of the *sql.DB methods. other changes: * upped time out to 10s on GET_LOCK, 1s timeout can too easily leave us in a state where we think we have the lock but it has timed out (during the operation). * fixes SetVersion which was not using the tx it was intending to, and fixed a bug where the transaction could have been left open since Rollback or Commit may never have been called. I added a test but it does not seem to come up in the previous patch, at least easily, I tried some shenanigans. At least, this behavior should be fixed with this patch and hopefully the test / comment is advisory enough. thank you for maintaining this library, it has been relatively easy to integrate with and most stuff works (pg works great :)
Getting |
Using MySQL
This code (cut from test):
Results in following output:
May be related to #220
And is not direclty related to #235 for sure (I do not run migrations concurrently, and in my case it is 100% reproduceable)
The text was updated successfully, but these errors were encountered: