github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/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/tx" 16 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 17 "github.com/ydb-platform/ydb-go-sdk/v3/table" 18 "github.com/ydb-platform/ydb-go-sdk/v3/table/options" 19 "github.com/ydb-platform/ydb-go-sdk/v3/table/result" 20 "github.com/ydb-platform/ydb-go-sdk/v3/trace" 21 ) 22 23 var ( 24 errTxAlreadyCommitted = xerrors.Wrap(fmt.Errorf("transaction already committed")) 25 errTxRollbackedEarly = xerrors.Wrap(fmt.Errorf("transaction rollbacked early")) 26 ) 27 28 type txState struct { 29 rawVal atomic.Uint32 30 } 31 32 func (s *txState) Load() txStateEnum { 33 return txStateEnum(s.rawVal.Load()) 34 } 35 36 func (s *txState) Store(val txStateEnum) { 37 s.rawVal.Store(uint32(val)) 38 } 39 40 type txStateEnum uint32 41 42 const ( 43 txStateInitialized txStateEnum = iota 44 txStateCommitted 45 txStateRollbacked 46 ) 47 48 var _ tx.Identifier = (*transaction)(nil) 49 50 type transaction struct { 51 tx.Identifier 52 53 s *session 54 control *table.TransactionControl 55 state txState 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.TableOnTxExecute( 65 tx.s.config.Trace(), &ctx, 66 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*transaction).Execute"), 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 val, ok := stmt.(*statement) 102 if !ok { 103 panic(fmt.Sprintf("unsupported type conversion from %T to *statement", val)) 104 } 105 106 onDone := trace.TableOnTxExecuteStatement( 107 tx.s.config.Trace(), &ctx, 108 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*transaction).ExecuteStatement"), 109 tx.s, tx, val.query, parameters, 110 ) 111 defer func() { 112 onDone(r, err) 113 }() 114 115 switch tx.state.Load() { 116 case txStateCommitted: 117 return nil, xerrors.WithStackTrace(errTxAlreadyCommitted) 118 case txStateRollbacked: 119 return nil, xerrors.WithStackTrace(errTxRollbackedEarly) 120 default: 121 _, r, err = stmt.Execute(ctx, tx.control, parameters, opts...) 122 if err != nil { 123 return nil, xerrors.WithStackTrace(err) 124 } 125 126 if tx.control.Desc().GetCommitTx() { 127 tx.state.Store(txStateCommitted) 128 } 129 130 return r, nil 131 } 132 } 133 134 // CommitTx commits specified active transaction. 135 func (tx *transaction) CommitTx( 136 ctx context.Context, 137 opts ...options.CommitTransactionOption, 138 ) (r result.Result, err error) { 139 onDone := trace.TableOnTxCommit( 140 tx.s.config.Trace(), &ctx, 141 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*transaction).CommitTx"), 142 tx.s, tx, 143 ) 144 defer func() { 145 onDone(err) 146 }() 147 148 switch tx.state.Load() { 149 case txStateCommitted: 150 return nil, xerrors.WithStackTrace(errTxAlreadyCommitted) 151 case txStateRollbacked: 152 return nil, xerrors.WithStackTrace(errTxRollbackedEarly) 153 default: 154 var ( 155 request = &Ydb_Table.CommitTransactionRequest{ 156 SessionId: tx.s.id, 157 TxId: tx.ID(), 158 OperationParams: operation.Params( 159 ctx, 160 tx.s.config.OperationTimeout(), 161 tx.s.config.OperationCancelAfter(), 162 operation.ModeSync, 163 ), 164 } 165 response *Ydb_Table.CommitTransactionResponse 166 result = new(Ydb_Table.CommitTransactionResult) 167 ) 168 169 for _, opt := range opts { 170 if opt != nil { 171 opt((*options.CommitTransactionDesc)(request)) 172 } 173 } 174 175 response, err = tx.s.tableService.CommitTransaction(ctx, request) 176 if err != nil { 177 return nil, xerrors.WithStackTrace(err) 178 } 179 180 err = response.GetOperation().GetResult().UnmarshalTo(result) 181 if err != nil { 182 return nil, xerrors.WithStackTrace(err) 183 } 184 185 tx.state.Store(txStateCommitted) 186 187 return scanner.NewUnary( 188 nil, 189 result.GetQueryStats(), 190 scanner.WithIgnoreTruncated(tx.s.config.IgnoreTruncated()), 191 ), nil 192 } 193 } 194 195 // Rollback performs a rollback of the specified active transaction. 196 func (tx *transaction) Rollback(ctx context.Context) (err error) { 197 onDone := trace.TableOnTxRollback( 198 tx.s.config.Trace(), &ctx, 199 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/table.(*transaction).Rollback"), 200 tx.s, tx, 201 ) 202 defer func() { 203 onDone(err) 204 }() 205 206 switch tx.state.Load() { 207 case txStateCommitted: 208 return nil // nop for committed tx 209 case txStateRollbacked: 210 return xerrors.WithStackTrace(errTxRollbackedEarly) 211 default: 212 _, err = tx.s.tableService.RollbackTransaction(ctx, 213 &Ydb_Table.RollbackTransactionRequest{ 214 SessionId: tx.s.id, 215 TxId: tx.ID(), 216 OperationParams: operation.Params( 217 ctx, 218 tx.s.config.OperationTimeout(), 219 tx.s.config.OperationCancelAfter(), 220 operation.ModeSync, 221 ), 222 }, 223 ) 224 if err != nil { 225 return xerrors.WithStackTrace(err) 226 } 227 228 tx.state.Store(txStateRollbacked) 229 230 return nil 231 } 232 }