diff options
author | Jason Song <i@wolfogre.com> | 2023-01-10 01:19:19 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-09 12:19:19 -0500 |
commit | a35714372d9e6be2cf92ce27ef8b05b37f8cb283 (patch) | |
tree | da86138c14e428ec238d20ff2c76b7402657c3f6 /models/db/context.go | |
parent | 99a675f4a1fc32339e9b93ea33207322c3e8bef1 (diff) | |
download | gitea-a35714372d9e6be2cf92ce27ef8b05b37f8cb283.tar.gz gitea-a35714372d9e6be2cf92ce27ef8b05b37f8cb283.zip |
Fix halfCommitter and WithTx (#22366)
Related to #22362.
I overlooked that there's always `committer.Close()`, like:
```go
ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil {
return nil
}
defer committer.Close()
// ...
if err != nil {
return nil
}
// ...
return committer.Commit()
```
So the `Close` of `halfCommitter` should ignore `commit and close`, it's
not a rollback.
See: [Why `halfCommitter` and `WithTx` should rollback IMMEDIATELY or
commit
LATER](https://github.com/go-gitea/gitea/pull/22366#issuecomment-1374778612).
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'models/db/context.go')
-rw-r--r-- | models/db/context.go | 27 |
1 files changed, 22 insertions, 5 deletions
diff --git a/models/db/context.go b/models/db/context.go index 455f3d1c5d..911dbd1c6f 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -98,19 +98,31 @@ type Committer interface { // halfCommitter is a wrapper of Committer. // It can be closed early, but can't be committed early, it is useful for reusing a transaction. type halfCommitter struct { - Committer + committer Committer + committed bool } -func (*halfCommitter) Commit() error { - // do nothing +func (c *halfCommitter) Commit() error { + c.committed = true + // should do nothing, and the parent committer will commit later return nil } +func (c *halfCommitter) Close() error { + if c.committed { + // it's "commit and close", should do nothing, and the parent committer will commit later + return nil + } + + // it's "rollback and close", let the parent committer rollback right now + return c.committer.Close() +} + // TxContext represents a transaction Context, // it will reuse the existing transaction in the parent context or create a new one. func TxContext(parentCtx context.Context) (*Context, Committer, error) { if sess, ok := inTransaction(parentCtx); ok { - return newContext(parentCtx, sess, true), &halfCommitter{Committer: sess}, nil + return newContext(parentCtx, sess, true), &halfCommitter{committer: sess}, nil } sess := x.NewSession() @@ -126,7 +138,12 @@ func TxContext(parentCtx context.Context) (*Context, Committer, error) { // this function will reuse it otherwise will create a new one and close it when finished. func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error { if sess, ok := inTransaction(parentCtx); ok { - return f(newContext(parentCtx, sess, true)) + err := f(newContext(parentCtx, sess, true)) + if err != nil { + // rollback immediately, in case the caller ignores returned error and tries to commit the transaction. + _ = sess.Close() + } + return err } return txWithNoCheck(parentCtx, f) } |