github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/client/operator.go (about) 1 // Copyright 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 client 16 17 import ( 18 "bytes" 19 "context" 20 "encoding/hex" 21 "errors" 22 "fmt" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "go.uber.org/zap" 28 29 "github.com/matrixorigin/matrixone/pkg/common/moerr" 30 "github.com/matrixorigin/matrixone/pkg/common/runtime" 31 "github.com/matrixorigin/matrixone/pkg/lockservice" 32 "github.com/matrixorigin/matrixone/pkg/pb/lock" 33 "github.com/matrixorigin/matrixone/pkg/pb/metadata" 34 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 35 "github.com/matrixorigin/matrixone/pkg/pb/txn" 36 "github.com/matrixorigin/matrixone/pkg/txn/clock" 37 "github.com/matrixorigin/matrixone/pkg/txn/rpc" 38 "github.com/matrixorigin/matrixone/pkg/txn/util" 39 v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" 40 ) 41 42 var ( 43 readTxnErrors = map[uint16]struct{}{ 44 moerr.ErrTAERead: {}, 45 moerr.ErrRpcError: {}, 46 moerr.ErrWaitTxn: {}, 47 moerr.ErrTxnNotFound: {}, 48 moerr.ErrTxnNotActive: {}, 49 } 50 writeTxnErrors = map[uint16]struct{}{ 51 moerr.ErrTAEWrite: {}, 52 moerr.ErrRpcError: {}, 53 moerr.ErrTxnNotFound: {}, 54 moerr.ErrTxnNotActive: {}, 55 } 56 commitTxnErrors = map[uint16]struct{}{ 57 moerr.ErrTAECommit: {}, 58 moerr.ErrTAERollback: {}, 59 moerr.ErrTAEPrepare: {}, 60 moerr.ErrRpcError: {}, 61 moerr.ErrTxnNotFound: {}, 62 moerr.ErrTxnNotActive: {}, 63 moerr.ErrLockTableBindChanged: {}, 64 moerr.ErrCannotCommitOrphan: {}, 65 } 66 rollbackTxnErrors = map[uint16]struct{}{ 67 moerr.ErrTAERollback: {}, 68 moerr.ErrRpcError: {}, 69 moerr.ErrTxnNotFound: {}, 70 moerr.ErrTxnNotActive: {}, 71 } 72 ) 73 74 // WithUserTxn setup user transaction flag. Only user transactions need to be controlled for the maximum 75 // number of active transactions. 76 func WithUserTxn() TxnOption { 77 return func(tc *txnOperator) { 78 tc.options = tc.options.WithUserTxn() 79 } 80 } 81 82 // WithTxnReadyOnly setup readonly flag 83 func WithTxnReadyOnly() TxnOption { 84 return func(tc *txnOperator) { 85 tc.options = tc.options.WithReadOnly() 86 } 87 } 88 89 // WithTxnDisable1PCOpt disable 1pc opt on distributed transaction. By default, mo enables 1pc 90 // optimization for distributed transactions. For write operations, if all partitions' prepares are 91 // executed successfully, then the transaction is considered committed and returned directly to the 92 // client. Partitions' prepared data are committed asynchronously. 93 func WithTxnDisable1PCOpt() TxnOption { 94 return func(tc *txnOperator) { 95 tc.options = tc.options.WithDisable1PC() 96 } 97 } 98 99 // WithTxnCNCoordinator set cn txn coordinator 100 func WithTxnCNCoordinator() TxnOption { 101 return func(tc *txnOperator) { 102 tc.coordinator = true 103 } 104 } 105 106 // WithTxnLockService set txn lock service 107 func WithTxnLockService(lockService lockservice.LockService) TxnOption { 108 return func(tc *txnOperator) { 109 tc.lockService = lockService 110 } 111 } 112 113 // WithTxnCreateBy set txn create by. 114 func WithTxnCreateBy( 115 accountID uint32, 116 userName string, 117 sessionID string, 118 connectionID uint32) TxnOption { 119 return func(tc *txnOperator) { 120 tc.options.CN = runtime.ProcessLevelRuntime().ServiceUUID() 121 tc.options.SessionID = sessionID 122 tc.options.ConnectionID = connectionID 123 tc.options.AccountID = accountID 124 tc.options.UserName = userName 125 } 126 } 127 128 // WithTxnCacheWrite Set cache write requests, after each Write call, the request will not be sent 129 // to the TN node immediately, but stored in the Coordinator's memory, and the Coordinator will 130 // choose the right time to send the cached requests. The following scenarios trigger the sending 131 // of requests to DN: 132 // 1. Before read, because the Coordinator is not aware of the format and content of the written data, 133 // it is necessary to send the cached write requests to the corresponding TN node each time Read is 134 // called, used to implement "read your write". 135 // 2. Before commit, obviously, the cached write requests needs to be sent to the corresponding TN node 136 // before commit. 137 func WithTxnCacheWrite() TxnOption { 138 return func(tc *txnOperator) { 139 tc.options = tc.options.WithEnableCacheWrite() 140 tc.mu.cachedWrites = make(map[uint64][]txn.TxnRequest) 141 } 142 } 143 144 // WithSnapshotTS use a spec snapshot timestamp to build TxnOperator. 145 func WithSnapshotTS(ts timestamp.Timestamp) TxnOption { 146 return func(tc *txnOperator) { 147 tc.mu.txn.SnapshotTS = ts 148 } 149 } 150 151 // WithTxnMode set txn mode 152 func WithTxnMode(value txn.TxnMode) TxnOption { 153 return func(tc *txnOperator) { 154 tc.mu.txn.Mode = value 155 } 156 } 157 158 // WithTxnIsolation set txn isolation 159 func WithTxnIsolation(value txn.TxnIsolation) TxnOption { 160 return func(tc *txnOperator) { 161 tc.mu.txn.Isolation = value 162 } 163 } 164 165 // WithTxnSkipLock skip txn lock on specified tables 166 func WithTxnSkipLock( 167 tables []uint64, 168 modes []lock.LockMode) TxnOption { 169 return func(tc *txnOperator) { 170 tc.options.SkipLockTables = append(tc.options.SkipLockTables, tables...) 171 tc.options.SkipLockTableModes = append(tc.options.SkipLockTableModes, modes...) 172 } 173 } 174 175 // WithTxnEnableCheckDup enable check duplicate before commit to TN 176 func WithTxnEnableCheckDup() TxnOption { 177 return func(tc *txnOperator) { 178 tc.options = tc.options.WithEnableCheckDup() 179 } 180 } 181 182 func WithDisableTrace(value bool) TxnOption { 183 return func(tc *txnOperator) { 184 if value { 185 tc.options = tc.options.WithDisableTrace() 186 } 187 } 188 } 189 190 func WithSessionInfo(info string) TxnOption { 191 return func(tc *txnOperator) { 192 tc.options.SessionInfo = info 193 } 194 } 195 196 type txnOperator struct { 197 sender rpc.TxnSender 198 waiter *waiter 199 txnID []byte 200 coordinator bool 201 options txn.TxnOptions 202 cannotCleanWorkspace bool 203 workspace Workspace 204 timestampWaiter TimestampWaiter 205 clock clock.Clock 206 createAt time.Time 207 commitAt time.Time 208 commitSeq uint64 209 lockService lockservice.LockService 210 sequence atomic.Uint64 211 createTs timestamp.Timestamp 212 //read-only txn operators for supporting snapshot read feature. 213 children []*txnOperator 214 parent atomic.Pointer[txnOperator] 215 216 mu struct { 217 sync.RWMutex 218 waitActive bool 219 closed bool 220 txn txn.TxnMeta 221 cachedWrites map[uint64][]txn.TxnRequest 222 lockTables []lock.LockTable 223 callbacks map[EventType][]func(TxnEvent) 224 retry bool 225 lockSeq uint64 226 waitLocks map[uint64]Lock 227 } 228 229 commitCounter counter 230 rollbackCounter counter 231 runSqlCounter counter 232 233 waitActiveCost time.Duration 234 } 235 236 func newTxnOperator( 237 clock clock.Clock, 238 sender rpc.TxnSender, 239 txnMeta txn.TxnMeta, 240 options ...TxnOption) *txnOperator { 241 tc := &txnOperator{sender: sender} 242 tc.mu.txn = txnMeta 243 tc.txnID = txnMeta.ID 244 tc.clock = clock 245 tc.createAt = time.Now() 246 tc.createTs, _ = clock.Now() 247 for _, opt := range options { 248 opt(tc) 249 } 250 tc.adjust() 251 util.LogTxnCreated(tc.mu.txn) 252 253 if tc.options.UserTxn() { 254 v2.TxnUserCounter.Inc() 255 } else { 256 v2.TxnInternalCounter.Inc() 257 } 258 return tc 259 } 260 261 func (tc *txnOperator) IsSnapOp() bool { 262 return tc.parent.Load() != nil 263 } 264 265 func (tc *txnOperator) CloneSnapshotOp(snapshot timestamp.Timestamp) TxnOperator { 266 op := &txnOperator{} 267 op.mu.txn = txn.TxnMeta{ 268 SnapshotTS: snapshot, 269 ID: tc.mu.txn.ID, 270 TNShards: tc.mu.txn.TNShards, 271 } 272 op.txnID = op.mu.txn.ID 273 274 op.workspace = tc.workspace.CloneSnapshotWS() 275 op.workspace.BindTxnOp(op) 276 277 tc.children = append(tc.children, op) 278 op.parent.Store(tc) 279 return op 280 } 281 282 func newTxnOperatorWithSnapshot( 283 sender rpc.TxnSender, 284 snapshot []byte) (*txnOperator, error) { 285 v := &txn.CNTxnSnapshot{} 286 if err := v.Unmarshal(snapshot); err != nil { 287 return nil, err 288 } 289 290 tc := &txnOperator{sender: sender} 291 tc.txnID = v.Txn.ID 292 tc.options = v.Options 293 tc.mu.txn = v.Txn 294 tc.mu.txn.Mirror = true 295 tc.mu.lockTables = v.LockTables 296 297 tc.adjust() 298 util.LogTxnCreated(tc.mu.txn) 299 return tc, nil 300 } 301 302 func (tc *txnOperator) setWaitActive(v bool) { 303 tc.mu.Lock() 304 defer tc.mu.Unlock() 305 tc.mu.waitActive = v 306 } 307 308 func (tc *txnOperator) waitActive(ctx context.Context) error { 309 if tc.waiter == nil { 310 return nil 311 } 312 313 tc.setWaitActive(true) 314 defer func() { 315 tc.waiter.close() 316 tc.setWaitActive(false) 317 }() 318 319 cost, err := tc.doCostAction( 320 time.Time{}, 321 WaitActiveEvent, 322 func() error { 323 return tc.waiter.wait(ctx) 324 }, 325 false) 326 tc.waitActiveCost = cost 327 v2.TxnWaitActiveDurationHistogram.Observe(cost.Seconds()) 328 return err 329 } 330 331 func (tc *txnOperator) GetWaitActiveCost() time.Duration { 332 return tc.waitActiveCost 333 } 334 335 func (tc *txnOperator) notifyActive() { 336 if tc.waiter == nil { 337 panic("BUG: notify active on non-waiter txn operator") 338 } 339 defer tc.waiter.close() 340 tc.waiter.notify() 341 } 342 343 func (tc *txnOperator) AddWorkspace(workspace Workspace) { 344 tc.workspace = workspace 345 } 346 347 func (tc *txnOperator) GetWorkspace() Workspace { 348 return tc.workspace 349 } 350 351 func (tc *txnOperator) adjust() { 352 if tc.sender == nil { 353 util.GetLogger().Fatal("missing txn sender") 354 } 355 if len(tc.mu.txn.ID) == 0 { 356 util.GetLogger().Fatal("missing txn id") 357 } 358 if tc.options.ReadOnly() && tc.options.CacheWriteEnabled() { 359 util.GetLogger().Fatal("readyOnly and delayWrites cannot both be set") 360 } 361 } 362 363 func (tc *txnOperator) Txn() txn.TxnMeta { 364 return tc.getTxnMeta(false) 365 } 366 367 func (tc *txnOperator) TxnRef() *txn.TxnMeta { 368 tc.mu.RLock() 369 defer tc.mu.RUnlock() 370 return &tc.mu.txn 371 } 372 373 func (tc *txnOperator) SnapshotTS() timestamp.Timestamp { 374 tc.mu.RLock() 375 defer tc.mu.RUnlock() 376 return tc.mu.txn.SnapshotTS 377 } 378 379 func (tc *txnOperator) CreateTS() timestamp.Timestamp { 380 return tc.createTs 381 } 382 383 func (tc *txnOperator) Status() txn.TxnStatus { 384 tc.mu.RLock() 385 defer tc.mu.RUnlock() 386 return tc.mu.txn.Status 387 } 388 389 func (tc *txnOperator) Snapshot() ([]byte, error) { 390 tc.mu.Lock() 391 defer tc.mu.Unlock() 392 393 if err := tc.checkStatus(true); err != nil { 394 return nil, err 395 } 396 snapshot := &txn.CNTxnSnapshot{ 397 Txn: tc.mu.txn, 398 LockTables: tc.mu.lockTables, 399 Options: tc.options, 400 } 401 return snapshot.Marshal() 402 } 403 404 func (tc *txnOperator) UpdateSnapshot( 405 ctx context.Context, 406 ts timestamp.Timestamp) error { 407 tc.mu.Lock() 408 defer tc.mu.Unlock() 409 if err := tc.checkStatus(true); err != nil { 410 return err 411 } 412 413 // ony push model support RC isolation 414 if tc.timestampWaiter == nil { 415 return nil 416 } 417 418 _, err := tc.doCostAction( 419 time.Time{}, 420 UpdateSnapshotEvent, 421 func() error { 422 var err error 423 tc.mu.txn.SnapshotTS, err = tc.timestampWaiter.GetTimestamp( 424 ctx, 425 ts) 426 return err 427 }, 428 true) 429 return err 430 } 431 432 func (tc *txnOperator) ApplySnapshot(data []byte) error { 433 if !tc.coordinator { 434 util.GetLogger().Fatal("apply snapshot on non-coordinator txn operator") 435 } 436 437 tc.mu.Lock() 438 defer tc.mu.Unlock() 439 440 if err := tc.checkStatus(true); err != nil { 441 return err 442 } 443 444 snapshot := &txn.CNTxnSnapshot{} 445 if err := snapshot.Unmarshal(data); err != nil { 446 return err 447 } 448 449 if !bytes.Equal(snapshot.Txn.ID, tc.mu.txn.ID) { 450 util.GetLogger().Fatal("apply snapshot with invalid txn id") 451 } 452 453 // apply locked tables in other cn 454 for _, v := range snapshot.LockTables { 455 if err := tc.doAddLockTableLocked(v); err != nil { 456 return err 457 } 458 } 459 460 for _, tn := range snapshot.Txn.TNShards { 461 has := false 462 for _, v := range tc.mu.txn.TNShards { 463 if v.ShardID == tn.ShardID { 464 has = true 465 break 466 } 467 } 468 469 if !has { 470 tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, tn) 471 } 472 } 473 if tc.mu.txn.SnapshotTS.Less(snapshot.Txn.SnapshotTS) { 474 tc.mu.txn.SnapshotTS = snapshot.Txn.SnapshotTS 475 } 476 util.LogTxnUpdated(tc.mu.txn) 477 return nil 478 } 479 480 func (tc *txnOperator) Read(ctx context.Context, requests []txn.TxnRequest) (*rpc.SendResult, error) { 481 util.LogTxnRead(tc.getTxnMeta(false)) 482 483 for idx := range requests { 484 requests[idx].Method = txn.TxnMethod_Read 485 } 486 487 if err := tc.validate(ctx, false); err != nil { 488 return nil, err 489 } 490 491 requests = tc.maybeInsertCachedWrites(requests, false) 492 return tc.trimResponses(tc.handleError(tc.doSend(ctx, requests, false))) 493 } 494 495 func (tc *txnOperator) Write(ctx context.Context, requests []txn.TxnRequest) (*rpc.SendResult, error) { 496 util.LogTxnWrite(tc.getTxnMeta(false)) 497 return tc.doWrite(ctx, requests, false) 498 } 499 500 func (tc *txnOperator) WriteAndCommit(ctx context.Context, requests []txn.TxnRequest) (*rpc.SendResult, error) { 501 util.LogTxnWrite(tc.getTxnMeta(false)) 502 util.LogTxnCommit(tc.getTxnMeta(false)) 503 return tc.doWrite(ctx, requests, true) 504 } 505 506 func (tc *txnOperator) Commit(ctx context.Context) (err error) { 507 tc.commitCounter.addEnter() 508 defer tc.commitCounter.addExit() 509 txn := tc.getTxnMeta(false) 510 util.LogTxnCommit(txn) 511 512 tc.commitSeq = tc.NextSequence() 513 tc.commitAt = time.Now() 514 515 tc.triggerEvent(newEvent(CommitEvent, txn, tc.commitSeq, nil)) 516 defer func() { 517 cost := time.Since(tc.commitAt) 518 v2.TxnCNCommitDurationHistogram.Observe(cost.Seconds()) 519 tc.triggerEvent(newCostEvent(CommitEvent, tc.getTxnMeta(false), tc.commitSeq, err, cost)) 520 }() 521 522 if tc.options.ReadOnly() { 523 tc.mu.Lock() 524 defer tc.mu.Unlock() 525 tc.closeLocked() 526 return 527 } 528 529 result, e := tc.doWrite(ctx, nil, true) 530 if e != nil { 531 err = e 532 return 533 } 534 535 if result != nil { 536 result.Release() 537 } 538 return 539 } 540 541 func (tc *txnOperator) Rollback(ctx context.Context) (err error) { 542 tc.rollbackCounter.addEnter() 543 defer tc.rollbackCounter.addExit() 544 v2.TxnRollbackCounter.Inc() 545 txnMeta := tc.getTxnMeta(false) 546 util.LogTxnRollback(txnMeta) 547 548 if tc.workspace != nil && !tc.cannotCleanWorkspace { 549 if err = tc.workspace.Rollback(ctx); err != nil { 550 util.GetLogger().Error("rollback workspace failed", 551 util.TxnIDField(txnMeta), zap.Error(err)) 552 } 553 } 554 555 tc.mu.Lock() 556 defer tc.mu.Unlock() 557 558 if tc.mu.closed { 559 return nil 560 } 561 562 seq := tc.NextSequence() 563 start := time.Now() 564 tc.triggerEventLocked(newEvent(RollbackEvent, txnMeta, seq, nil)) 565 defer func() { 566 cost := time.Since(start) 567 tc.triggerEventLocked(newCostEvent(RollbackEvent, txnMeta, seq, err, cost)) 568 }() 569 570 defer func() { 571 tc.mu.txn.Status = txn.TxnStatus_Aborted 572 tc.closeLocked() 573 }() 574 575 if tc.needUnlockLocked() { 576 defer tc.unlock(ctx) 577 } 578 579 if len(tc.mu.txn.TNShards) == 0 { 580 return nil 581 } 582 583 result, err := tc.handleError(tc.doSend(ctx, []txn.TxnRequest{{ 584 Method: txn.TxnMethod_Rollback, 585 RollbackRequest: &txn.TxnRollbackRequest{}, 586 }}, true)) 587 if err != nil { 588 if moerr.IsMoErrCode(err, moerr.ErrTxnClosed) { 589 return nil 590 } 591 return err 592 } 593 if result != nil { 594 result.Release() 595 } 596 return nil 597 } 598 599 func (tc *txnOperator) AddLockTable(value lock.LockTable) error { 600 tc.mu.Lock() 601 defer tc.mu.Unlock() 602 if tc.mu.txn.Mode != txn.TxnMode_Pessimistic { 603 panic("lock in optimistic mode") 604 } 605 606 // mirror txn can not check status, and the txn's status is on the creation cn of the txn. 607 if !tc.mu.txn.Mirror { 608 if err := tc.checkStatus(true); err != nil { 609 return err 610 } 611 } 612 613 return tc.doAddLockTableLocked(value) 614 } 615 616 func (tc *txnOperator) ResetRetry(retry bool) { 617 tc.mu.Lock() 618 defer tc.mu.Unlock() 619 tc.mu.retry = retry 620 } 621 622 func (tc *txnOperator) IsRetry() bool { 623 tc.mu.RLock() 624 defer tc.mu.RUnlock() 625 return tc.mu.retry 626 } 627 628 func (tc *txnOperator) doAddLockTableLocked(value lock.LockTable) error { 629 for _, l := range tc.mu.lockTables { 630 if l.Group == value.Group && 631 l.Table == value.Table { 632 if l.Changed(value) { 633 return moerr.NewLockTableBindChangedNoCtx() 634 } 635 return nil 636 } 637 } 638 tc.mu.lockTables = append(tc.mu.lockTables, value) 639 return nil 640 } 641 642 func (tc *txnOperator) Debug(ctx context.Context, requests []txn.TxnRequest) (*rpc.SendResult, error) { 643 for idx := range requests { 644 requests[idx].Method = txn.TxnMethod_DEBUG 645 } 646 647 if err := tc.validate(ctx, false); err != nil { 648 return nil, err 649 } 650 651 requests = tc.maybeInsertCachedWrites(requests, false) 652 return tc.trimResponses(tc.handleError(tc.doSend(ctx, requests, false))) 653 } 654 655 func (tc *txnOperator) doWrite(ctx context.Context, requests []txn.TxnRequest, commit bool) (*rpc.SendResult, error) { 656 for idx := range requests { 657 requests[idx].Method = txn.TxnMethod_Write 658 } 659 660 if tc.options.ReadOnly() { 661 util.GetLogger().Fatal("can not write on ready only transaction") 662 } 663 var payload []txn.TxnRequest 664 if commit { 665 if tc.workspace != nil { 666 reqs, err := tc.workspace.Commit(ctx) 667 if err != nil { 668 return nil, errors.Join(err, tc.Rollback(ctx)) 669 } 670 payload = reqs 671 } 672 tc.mu.Lock() 673 defer func() { 674 tc.closeLocked() 675 tc.mu.Unlock() 676 }() 677 if tc.mu.closed { 678 return nil, moerr.NewTxnClosedNoCtx(tc.txnID) 679 } 680 681 if tc.needUnlockLocked() { 682 tc.mu.txn.LockTables = tc.mu.lockTables 683 defer tc.unlock(ctx) 684 } 685 } 686 687 if err := tc.validate(ctx, commit); err != nil { 688 return nil, err 689 } 690 691 var txnReqs []*txn.TxnRequest 692 if payload != nil { 693 v2.TxnCNCommitCounter.Inc() 694 for i := range payload { 695 payload[i].Txn = tc.getTxnMeta(true) 696 txnReqs = append(txnReqs, &payload[i]) 697 } 698 tc.updateWritePartitions(payload, commit) 699 } 700 701 tc.updateWritePartitions(requests, commit) 702 703 // delayWrites enabled, no responses 704 if !commit && tc.maybeCacheWrites(requests, commit) { 705 return nil, nil 706 } 707 708 if commit { 709 if len(tc.mu.txn.TNShards) == 0 { // commit no write handled txn 710 tc.mu.txn.Status = txn.TxnStatus_Committed 711 return nil, nil 712 } 713 714 requests = tc.maybeInsertCachedWrites(requests, true) 715 requests = append(requests, txn.TxnRequest{ 716 Method: txn.TxnMethod_Commit, 717 Flag: txn.SkipResponseFlag, 718 CommitRequest: &txn.TxnCommitRequest{ 719 Payload: txnReqs, 720 Disable1PCOpt: tc.options.Is1PCDisabled(), 721 }}) 722 } 723 return tc.trimResponses(tc.handleError(tc.doSend(ctx, requests, commit))) 724 } 725 726 func (tc *txnOperator) updateWritePartitions(requests []txn.TxnRequest, locked bool) { 727 if len(requests) == 0 { 728 return 729 } 730 731 if !locked { 732 tc.mu.Lock() 733 defer tc.mu.Unlock() 734 } 735 736 for _, req := range requests { 737 tc.addPartitionLocked(req.CNRequest.Target) 738 } 739 } 740 741 func (tc *txnOperator) addPartitionLocked(tn metadata.TNShard) { 742 for idx := range tc.mu.txn.TNShards { 743 if tc.mu.txn.TNShards[idx].ShardID == tn.ShardID { 744 return 745 } 746 } 747 tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, tn) 748 util.LogTxnUpdated(tc.mu.txn) 749 } 750 751 func (tc *txnOperator) validate(ctx context.Context, locked bool) error { 752 if _, ok := ctx.Deadline(); !ok { 753 util.GetLogger().Fatal("context deadline set") 754 } 755 756 return tc.checkStatus(locked) 757 } 758 759 func (tc *txnOperator) checkStatus(locked bool) error { 760 if !locked { 761 tc.mu.RLock() 762 defer tc.mu.RUnlock() 763 } 764 765 if tc.mu.closed { 766 return moerr.NewTxnClosedNoCtx(tc.txnID) 767 } 768 return nil 769 } 770 771 func (tc *txnOperator) maybeCacheWrites(requests []txn.TxnRequest, locked bool) bool { 772 if tc.options.CacheWriteEnabled() { 773 if !locked { 774 tc.mu.Lock() 775 defer tc.mu.Unlock() 776 } 777 778 for idx := range requests { 779 requests[idx].Flag |= txn.SkipResponseFlag 780 tn := requests[idx].CNRequest.Target.ShardID 781 tc.mu.cachedWrites[tn] = append(tc.mu.cachedWrites[tn], requests[idx]) 782 } 783 return true 784 } 785 return false 786 } 787 788 func (tc *txnOperator) maybeInsertCachedWrites( 789 requests []txn.TxnRequest, 790 locked bool, 791 ) []txn.TxnRequest { 792 if len(requests) == 0 || 793 !tc.options.CacheWriteEnabled() { 794 return requests 795 } 796 797 if !locked { 798 tc.mu.Lock() 799 defer tc.mu.Unlock() 800 } 801 802 if len(tc.mu.cachedWrites) == 0 { 803 return requests 804 } 805 806 newRequests := requests 807 hasCachedWrites := false 808 insertCount := 0 809 for idx := range requests { 810 tn := requests[idx].CNRequest.Target.ShardID 811 if writes, ok := tc.getCachedWritesLocked(tn); ok { 812 if !hasCachedWrites { 813 // copy all requests into newRequests if cached writes encountered 814 newRequests = append([]txn.TxnRequest(nil), requests[:idx]...) 815 } 816 newRequests = append(newRequests, writes...) 817 tc.clearCachedWritesLocked(tn) 818 hasCachedWrites = true 819 insertCount += len(writes) 820 } 821 if hasCachedWrites { 822 newRequests = append(newRequests, requests[idx]) 823 } 824 } 825 return newRequests 826 } 827 828 func (tc *txnOperator) getCachedWritesLocked(tn uint64) ([]txn.TxnRequest, bool) { 829 writes, ok := tc.mu.cachedWrites[tn] 830 if !ok || len(writes) == 0 { 831 return nil, false 832 } 833 return writes, true 834 } 835 836 func (tc *txnOperator) clearCachedWritesLocked(tn uint64) { 837 delete(tc.mu.cachedWrites, tn) 838 } 839 840 func (tc *txnOperator) getTxnMeta(locked bool) txn.TxnMeta { 841 if !locked { 842 tc.mu.RLock() 843 defer tc.mu.RUnlock() 844 } 845 return tc.mu.txn 846 } 847 848 func (tc *txnOperator) doSend(ctx context.Context, requests []txn.TxnRequest, locked bool) (*rpc.SendResult, error) { 849 txnMeta := tc.getTxnMeta(locked) 850 for idx := range requests { 851 requests[idx].Txn = txnMeta 852 } 853 854 util.LogTxnSendRequests(requests) 855 result, err := tc.sender.Send(ctx, requests) 856 if err != nil { 857 util.LogTxnSendRequestsFailed(requests, err) 858 return nil, err 859 } 860 util.LogTxnReceivedResponses(result.Responses) 861 862 if len(result.Responses) == 0 { 863 return result, nil 864 } 865 866 // update commit timestamp 867 resp := result.Responses[len(result.Responses)-1] 868 if resp.Txn == nil { 869 return result, nil 870 } 871 if !locked { 872 tc.mu.Lock() 873 defer tc.mu.Unlock() 874 } 875 tc.mu.txn.CommitTS = resp.Txn.CommitTS 876 tc.mu.txn.Status = resp.Txn.Status 877 return result, nil 878 } 879 880 func (tc *txnOperator) handleError(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 881 if err != nil { 882 return nil, err 883 } 884 885 for _, resp := range result.Responses { 886 if err := tc.handleErrorResponse(resp); err != nil { 887 result.Release() 888 return nil, err 889 } 890 } 891 return result, nil 892 } 893 894 func (tc *txnOperator) handleErrorResponse(resp txn.TxnResponse) error { 895 switch resp.Method { 896 case txn.TxnMethod_Read: 897 if err := tc.checkResponseTxnStatusForReadWrite(resp); err != nil { 898 return err 899 } 900 return tc.checkTxnError(resp.TxnError, readTxnErrors) 901 case txn.TxnMethod_Write: 902 if err := tc.checkResponseTxnStatusForReadWrite(resp); err != nil { 903 return err 904 } 905 return tc.checkTxnError(resp.TxnError, writeTxnErrors) 906 case txn.TxnMethod_Commit: 907 tc.triggerEventLocked( 908 newCostEvent( 909 CommitResponseEvent, 910 tc.mu.txn, 911 tc.commitSeq, 912 nil, 913 time.Since(tc.commitAt))) 914 915 if err := tc.checkResponseTxnStatusForCommit(resp); err != nil { 916 return err 917 } 918 919 err := tc.checkTxnError(resp.TxnError, commitTxnErrors) 920 if err == nil || !tc.mu.txn.IsPessimistic() { 921 return err 922 } 923 924 // commit failed, refresh invalid lock tables 925 if moerr.IsMoErrCode(err, moerr.ErrLockTableBindChanged) { 926 tc.lockService.ForceRefreshLockTableBinds( 927 resp.CommitResponse.InvalidLockTables, 928 func(bind lock.LockTable) bool { 929 for _, hold := range tc.mu.lockTables { 930 if hold.Table == bind.Table && !hold.Changed(bind) { 931 return true 932 } 933 } 934 return false 935 }) 936 } 937 938 if moerr.IsMoErrCode(err, moerr.ErrTxnWWConflict) || 939 moerr.IsMoErrCode(err, moerr.ErrDuplicateEntry) { 940 v, ok := runtime.ProcessLevelRuntime().GetGlobalVariables(runtime.EnableCheckInvalidRCErrors) 941 if ok && v.(bool) { 942 util.GetLogger().Fatal("failed", 943 zap.Error(err), 944 zap.String("txn", hex.EncodeToString(tc.txnID))) 945 } 946 } 947 return err 948 case txn.TxnMethod_Rollback: 949 if err := tc.checkResponseTxnStatusForRollback(resp); err != nil { 950 return err 951 } 952 return tc.checkTxnError(resp.TxnError, rollbackTxnErrors) 953 case txn.TxnMethod_DEBUG: 954 if resp.TxnError != nil { 955 return resp.TxnError.UnwrapError() 956 } 957 return nil 958 default: 959 return moerr.NewNotSupportedNoCtx("unknown txn response method: %s", resp.DebugString()) 960 } 961 } 962 963 func (tc *txnOperator) checkResponseTxnStatusForReadWrite(resp txn.TxnResponse) error { 964 if resp.TxnError != nil { 965 return nil 966 } 967 968 txnMeta := resp.Txn 969 if txnMeta == nil { 970 return moerr.NewTxnClosedNoCtx(tc.txnID) 971 } 972 973 switch txnMeta.Status { 974 case txn.TxnStatus_Active: 975 return nil 976 case txn.TxnStatus_Aborted, txn.TxnStatus_Aborting, 977 txn.TxnStatus_Committed, txn.TxnStatus_Committing: 978 return moerr.NewTxnClosedNoCtx(tc.txnID) 979 default: 980 util.GetLogger().Fatal("invalid response status for read or write", 981 util.TxnField(*txnMeta)) 982 } 983 return nil 984 } 985 986 func (tc *txnOperator) checkTxnError(txnError *txn.TxnError, possibleErrorMap map[uint16]struct{}) error { 987 if txnError == nil { 988 return nil 989 } 990 991 // use txn internal error code to check error 992 txnCode := uint16(txnError.TxnErrCode) 993 if txnCode == moerr.ErrTNShardNotFound { 994 // do we still have the uuid and shard id? 995 return moerr.NewTNShardNotFoundNoCtx("", 0xDEADBEAF) 996 } 997 998 if _, ok := possibleErrorMap[txnCode]; ok { 999 return txnError.UnwrapError() 1000 } 1001 1002 panic(moerr.NewInternalErrorNoCtx("invalid txn error, code %d, msg %s", txnCode, txnError.DebugString())) 1003 } 1004 1005 func (tc *txnOperator) checkResponseTxnStatusForCommit(resp txn.TxnResponse) error { 1006 if resp.TxnError != nil { 1007 return nil 1008 } 1009 1010 txnMeta := resp.Txn 1011 if txnMeta == nil { 1012 return moerr.NewTxnClosedNoCtx(tc.txnID) 1013 } 1014 1015 switch txnMeta.Status { 1016 case txn.TxnStatus_Committed, txn.TxnStatus_Aborted: 1017 return nil 1018 default: 1019 panic(moerr.NewInternalErrorNoCtx("invalid response status for commit, %v", txnMeta.Status)) 1020 } 1021 } 1022 1023 func (tc *txnOperator) checkResponseTxnStatusForRollback(resp txn.TxnResponse) error { 1024 if resp.TxnError != nil { 1025 return nil 1026 } 1027 1028 txnMeta := resp.Txn 1029 if txnMeta == nil { 1030 return moerr.NewTxnClosedNoCtx(tc.txnID) 1031 } 1032 1033 switch txnMeta.Status { 1034 case txn.TxnStatus_Aborted: 1035 return nil 1036 default: 1037 panic(moerr.NewInternalErrorNoCtx("invalid response status for rollback %v", txnMeta.Status)) 1038 } 1039 } 1040 1041 func (tc *txnOperator) trimResponses(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 1042 if err != nil { 1043 return nil, err 1044 } 1045 1046 values := result.Responses[:0] 1047 for _, resp := range result.Responses { 1048 if !resp.HasFlag(txn.SkipResponseFlag) { 1049 values = append(values, resp) 1050 } 1051 } 1052 result.Responses = values 1053 return result, nil 1054 } 1055 1056 func (tc *txnOperator) unlock(ctx context.Context) { 1057 if !tc.commitAt.IsZero() { 1058 v2.TxnCNCommitResponseDurationHistogram.Observe(float64(time.Since(tc.commitAt).Seconds())) 1059 } 1060 1061 // rc mode need to see the committed value, so wait logtail applied 1062 if tc.mu.txn.IsRCIsolation() && 1063 tc.timestampWaiter != nil { 1064 cost, err := tc.doCostAction( 1065 time.Time{}, 1066 CommitWaitApplyEvent, 1067 func() error { 1068 _, err := tc.timestampWaiter.GetTimestamp(ctx, tc.mu.txn.CommitTS) 1069 return err 1070 }, 1071 true) 1072 v2.TxnCNCommitWaitLogtailDurationHistogram.Observe(cost.Seconds()) 1073 1074 if err != nil { 1075 util.GetLogger().Error("txn wait committed log applied failed in rc mode", 1076 util.TxnField(tc.mu.txn), 1077 zap.Error(err)) 1078 } 1079 } 1080 1081 _, err := tc.doCostAction( 1082 time.Time{}, 1083 UnlockEvent, 1084 func() error { 1085 return tc.lockService.Unlock( 1086 ctx, 1087 tc.mu.txn.ID, 1088 tc.mu.txn.CommitTS) 1089 }, 1090 true) 1091 if err != nil { 1092 util.GetLogger().Error("failed to unlock txn", 1093 util.TxnField(tc.mu.txn), 1094 zap.Error(err)) 1095 } 1096 } 1097 1098 func (tc *txnOperator) needUnlockLocked() bool { 1099 if tc.mu.txn.Mode == 1100 txn.TxnMode_Optimistic { 1101 return false 1102 } 1103 return tc.lockService != nil 1104 } 1105 1106 func (tc *txnOperator) closeLocked() { 1107 if !tc.mu.closed { 1108 tc.mu.closed = true 1109 tc.triggerEventLocked( 1110 TxnEvent{ 1111 Event: ClosedEvent, 1112 Txn: tc.mu.txn, 1113 }) 1114 } 1115 } 1116 1117 func (tc *txnOperator) AddWaitLock(tableID uint64, rows [][]byte, opt lock.LockOptions) uint64 { 1118 tc.mu.Lock() 1119 defer tc.mu.Unlock() 1120 if tc.mu.waitLocks == nil { 1121 tc.mu.waitLocks = make(map[uint64]Lock) 1122 } 1123 1124 seq := tc.mu.lockSeq 1125 tc.mu.lockSeq++ 1126 1127 tc.mu.waitLocks[seq] = Lock{ 1128 TableID: tableID, 1129 Rows: rows, 1130 Options: opt, 1131 } 1132 return seq 1133 } 1134 1135 func (tc *txnOperator) RemoveWaitLock(key uint64) { 1136 tc.mu.Lock() 1137 defer tc.mu.Unlock() 1138 1139 delete(tc.mu.waitLocks, key) 1140 } 1141 1142 func (tc *txnOperator) LockTableCount() int32 { 1143 tc.mu.RLock() 1144 defer tc.mu.RUnlock() 1145 if tc.mu.txn.Mode != txn.TxnMode_Pessimistic { 1146 panic("lock in optimistic mode") 1147 } 1148 return int32(len(tc.mu.lockTables)) 1149 } 1150 1151 func (tc *txnOperator) GetOverview() TxnOverview { 1152 tc.mu.RLock() 1153 defer tc.mu.RUnlock() 1154 1155 return TxnOverview{ 1156 CreateAt: tc.createAt, 1157 Meta: tc.mu.txn, 1158 UserTxn: tc.options.UserTxn(), 1159 WaitLocks: tc.getWaitLocksLocked(), 1160 } 1161 } 1162 1163 func (tc *txnOperator) getWaitLocksLocked() []Lock { 1164 if tc.mu.waitLocks == nil { 1165 return nil 1166 } 1167 1168 values := make([]Lock, 0, len(tc.mu.waitLocks)) 1169 for _, l := range tc.mu.waitLocks { 1170 values = append(values, l) 1171 } 1172 return values 1173 } 1174 1175 func (tc *txnOperator) LockSkipped( 1176 tableID uint64, 1177 mode lock.LockMode) bool { 1178 if len(tc.options.SkipLockTables) == 0 { 1179 return false 1180 } 1181 for i, id := range tc.options.SkipLockTables { 1182 if id == tableID && 1183 mode == tc.options.SkipLockTableModes[i] { 1184 return true 1185 } 1186 } 1187 return false 1188 } 1189 1190 func (tc *txnOperator) TxnOptions() txn.TxnOptions { 1191 return tc.options 1192 } 1193 1194 func (tc *txnOperator) NextSequence() uint64 { 1195 return tc.sequence.Add(1) 1196 } 1197 1198 func (tc *txnOperator) doCostAction( 1199 startAt time.Time, 1200 event EventType, 1201 action func() error, 1202 locked bool) (time.Duration, error) { 1203 if !locked { 1204 tc.mu.RLock() 1205 defer tc.mu.RUnlock() 1206 } 1207 1208 seq := tc.NextSequence() 1209 if startAt == (time.Time{}) { 1210 startAt = time.Now() 1211 } 1212 1213 tc.triggerEventLocked( 1214 newEvent( 1215 event, 1216 tc.mu.txn, 1217 seq, 1218 nil)) 1219 1220 err := action() 1221 cost := time.Since(startAt) 1222 tc.triggerEventLocked( 1223 newCostEvent( 1224 event, 1225 tc.mu.txn, 1226 seq, 1227 err, 1228 time.Since(startAt))) 1229 return cost, err 1230 } 1231 1232 func (tc *txnOperator) EnterRunSql() { 1233 tc.runSqlCounter.addEnter() 1234 } 1235 1236 func (tc *txnOperator) ExitRunSql() { 1237 tc.runSqlCounter.addExit() 1238 } 1239 1240 func (tc *txnOperator) inRunSql() bool { 1241 return tc.runSqlCounter.more() 1242 } 1243 1244 func (tc *txnOperator) inCommit() bool { 1245 return tc.commitCounter.more() 1246 } 1247 1248 func (tc *txnOperator) inRollback() bool { 1249 return tc.rollbackCounter.more() 1250 } 1251 1252 func (tc *txnOperator) counter() string { 1253 return fmt.Sprintf("commit: %s rollback: %s runSql: %s", 1254 tc.commitCounter.String(), 1255 tc.rollbackCounter.String(), 1256 tc.runSqlCounter.String()) 1257 }