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