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 }