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  }