go-transaction-manager
114 строк · 2.4 Кб
1package manager
2
3import (
4"context"
5"errors"
6"fmt"
7
8"go.uber.org/multierr"
9
10"github.com/avito-tech/go-transaction-manager/trm/v2"
11)
12
13// Closer closes trm.Transaction.
14type Closer func(context.Context, interface{}, *error) error
15
16type trCloser struct {
17tr trm.Transaction
18cancel context.CancelFunc
19log logger
20}
21
22func newTxCommit(tr trm.Transaction, l logger, c context.CancelFunc) Closer {
23return (&trCloser{
24tr: tr,
25cancel: c,
26log: l,
27}).close
28}
29
30//nolint:funlen
31func (c *trCloser) close(ctx context.Context, p interface{}, errInProcessTr *error) error {
32defer c.cancel()
33
34// recovering from panic
35if p != nil {
36if c.tr.IsActive() {
37if err := c.tr.Rollback(ctx); err != nil {
38c.log.Warning(ctx, fmt.Sprintf("%v, %v", err, p))
39}
40}
41
42panic(p)
43}
44
45hasError := *errInProcessTr != nil
46isErrSkippable := hasError && trm.IsSkippable(*errInProcessTr)
47// TODO not sure that context errors should be propagated.
48isCtxCanceled := errors.Is(*errInProcessTr, context.Canceled)
49isCtxDeadlineExceeded := errors.Is(*errInProcessTr, context.DeadlineExceeded)
50isCtxErr := isCtxCanceled || isCtxDeadlineExceeded
51
52ctxErr := ctx.Err()
53
54if ctxErr != nil {
55if !hasError {
56*errInProcessTr = ctxErr
57} else if !isCtxCanceled && errors.Is(ctxErr, context.Canceled) ||
58!isCtxDeadlineExceeded && errors.Is(ctxErr, context.DeadlineExceeded) {
59*errInProcessTr = multierr.Combine(*errInProcessTr, ctxErr)
60}
61
62isCtxErr = true
63hasError = true
64}
65
66if !c.tr.IsActive() {
67if hasError {
68if isCtxErr || errors.Is(*errInProcessTr, trm.ErrAlreadyClosed) {
69return *errInProcessTr
70}
71
72return multierr.Combine(*errInProcessTr, trm.ErrAlreadyClosed)
73}
74
75return trm.ErrAlreadyClosed
76}
77
78if hasError && !isErrSkippable {
79if errRollback := c.tr.Rollback(ctx); errRollback != nil {
80return multierr.Combine(*errInProcessTr, trm.ErrRollback, errRollback)
81}
82
83return *errInProcessTr
84}
85
86if err := c.tr.Commit(ctx); err != nil {
87var errUnSkipped error
88if isErrSkippable {
89errUnSkipped = trm.UnSkippable(*errInProcessTr)
90}
91
92return multierr.Combine(trm.ErrCommit, err, errUnSkipped)
93} else if isErrSkippable {
94return *errInProcessTr
95}
96
97return nil
98}
99
100func newNilClose(cancel context.CancelFunc) Closer {
101return func(_ context.Context, p interface{}, err *error) error {
102defer cancel()
103
104if p != nil {
105panic(p)
106}
107
108if *err != nil {
109return *err
110}
111
112return nil
113}
114}
115