fix(11-08): refuse unmanaged after-commit work
This commit is contained in:
@@ -52,6 +52,9 @@ func Transaction(ctx context.Context, gdb *gorm.DB, fn func(ctx context.Context,
|
||||
ctx = context.Background()
|
||||
}
|
||||
if parent, ok := ctx.Value(afterCommitKey{}).(*afterCommitBuffer); ok && parent != nil {
|
||||
if !transactionalHandle(gdb) {
|
||||
return fmt.Errorf("lagoon: nested transaction requires the parent transaction handle")
|
||||
}
|
||||
child := &afterCommitBuffer{}
|
||||
childCtx := context.WithValue(ctx, afterCommitKey{}, child)
|
||||
err := gdb.WithContext(childCtx).Transaction(func(tx *gorm.DB) error {
|
||||
@@ -79,8 +82,9 @@ func Transaction(ctx context.Context, gdb *gorm.DB, fn func(ctx context.Context,
|
||||
// single-statement write for which GORM opened its own transaction (for
|
||||
// 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 on db's connection.
|
||||
// Inside a foreign plain GORM transaction it logs a warning and refuses to
|
||||
// run, because Lagoon cannot know whether that transaction will commit.
|
||||
// Anywhere else, fn runs 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
|
||||
@@ -113,10 +117,22 @@ func AfterCommit(ctx context.Context, db *gorm.DB, fn func(ctx context.Context,
|
||||
db.InstanceSet(statementBufferKey, buf)
|
||||
return
|
||||
}
|
||||
if transactionalHandle(db) {
|
||||
slog.Default().Warn("lagoon: after-commit callback skipped inside unmanaged transaction")
|
||||
return
|
||||
}
|
||||
}
|
||||
runAfterCommit(ctx, cleanHandle(db, ctx), []func(context.Context, *gorm.DB){fn})
|
||||
}
|
||||
|
||||
func transactionalHandle(db *gorm.DB) bool {
|
||||
if db == nil || db.Statement == nil || db.Statement.ConnPool == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := db.Statement.ConnPool.(gorm.TxCommitter)
|
||||
return ok
|
||||
}
|
||||
|
||||
func bufferFrom(ctx context.Context, db *gorm.DB) *afterCommitBuffer {
|
||||
if buf, ok := ctx.Value(afterCommitKey{}).(*afterCommitBuffer); ok && buf != nil {
|
||||
return buf
|
||||
|
||||
Reference in New Issue
Block a user