github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/service/service_dn_handler.go (about)

     1  // Copyright 2021 - 2022 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package service
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  
    21  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    22  	"github.com/matrixorigin/matrixone/pkg/pb/txn"
    23  	"github.com/matrixorigin/matrixone/pkg/txn/util"
    24  	"go.uber.org/zap"
    25  )
    26  
    27  func (s *service) Prepare(ctx context.Context, request *txn.TxnRequest, response *txn.TxnResponse) error {
    28  	s.waitRecoveryCompleted()
    29  
    30  	util.LogTxnHandleRequest(request)
    31  	defer util.LogTxnHandleResult(response)
    32  
    33  	response.PrepareResponse = &txn.TxnPrepareResponse{}
    34  
    35  	txnID := request.Txn.ID
    36  	txnCtx := s.getTxnContext(txnID)
    37  	if txnCtx == nil {
    38  		response.TxnError = txn.WrapError(moerr.NewTxnNotFoundNoCtx(), 0)
    39  		return nil
    40  	}
    41  
    42  	txnCtx.mu.Lock()
    43  	defer txnCtx.mu.Unlock()
    44  
    45  	newTxn := txnCtx.getTxnLocked()
    46  	if !bytes.Equal(newTxn.ID, txnID) {
    47  		response.TxnError = txn.WrapError(moerr.NewTxnNotFoundNoCtx(), 0)
    48  		return nil
    49  	}
    50  	response.Txn = &newTxn
    51  
    52  	switch newTxn.Status {
    53  	case txn.TxnStatus_Active:
    54  		break
    55  	case txn.TxnStatus_Prepared:
    56  		return nil
    57  	default:
    58  		response.TxnError = txn.WrapError(moerr.NewTxnNotActiveNoCtx(""), 0)
    59  		return nil
    60  	}
    61  
    62  	newTxn.TNShards = request.Txn.TNShards
    63  	ts, err := s.storage.Prepare(ctx, newTxn)
    64  	if err != nil {
    65  		response.TxnError = txn.WrapError(err, moerr.ErrTAEPrepare)
    66  		if err := s.storage.Rollback(ctx, newTxn); err != nil {
    67  			s.logger.Error("rollback failed",
    68  				util.TxnIDFieldWithID(newTxn.ID),
    69  				zap.Error(err))
    70  		}
    71  		return nil
    72  	}
    73  	newTxn.PreparedTS = ts
    74  	txnCtx.updateTxnLocked(newTxn)
    75  
    76  	newTxn.Status = txn.TxnStatus_Prepared
    77  	txnCtx.changeStatusLocked(txn.TxnStatus_Prepared)
    78  	return nil
    79  }
    80  
    81  func (s *service) GetStatus(ctx context.Context, request *txn.TxnRequest, response *txn.TxnResponse) error {
    82  	s.waitRecoveryCompleted()
    83  
    84  	util.LogTxnHandleRequest(request)
    85  	defer util.LogTxnHandleResult(response)
    86  
    87  	response.GetStatusResponse = &txn.TxnGetStatusResponse{}
    88  
    89  	txnID := request.Txn.ID
    90  	txnCtx := s.getTxnContext(txnID)
    91  	if txnCtx == nil {
    92  		return nil
    93  	}
    94  
    95  	txnCtx.mu.RLock()
    96  	defer txnCtx.mu.RUnlock()
    97  
    98  	newTxn := txnCtx.getTxnLocked()
    99  	if !bytes.Equal(newTxn.ID, txnID) {
   100  		return nil
   101  	}
   102  	response.Txn = &newTxn
   103  	return nil
   104  }
   105  
   106  func (s *service) CommitTNShard(ctx context.Context, request *txn.TxnRequest, response *txn.TxnResponse) error {
   107  	s.waitRecoveryCompleted()
   108  
   109  	util.LogTxnHandleRequest(request)
   110  	defer util.LogTxnHandleResult(response)
   111  
   112  	response.CommitTNShardResponse = &txn.TxnCommitTNShardResponse{}
   113  
   114  	txnID := request.Txn.ID
   115  	txnCtx := s.getTxnContext(txnID)
   116  	if txnCtx == nil {
   117  		// txn must be committed, donot need to return newTxnNotFoundError
   118  		return nil
   119  	}
   120  
   121  	txnCtx.mu.Lock()
   122  	defer txnCtx.mu.Unlock()
   123  
   124  	newTxn := txnCtx.getTxnLocked()
   125  	if !bytes.Equal(newTxn.ID, txnID) {
   126  		// txn must be committed, donot need to return newTxnNotFoundError
   127  		return nil
   128  	}
   129  
   130  	defer func() {
   131  		if response.Txn.Status == txn.TxnStatus_Committed {
   132  			s.removeTxn(txnID)
   133  			s.releaseTxnContext(txnCtx)
   134  		}
   135  	}()
   136  
   137  	response.Txn = &newTxn
   138  	switch newTxn.Status {
   139  	case txn.TxnStatus_Prepared:
   140  		break
   141  	case txn.TxnStatus_Committed:
   142  		return nil
   143  	default:
   144  		s.logger.Fatal("commit on invalid status",
   145  			zap.String("status", newTxn.Status.String()),
   146  			util.TxnIDFieldWithID(newTxn.ID))
   147  	}
   148  
   149  	newTxn.CommitTS = request.Txn.CommitTS
   150  	if _, err := s.storage.Commit(ctx, newTxn); err != nil {
   151  		response.TxnError = txn.WrapError(err, moerr.ErrTAECommit)
   152  		return nil
   153  	}
   154  	txnCtx.updateTxnLocked(newTxn)
   155  
   156  	newTxn.Status = txn.TxnStatus_Committed
   157  	txnCtx.changeStatusLocked(txn.TxnStatus_Committed)
   158  	return nil
   159  }
   160  
   161  func (s *service) RollbackTNShard(ctx context.Context, request *txn.TxnRequest, response *txn.TxnResponse) error {
   162  	s.waitRecoveryCompleted()
   163  
   164  	util.LogTxnHandleRequest(request)
   165  	defer util.LogTxnHandleResult(response)
   166  
   167  	response.RollbackTNShardResponse = &txn.TxnRollbackTNShardResponse{}
   168  
   169  	txnID := request.Txn.ID
   170  	txnCtx := s.getTxnContext(txnID)
   171  	if txnCtx == nil {
   172  		// txn must be aborted, no need to return newTxnNotFoundError
   173  		return nil
   174  	}
   175  
   176  	txnCtx.mu.Lock()
   177  	defer txnCtx.mu.Unlock()
   178  
   179  	newTxn := txnCtx.getTxnLocked()
   180  	if !bytes.Equal(newTxn.ID, txnID) {
   181  		// txn must be aborted, donot need to return newTxnNotFoundError
   182  		return nil
   183  	}
   184  
   185  	defer func() {
   186  		s.removeTxn(txnID)
   187  		s.releaseTxnContext(txnCtx)
   188  	}()
   189  
   190  	response.Txn = &newTxn
   191  	switch newTxn.Status {
   192  	case txn.TxnStatus_Prepared:
   193  		break
   194  	case txn.TxnStatus_Active:
   195  		break
   196  	case txn.TxnStatus_Aborted:
   197  		return nil
   198  	default:
   199  		s.logger.Fatal("rollback on invalid status",
   200  			zap.String("status", newTxn.Status.String()),
   201  			util.TxnIDFieldWithID(newTxn.ID))
   202  	}
   203  
   204  	if err := s.storage.Rollback(ctx, newTxn); err != nil {
   205  		response.TxnError = txn.WrapError(err, moerr.ErrTAERollback)
   206  	}
   207  
   208  	newTxn.Status = txn.TxnStatus_Aborted
   209  	txnCtx.changeStatusLocked(txn.TxnStatus_Aborted)
   210  	return nil
   211  }