-
Notifications
You must be signed in to change notification settings - Fork 326
Transaction troubles in v3 with Postgres #274
Comments
Thanks for the detailed write-up. This is a problem. Do you think an |
While it would probably work most of the time, I don't think we can guarantee that the I believe the only way to ensure that multiple commands are executed on the same connection is to use the What if this was a driver option that could be passed via the type Config struct {
MigrationsTable string
DatabaseName string
UseTransactions bool
} Then the Postgres driver would call I guess this could lead to migrations being run without transactions, if a user forgets to provide the option in the connection string when calling the CLI. Maybe that's acceptable? |
This is a tough one. #13 explains why no transaction (sql.Tx) is enforced by the driver.
I don't think this is doable. People use migrate as lib, too. Meaning no user interaction. Really unsure what the best approach is. Happy to hear more feedback and more thoughts. |
I looked through the func (p *Postgres) Run(migration io.Reader) error {
migr, err := ioutil.ReadAll(migration)
if err != nil {
return err
}
// get connection
ctx := context.Background()
c, err := p.db.Conn(ctx)
if err != nil {
return err
}
defer c.Close()
// run migration
query := string(migr[:])
if _, err := c.ExecContext(ctx, query); err != nil {
// attempt to rollback the current transaction, if any
// this generates a warning if no transaction is active, but has no effect otherwise
// TODO: handle or report a rollback failure
c.ExecContext(ctx, "ROLLBACK")
// TODO: cast to postgress error and get line number
return database.Error{OrigErr: err, Err: "migration failed", Query: migr}
}
return nil
} I think this could also work with your idea of an |
Go 1.9 might have solved it for us: The new DB.Conn method returns the new Conn type representing an exclusive connection to the database from the connection pool. All queries run on a Conn will use the same underlying connection until Conn.Close is called to return the connection to the connection pool. |
Ah, I didn't realize this was a new Go 1.9 feature when I posted my last comment. What's the policy for this library on using new language features? Would that require a major version bump? |
I catch the same issue in my tests. But when I used separate connection for each migrate and then close them I have stable work
|
In #252, the reasoning for removing automatic transactions in v3 is explained. The suggestion is that users should wrap multiple statements into transactions as necessary, but I'm not sure it's possible to do this correctly, at least with Postgres as currently implemented.
Consider a migration file like this:
This works fine, provided all the statements succeed. If any statement fails, Postgres does not automatically rollback the current transaction. As a user, I can't issue the
ROLLBACK
command because execution of my statements stops as soon as the error occurs.Then the problem gets worse because Postgres ignores commands issued in a transaction after a failure. When the connection that started the transaction is returned to the pool, there's a good chance it will be reused to release the lock... which will not happen because the command is ignored, e.g.:
Now the migration table is locked and there's an invalid connection in the connection pool until the process is terminated. This is probably fine if running from the command line, but could be annoying if running migrations automatically in a server or other process using the library interface.
I suspect this could also prevent the migration from being marked as
dirty
after the error.As far as I can tell, there are a few options to get around this:
Is there a way to correctly use transactions with the current Postgres implementation? Does this problem affect any other database backends? Maybe the decision on whether to wrap migrations in transactions should be left to the individual drivers?
The text was updated successfully, but these errors were encountered: