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  }