fix(11-07): hand after-commit callbacks a clean statement
- lagoon.Transaction, the lagoon:after_commit flush and the immediate AfterCommit path pass a handle with an empty statement on the write's connection (Session NewDB+Context, Clauses(), Session NewDB) - a WithContext query through the handle no longer continues from the written model's statement (deferred from 11-05) - TestTransactionAfterCommit/callback_handle_has_a_clean_statement covers the implicit, plain-transaction and lagoon.Transaction paths
This commit is contained in:
@@ -70,7 +70,7 @@ func Transaction(ctx context.Context, gdb *gorm.DB, fn func(ctx context.Context,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
runAfterCommit(ctx, gdb.Session(&gorm.Session{NewDB: true, Context: ctx}), buf.take())
|
||||
runAfterCommit(ctx, cleanHandle(gdb, ctx), buf.take())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -80,7 +80,13 @@ func Transaction(ctx context.Context, gdb *gorm.DB, fn func(ctx context.Context,
|
||||
// example from a GORM create callback) it runs after that commit through the
|
||||
// AfterCommitCallback callback, and not at all when the write fails.
|
||||
// Anywhere else, including inside a plain gorm Transaction, fn runs
|
||||
// immediately with db.
|
||||
// immediately on db's connection.
|
||||
//
|
||||
// The handle fn receives always has an empty statement on the connection
|
||||
// the work belongs to (the pool after a commit, the open transaction
|
||||
// inside a plain gorm Transaction), whatever handle AfterCommit was called
|
||||
// with. Queries through it never continue from the written model's
|
||||
// statement, even when they start with WithContext.
|
||||
func AfterCommit(ctx context.Context, db *gorm.DB, fn func(ctx context.Context, db *gorm.DB)) {
|
||||
if fn == nil {
|
||||
return
|
||||
@@ -108,7 +114,7 @@ func AfterCommit(ctx context.Context, db *gorm.DB, fn func(ctx context.Context,
|
||||
return
|
||||
}
|
||||
}
|
||||
runAfterCommit(ctx, db, []func(context.Context, *gorm.DB){fn})
|
||||
runAfterCommit(ctx, cleanHandle(db, ctx), []func(context.Context, *gorm.DB){fn})
|
||||
}
|
||||
|
||||
func bufferFrom(ctx context.Context, db *gorm.DB) *afterCommitBuffer {
|
||||
@@ -143,7 +149,21 @@ func flushStatementAfterCommit(db *gorm.DB) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
runAfterCommit(ctx, db.Session(&gorm.Session{NewDB: true, Context: ctx}), fns)
|
||||
runAfterCommit(ctx, cleanHandle(db, ctx), fns)
|
||||
}
|
||||
|
||||
// cleanHandle returns a handle on db's connection with an empty statement.
|
||||
// db.Session with NewDB and a Context is not enough on a callback's
|
||||
// handle: the Context makes it clone the write's statement (model, table,
|
||||
// clauses), and a later WithContext on the result continues from that
|
||||
// clone, so a query would run against the written model's table. Clauses()
|
||||
// starts a fresh statement on the same connection, and the final Session
|
||||
// makes the next chained call start from it again. A nil db stays nil.
|
||||
func cleanHandle(db *gorm.DB, ctx context.Context) *gorm.DB {
|
||||
if db == nil {
|
||||
return nil
|
||||
}
|
||||
return db.Session(&gorm.Session{NewDB: true, Context: ctx}).Clauses().Session(&gorm.Session{NewDB: true})
|
||||
}
|
||||
|
||||
func runAfterCommit(ctx context.Context, db *gorm.DB, fns []func(context.Context, *gorm.DB)) {
|
||||
|
||||
Reference in New Issue
Block a user