github.com/matrixorigin/matrixone@v0.7.0/pkg/vm/engine/tae/txn/txnbase/txn.go (about) 1 // Copyright 2021 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 txnbase 16 17 import ( 18 "fmt" 19 "sync/atomic" 20 21 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logstore/entry" 22 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 "github.com/matrixorigin/matrixone/pkg/container/types" 25 "github.com/matrixorigin/matrixone/pkg/logutil" 26 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 27 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/handle" 28 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 29 ) 30 31 type OpType int8 32 33 const ( 34 OpCommit = iota 35 OpRollback 36 OpPrepare 37 OpCommitting 38 OpInvalid 39 ) 40 41 const ( 42 EventRollback = iota + 1 43 EventCommitting 44 EventCommit 45 ) 46 47 type OpTxn struct { 48 Txn txnif.AsyncTxn 49 Op OpType 50 } 51 52 func (txn *OpTxn) IsReplay() bool { return txn.Txn.IsReplay() } 53 func (txn *OpTxn) Is2PC() bool { return txn.Txn.Is2PC() } 54 func (txn *OpTxn) IsTryCommitting() bool { 55 return txn.Op == OpCommit || txn.Op == OpPrepare 56 } 57 58 func (txn *OpTxn) Repr() string { 59 if txn.Op == OpCommit { 60 return fmt.Sprintf("[Commit][Txn-%X]", txn.Txn.GetID()) 61 } else { 62 return fmt.Sprintf("[Rollback][Txn-%X]", txn.Txn.GetID()) 63 } 64 } 65 66 var DefaultTxnFactory = func( 67 mgr *TxnManager, 68 store txnif.TxnStore, 69 id []byte, 70 startTS types.TS, 71 info []byte) txnif.AsyncTxn { 72 return NewTxn(mgr, store, id, startTS, info) 73 } 74 75 type Txn struct { 76 *TxnCtx 77 Mgr *TxnManager 78 Store txnif.TxnStore 79 Err error 80 LSN uint64 81 TenantID, UserID, RoleID atomic.Uint32 82 isReplay bool 83 84 PrepareCommitFn func(txnif.AsyncTxn) error 85 PrepareRollbackFn func(txnif.AsyncTxn) error 86 ApplyCommitFn func(txnif.AsyncTxn) error 87 ApplyRollbackFn func(txnif.AsyncTxn) error 88 } 89 90 func NewTxn(mgr *TxnManager, store txnif.TxnStore, txnId []byte, start types.TS, info []byte) *Txn { 91 txn := &Txn{ 92 Mgr: mgr, 93 Store: store, 94 } 95 txn.TxnCtx = NewTxnCtx(txnId, start, info) 96 return txn 97 } 98 99 func NewPersistedTxn( 100 mgr *TxnManager, 101 ctx *TxnCtx, 102 store txnif.TxnStore, 103 lsn uint64, 104 prepareCommitFn func(txnif.AsyncTxn) error, 105 prepareRollbackFn func(txnif.AsyncTxn) error, 106 applyCommitFn func(txnif.AsyncTxn) error, 107 applyRollbackFn func(txnif.AsyncTxn) error) *Txn { 108 return &Txn{ 109 Mgr: mgr, 110 TxnCtx: ctx, 111 Store: store, 112 isReplay: true, 113 LSN: lsn, 114 PrepareRollbackFn: prepareRollbackFn, 115 PrepareCommitFn: prepareCommitFn, 116 ApplyRollbackFn: applyRollbackFn, 117 ApplyCommitFn: applyCommitFn, 118 } 119 } 120 func (txn *Txn) GetLsn() uint64 { return txn.LSN } 121 func (txn *Txn) IsReplay() bool { return txn.isReplay } 122 123 func (txn *Txn) MockIncWriteCnt() int { return txn.Store.IncreateWriteCnt() } 124 125 func (txn *Txn) SetError(err error) { txn.Err = err } 126 func (txn *Txn) GetError() error { return txn.Err } 127 128 func (txn *Txn) SetPrepareCommitFn(fn func(txnif.AsyncTxn) error) { txn.PrepareCommitFn = fn } 129 func (txn *Txn) SetPrepareRollbackFn(fn func(txnif.AsyncTxn) error) { txn.PrepareRollbackFn = fn } 130 func (txn *Txn) SetApplyCommitFn(fn func(txnif.AsyncTxn) error) { txn.ApplyCommitFn = fn } 131 func (txn *Txn) SetApplyRollbackFn(fn func(txnif.AsyncTxn) error) { txn.ApplyRollbackFn = fn } 132 133 //The state transition of transaction is as follows: 134 // 1PC: TxnStateActive--->TxnStatePreparing--->TxnStateCommitted/TxnStateRollbacked 135 // TxnStateActive--->TxnStatePreparing--->TxnStateRollbacking--->TxnStateRollbacked 136 // TxnStateActive--->TxnStateRollbacking--->TxnStateRollbacked 137 // 2PC running on Coordinator: TxnStateActive--->TxnStatePreparing-->TxnStatePrepared 138 // -->TxnStateCommittingFinished--->TxnStateCommitted or 139 // TxnStateActive--->TxnStatePreparing-->TxnStatePrepared-->TxnStateRollbacked or 140 // TxnStateActive--->TxnStateRollbacking--->TxnStateRollbacked. 141 // 2PC running on Participant: TxnStateActive--->TxnStatePreparing-->TxnStatePrepared-->TxnStateCommitted or 142 // TxnStateActive--->TxnStatePreparing-->TxnStatePrepared--> 143 // TxnStateRollbacking-->TxnStateRollbacked or 144 // TxnStateActive--->TxnStateRollbacking-->TxnStateRollbacked. 145 146 // Prepare is used to pre-commit a 2PC distributed transaction. 147 // Notice that once any error happened, we should rollback the txn. 148 // TODO: 149 // 1. How to handle the case in which log service timed out? 150 // 2. For a 2pc transaction, Rollback message may arrive before Prepare message, 151 // should handle this case by TxnStorage? 152 func (txn *Txn) Prepare() (pts types.TS, err error) { 153 if txn.Mgr.GetTxn(txn.GetID()) == nil { 154 logutil.Warn("tae : txn is not found in TxnManager") 155 //txn.Err = ErrTxnNotFound 156 return types.TS{}, moerr.NewTxnNotFoundNoCtx() 157 } 158 state := txn.GetTxnState(false) 159 if state != txnif.TxnStateActive { 160 logutil.Warnf("unexpected txn status : %s", txnif.TxnStrState(state)) 161 txn.Err = moerr.NewTxnNotActiveNoCtx(txnif.TxnStrState(state)) 162 return types.TS{}, txn.Err 163 } 164 txn.Add(1) 165 err = txn.Mgr.OnOpTxn(&OpTxn{ 166 Txn: txn, 167 Op: OpPrepare, 168 }) 169 // TxnManager is closed 170 if err != nil { 171 txn.SetError(err) 172 txn.ToRollbacking(txn.GetStartTS()) 173 _ = txn.PrepareRollback() 174 _ = txn.ApplyRollback() 175 txn.DoneWithErr(err, true) 176 } 177 txn.Wait() 178 179 if txn.Err != nil { 180 txn.Mgr.DeleteTxn(txn.GetID()) 181 } 182 return txn.GetPrepareTS(), txn.GetError() 183 } 184 185 // Rollback is used to roll back a 1PC or 2PC transaction. 186 // Notice that there may be a such scenario in which a 2PC distributed transaction in ACTIVE 187 // will be rollbacked, since Rollback message may arrive before the Prepare message. 188 func (txn *Txn) Rollback() (err error) { 189 //idempotent check 190 if txn.Mgr.GetTxn(txn.GetID()) == nil { 191 logutil.Warnf("tae : txn %s is not found in TxnManager", txn.GetID()) 192 err = moerr.NewTxnNotFoundNoCtx() 193 return 194 } 195 if txn.Store.IsReadonly() { 196 err = txn.Mgr.DeleteTxn(txn.GetID()) 197 return 198 } 199 200 if txn.Is2PC() { 201 return txn.rollback2PC() 202 } 203 204 return txn.rollback1PC() 205 } 206 207 // Committing is used to record a "committing" status for coordinator. 208 // Notice that txn must commit successfully once committing message arrives, since Preparing 209 // had already succeeded. 210 func (txn *Txn) Committing() (err error) { 211 return txn.doCommitting(false) 212 } 213 214 func (txn *Txn) CommittingInRecovery() (err error) { 215 return txn.doCommitting(true) 216 } 217 218 func (txn *Txn) doCommitting(inRecovery bool) (err error) { 219 if txn.Mgr.GetTxn(txn.GetID()) == nil { 220 err = moerr.NewTxnNotFoundNoCtx() 221 return 222 } 223 state := txn.GetTxnState(false) 224 if state != txnif.TxnStatePrepared { 225 return moerr.NewInternalErrorNoCtx( 226 "stat not prepared, unexpected txn status : %s", 227 txnif.TxnStrState(state), 228 ) 229 } 230 if err = txn.ToCommittingFinished(); err != nil { 231 panic(err) 232 } 233 234 // Skip logging in recovery 235 if !inRecovery { 236 //Make a committing log entry, flush and wait it synced. 237 //A log entry's payload contains txn id , commit timestamp and txn's state. 238 _, err = txn.LogTxnState(true) 239 if err != nil { 240 panic(err) 241 } 242 } 243 err = txn.Err 244 return 245 } 246 247 // Commit is used to commit a 1PC or 2PC transaction running on Coordinator or running on Participant. 248 // Notice that the Commit of a 2PC transaction must be success once the Commit message arrives, 249 // since Preparing had already succeeded. 250 func (txn *Txn) Commit() (err error) { 251 return txn.doCommit(false) 252 } 253 254 // CommitInRecovery is called during recovery 255 func (txn *Txn) CommitInRecovery() (err error) { 256 return txn.doCommit(true) 257 } 258 259 func (txn *Txn) doCommit(inRecovery bool) (err error) { 260 if txn.Mgr.GetTxn(txn.GetID()) == nil { 261 err = moerr.NewTxnNotFoundNoCtx() 262 return 263 } 264 // Skip readonly txn 265 if txn.Store.IsReadonly() { 266 txn.Mgr.DeleteTxn(txn.GetID()) 267 return nil 268 } 269 270 if txn.Is2PC() { 271 return txn.commit2PC(inRecovery) 272 } 273 274 return txn.commit1PC(inRecovery) 275 } 276 277 func (txn *Txn) GetStore() txnif.TxnStore { 278 return txn.Store 279 } 280 281 func (txn *Txn) GetLSN() uint64 { return txn.LSN } 282 283 func (txn *Txn) DoneWithErr(err error, isAbort bool) { 284 // Idempotent check 285 if moerr.IsMoErrCode(err, moerr.ErrTxnNotActive) { 286 // FIXME::?? 287 txn.WaitGroup.Done() 288 return 289 } 290 291 if txn.Is2PC() { 292 txn.done2PCWithErr(err, isAbort) 293 return 294 } 295 txn.done1PCWithErr(err) 296 } 297 298 func (txn *Txn) PrepareCommit() (err error) { 299 logutil.Debugf("Prepare Commite %X", txn.ID) 300 if txn.PrepareCommitFn != nil { 301 err = txn.PrepareCommitFn(txn) 302 return 303 } 304 err = txn.Store.PrepareCommit() 305 return err 306 } 307 308 func (txn *Txn) PreApplyCommit() (err error) { 309 err = txn.Store.PreApplyCommit() 310 return 311 } 312 313 func (txn *Txn) ApplyCommit() (err error) { 314 if txn.ApplyCommitFn != nil { 315 err = txn.ApplyCommitFn(txn) 316 return 317 } 318 defer func() { 319 //Get the lsn of ETTxnRecord entry in GroupC. 320 txn.LSN = txn.Store.GetLSN() 321 if err == nil { 322 err = txn.Store.Close() 323 } else { 324 txn.Store.Close() 325 } 326 }() 327 err = txn.Store.ApplyCommit() 328 return 329 } 330 331 func (txn *Txn) ApplyRollback() (err error) { 332 if txn.ApplyRollbackFn != nil { 333 err = txn.ApplyRollbackFn(txn) 334 return 335 } 336 defer func() { 337 txn.LSN = txn.Store.GetLSN() 338 if err == nil { 339 err = txn.Store.Close() 340 } else { 341 txn.Store.Close() 342 } 343 }() 344 err = txn.Store.ApplyRollback() 345 return 346 } 347 348 func (txn *Txn) PrePrepare() error { 349 return txn.Store.PrePrepare() 350 } 351 352 func (txn *Txn) PrepareRollback() (err error) { 353 logutil.Debugf("Prepare Rollbacking %X", txn.ID) 354 if txn.PrepareRollbackFn != nil { 355 err = txn.PrepareRollbackFn(txn) 356 return 357 } 358 err = txn.Store.PrepareRollback() 359 return 360 } 361 362 func (txn *Txn) String() string { 363 str := txn.TxnCtx.String() 364 return fmt.Sprintf("%s: %v", str, txn.GetError()) 365 } 366 367 func (txn *Txn) WaitPrepared() error { 368 return txn.Store.WaitPrepared() 369 } 370 371 func (txn *Txn) WaitDone(err error, isAbort bool) error { 372 // logutil.Infof("Wait %s Done", txn.String()) 373 txn.DoneWithErr(err, isAbort) 374 return txn.Err 375 } 376 377 func (txn *Txn) BindAccessInfo(tenantID, userID, roleID uint32) { 378 txn.TenantID.Store(tenantID) 379 txn.UserID.Store(userID) 380 txn.RoleID.Store(roleID) 381 } 382 383 func (txn *Txn) GetTenantID() uint32 { 384 return txn.TenantID.Load() 385 } 386 387 func (txn *Txn) GetUserAndRoleID() (uint32, uint32) { 388 return txn.UserID.Load(), txn.RoleID.Load() 389 } 390 391 func (txn *Txn) CreateDatabase(name, createSql string) (db handle.Database, err error) { 392 return 393 } 394 395 func (txn *Txn) CreateDatabaseWithID(name, createSql string, id uint64) (db handle.Database, err error) { 396 return 397 } 398 399 func (txn *Txn) DropDatabase(name string) (db handle.Database, err error) { 400 return 401 } 402 403 func (txn *Txn) DropDatabaseByID(id uint64) (db handle.Database, err error) { 404 return 405 } 406 407 func (txn *Txn) UnsafeGetDatabase(id uint64) (db handle.Database, err error) { 408 return 409 } 410 411 func (txn *Txn) UnsafeGetRelation(dbId, id uint64) (db handle.Relation, err error) { 412 return 413 } 414 415 func (txn *Txn) GetDatabase(name string) (db handle.Database, err error) { 416 return 417 } 418 419 func (txn *Txn) GetDatabaseByID(id uint64) (db handle.Database, err error) { 420 return 421 } 422 423 func (txn *Txn) UseDatabase(name string) (err error) { 424 return 425 } 426 427 func (txn *Txn) CurrentDatabase() (db handle.Database) { 428 return 429 } 430 431 func (txn *Txn) DatabaseNames() (names []string) { 432 return 433 } 434 435 func (txn *Txn) LogTxnEntry(dbId, tableId uint64, entry txnif.TxnEntry, readed []*common.ID) (err error) { 436 return 437 } 438 439 func (txn *Txn) LogTxnState(sync bool) (logEntry entry.Entry, err error) { 440 return 441 }