github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/table/transaction.go (about) 1 package table 2 3 import ( 4 "context" 5 "fmt" 6 "sync/atomic" 7 8 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Table" 9 10 "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator" 11 "github.com/ydb-platform/ydb-go-sdk/v3/internal/operation" 12 "github.com/ydb-platform/ydb-go-sdk/v3/internal/params" 13 "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack" 14 "github.com/ydb-platform/ydb-go-sdk/v3/internal/table/scanner" 15 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 16 "github.com/ydb-platform/ydb-go-sdk/v3/table" 17 "github.com/ydb-platform/ydb-go-sdk/v3/table/options" 18 "github.com/ydb-platform/ydb-go-sdk/v3/table/result" 19 "github.com/ydb-platform/ydb-go-sdk/v3/trace" 20 ) 21 22 var ( 23 errTxAlreadyCommitted = xerrors.Wrap(fmt.Errorf("transaction already committed")) 24 errTxRollbackedEarly = xerrors.Wrap(fmt.Errorf("transaction rollbacked early")) 25 ) 26 27 type txState struct { 28 rawVal atomic.Uint32 29 } 30 31 func (s *txState) Load() txStateEnum { 32 return txStateEnum(s.rawVal.Load()) 33 } 34 35 func (s *txState) Store(val txStateEnum) { 36 s.rawVal.Store(uint32(val)) 37 } 38 39 type txStateEnum uint32 40 41 const ( 42 txStateInitialized txStateEnum = iota 43 txStateCommitted 44 txStateRollbacked 45 ) 46 47 type transaction struct { 48 id string 49 s *session 50 control *table.TransactionControl 51 state txState 52 } 53 54 func (tx *transaction) ID() string { 55 return tx.id 56 } 57 58 // Execute executes query represented by text within transaction tx. 59 func (tx *transaction) Execute( 60 ctx context.Context, 61 query string, parameters *params.Parameters, 62 opts ...options.ExecuteDataQueryOption, 63 ) (r result.Result, err error) { 64 onDone := trace.TableOnSessionTransactionExecute( 65 tx.s.config.Trace(), &ctx, 66 stack.FunctionID(""), 67 tx.s, tx, queryFromText(query), parameters, 68 ) 69 defer func() { 70 onDone(r, err) 71 }() 72 73 switch tx.state.Load() { 74 case txStateCommitted: 75 return nil, xerrors.WithStackTrace(errTxAlreadyCommitted) 76 case txStateRollbacked: 77 return nil, xerrors.WithStackTrace(errTxRollbackedEarly) 78 default: 79 _, r, err = tx.s.Execute(ctx, tx.control, query, parameters, opts...) 80 if err != nil { 81 return nil, xerrors.WithStackTrace(err) 82 } 83 84 if tx.control.Desc().GetCommitTx() { 85 tx.state.Store(txStateCommitted) 86 } 87 88 return r, nil 89 } 90 } 91 92 // ExecuteStatement executes prepared statement stmt within transaction tx. 93 func (tx *transaction) ExecuteStatement( 94 ctx context.Context, 95 stmt table.Statement, parameters *params.Parameters, 96 opts ...options.ExecuteDataQueryOption, 97 ) (r result.Result, err error) { 98 a := allocator.New() 99 defer a.Free() 100 101 onDone := trace.TableOnSessionTransactionExecuteStatement( 102 tx.s.config.Trace(), &ctx, 103 stack.FunctionID(""), 104 tx.s, tx, stmt.(*statement).query, parameters, 105 ) 106 defer func() { 107 onDone(r, err) 108 }() 109 110 switch tx.state.Load() { 111 case txStateCommitted: 112 return nil, xerrors.WithStackTrace(errTxAlreadyCommitted) 113 case txStateRollbacked: 114 return nil, xerrors.WithStackTrace(errTxRollbackedEarly) 115 default: 116 _, r, err = stmt.Execute(ctx, tx.control, parameters, opts...) 117 if err != nil { 118 return nil, xerrors.WithStackTrace(err) 119 } 120 121 if tx.control.Desc().GetCommitTx() { 122 tx.state.Store(txStateCommitted) 123 } 124 125 return r, nil 126 } 127 } 128 129 // CommitTx commits specified active transaction. 130 func (tx *transaction) CommitTx( 131 ctx context.Context, 132 opts ...options.CommitTransactionOption, 133 ) (r result.Result, err error) { 134 onDone := trace.TableOnSessionTransactionCommit( 135 tx.s.config.Trace(), &ctx, 136 stack.FunctionID(""), 137 tx.s, tx, 138 ) 139 defer func() { 140 onDone(err) 141 }() 142 143 switch tx.state.Load() { 144 case txStateCommitted: 145 return nil, xerrors.WithStackTrace(errTxAlreadyCommitted) 146 case txStateRollbacked: 147 return nil, xerrors.WithStackTrace(errTxRollbackedEarly) 148 default: 149 var ( 150 request = &Ydb_Table.CommitTransactionRequest{ 151 SessionId: tx.s.id, 152 TxId: tx.id, 153 OperationParams: operation.Params( 154 ctx, 155 tx.s.config.OperationTimeout(), 156 tx.s.config.OperationCancelAfter(), 157 operation.ModeSync, 158 ), 159 } 160 response *Ydb_Table.CommitTransactionResponse 161 result = new(Ydb_Table.CommitTransactionResult) 162 ) 163 164 for _, opt := range opts { 165 if opt != nil { 166 opt((*options.CommitTransactionDesc)(request)) 167 } 168 } 169 170 response, err = tx.s.tableService.CommitTransaction(ctx, request) 171 if err != nil { 172 return nil, xerrors.WithStackTrace(err) 173 } 174 175 err = response.GetOperation().GetResult().UnmarshalTo(result) 176 if err != nil { 177 return nil, xerrors.WithStackTrace(err) 178 } 179 180 tx.state.Store(txStateCommitted) 181 182 return scanner.NewUnary( 183 nil, 184 result.GetQueryStats(), 185 scanner.WithIgnoreTruncated(tx.s.config.IgnoreTruncated()), 186 ), nil 187 } 188 } 189 190 // Rollback performs a rollback of the specified active transaction. 191 func (tx *transaction) Rollback(ctx context.Context) (err error) { 192 onDone := trace.TableOnSessionTransactionRollback( 193 tx.s.config.Trace(), &ctx, 194 stack.FunctionID(""), 195 tx.s, tx, 196 ) 197 defer func() { 198 onDone(err) 199 }() 200 201 switch tx.state.Load() { 202 case txStateCommitted: 203 return nil // nop for committed tx 204 case txStateRollbacked: 205 return xerrors.WithStackTrace(errTxRollbackedEarly) 206 default: 207 _, err = tx.s.tableService.RollbackTransaction(ctx, 208 &Ydb_Table.RollbackTransactionRequest{ 209 SessionId: tx.s.id, 210 TxId: tx.id, 211 OperationParams: operation.Params( 212 ctx, 213 tx.s.config.OperationTimeout(), 214 tx.s.config.OperationCancelAfter(), 215 operation.ModeSync, 216 ), 217 }, 218 ) 219 if err != nil { 220 return xerrors.WithStackTrace(err) 221 } 222 223 tx.state.Store(txStateRollbacked) 224 225 return nil 226 } 227 }