github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/go-themis/themis_txn.go (about) 1 package themis 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 8 "github.com/insionng/yougam/libraries/juju/errors" 9 "github.com/insionng/yougam/libraries/ngaut/log" 10 "github.com/insionng/yougam/libraries/pingcap/go-hbase" 11 "github.com/insionng/yougam/libraries/pingcap/go-themis/oracle" 12 ) 13 14 type TxnConfig struct { 15 ConcurrentPrewriteAndCommit bool 16 WaitSecondaryCommit bool 17 TTLInMs uint64 18 MaxRowsInOneTxn int 19 // options below is for debugging and testing 20 brokenPrewriteSecondaryTest bool 21 brokenPrewriteSecondaryAndRollbackTest bool 22 brokenCommitPrimaryTest bool 23 brokenCommitSecondaryTest bool 24 } 25 26 var defaultTxnConf = TxnConfig{ 27 ConcurrentPrewriteAndCommit: true, 28 WaitSecondaryCommit: false, 29 MaxRowsInOneTxn: 50000, 30 TTLInMs: 5 * 1000, // default txn TTL: 5s 31 brokenPrewriteSecondaryTest: false, 32 brokenPrewriteSecondaryAndRollbackTest: false, 33 brokenCommitPrimaryTest: false, 34 brokenCommitSecondaryTest: false, 35 } 36 37 type themisTxn struct { 38 client hbase.HBaseClient 39 rpc *themisRPC 40 lockCleaner LockManager 41 oracle oracle.Oracle 42 mutationCache *columnMutationCache 43 startTs uint64 44 commitTs uint64 45 primaryRow *rowMutation 46 primary *hbase.ColumnCoordinate 47 secondaryRows []*rowMutation 48 secondary []*hbase.ColumnCoordinate 49 primaryRowOffset int 50 singleRowTxn bool 51 secondaryLockBytes []byte 52 conf TxnConfig 53 hooks *txnHook 54 } 55 56 var _ Txn = (*themisTxn)(nil) 57 58 var ( 59 // ErrSimulated is used when maybe rollback occurs error too. 60 ErrSimulated = errors.New("simulated error") 61 maxCleanLockRetryCount = 30 62 pauseTime = 300 * time.Millisecond 63 ) 64 65 func NewTxn(c hbase.HBaseClient, oracle oracle.Oracle) (Txn, error) { 66 return NewTxnWithConf(c, defaultTxnConf, oracle) 67 } 68 69 func NewTxnWithConf(c hbase.HBaseClient, conf TxnConfig, oracle oracle.Oracle) (Txn, error) { 70 var err error 71 txn := &themisTxn{ 72 client: c, 73 mutationCache: newColumnMutationCache(), 74 oracle: oracle, 75 primaryRowOffset: -1, 76 conf: conf, 77 rpc: newThemisRPC(c, oracle, conf), 78 hooks: newHook(), 79 } 80 txn.startTs, err = txn.oracle.GetTimestamp() 81 if err != nil { 82 return nil, errors.Trace(err) 83 } 84 txn.lockCleaner = newThemisLockManager(txn.rpc, c) 85 return txn, nil 86 } 87 88 func (txn *themisTxn) setHook(hooks *txnHook) { 89 txn.hooks = hooks 90 } 91 92 func (txn *themisTxn) Gets(tbl string, gets []*hbase.Get) ([]*hbase.ResultRow, error) { 93 results, err := txn.rpc.themisBatchGet([]byte(tbl), gets, txn.startTs, false) 94 if err != nil { 95 return nil, errors.Trace(err) 96 } 97 var ret []*hbase.ResultRow 98 hasLock := false 99 for _, r := range results { 100 // if this row is locked, try clean lock and get again 101 if isLockResult(r) { 102 hasLock = true 103 err = txn.constructLockAndClean([]byte(tbl), r.SortedColumns) 104 if err != nil { 105 // TODO if it's a conflict error, it means this lock 106 // isn't expired, maybe we can retry or return partial results. 107 return nil, errors.Trace(err) 108 } 109 } 110 // it's OK, because themisBatchGet doesn't return nil value. 111 ret = append(ret, r) 112 } 113 if hasLock { 114 // after we cleaned locks, try to get again. 115 ret, err = txn.rpc.themisBatchGet([]byte(tbl), gets, txn.startTs, true) 116 if err != nil { 117 return nil, errors.Trace(err) 118 } 119 } 120 return ret, nil 121 } 122 123 func (txn *themisTxn) Get(tbl string, g *hbase.Get) (*hbase.ResultRow, error) { 124 r, err := txn.rpc.themisGet([]byte(tbl), g, txn.startTs, false) 125 if err != nil { 126 return nil, errors.Trace(err) 127 } 128 // contain locks, try to clean and get again 129 if r != nil && isLockResult(r) { 130 r, err = txn.tryToCleanLockAndGetAgain([]byte(tbl), g, r.SortedColumns) 131 if err != nil { 132 return nil, errors.Trace(err) 133 } 134 } 135 return r, nil 136 } 137 138 func (txn *themisTxn) Put(tbl string, p *hbase.Put) { 139 // add mutation to buffer 140 for _, e := range getEntriesFromPut(p) { 141 txn.mutationCache.addMutation([]byte(tbl), p.Row, e.Column, e.typ, e.value, false) 142 } 143 } 144 145 func (txn *themisTxn) Delete(tbl string, p *hbase.Delete) error { 146 entries, err := getEntriesFromDel(p) 147 if err != nil { 148 return errors.Trace(err) 149 } 150 for _, e := range entries { 151 txn.mutationCache.addMutation([]byte(tbl), p.Row, e.Column, e.typ, e.value, false) 152 } 153 return nil 154 } 155 156 func (txn *themisTxn) Commit() error { 157 if txn.mutationCache.getMutationCount() == 0 { 158 return nil 159 } 160 if txn.mutationCache.getRowCount() > txn.conf.MaxRowsInOneTxn { 161 return ErrTooManyRows 162 } 163 164 txn.selectPrimaryAndSecondaries() 165 err := txn.prewritePrimary() 166 if err != nil { 167 // no need to check wrong region here, hbase client will retry when 168 // occurs single row NotInRegion error. 169 log.Error(errors.ErrorStack(err)) 170 // it's safe to retry, because this transaction is not committed. 171 return ErrRetryable 172 } 173 174 err = txn.prewriteSecondary() 175 if err != nil { 176 if isWrongRegionErr(err) { 177 log.Warn("region info outdated") 178 // reset hbase client buffered region info 179 txn.client.CleanAllRegionCache() 180 } 181 return ErrRetryable 182 } 183 184 txn.commitTs, err = txn.oracle.GetTimestamp() 185 if err != nil { 186 log.Error(errors.ErrorStack(err)) 187 return ErrRetryable 188 } 189 err = txn.commitPrimary() 190 if err != nil { 191 // commit primary error, rollback 192 log.Error("commit primary row failed", txn.startTs, err) 193 txn.rollbackRow(txn.primaryRow.tbl, txn.primaryRow) 194 txn.rollbackSecondaryRow(len(txn.secondaryRows) - 1) 195 return ErrRetryable 196 } 197 txn.commitSecondary() 198 log.Debug("themis txn commit successfully", txn.startTs, txn.commitTs) 199 return nil 200 } 201 202 func (txn *themisTxn) commitSecondary() { 203 if bypass, _, _ := txn.hooks.beforeCommitSecondary(txn, nil); !bypass { 204 return 205 } 206 if txn.conf.brokenCommitSecondaryTest { 207 txn.brokenCommitSecondary() 208 return 209 } 210 if txn.conf.ConcurrentPrewriteAndCommit { 211 txn.batchCommitSecondary(txn.conf.WaitSecondaryCommit) 212 } else { 213 txn.commitSecondarySync() 214 } 215 } 216 217 func (txn *themisTxn) commitSecondarySync() { 218 for _, r := range txn.secondaryRows { 219 err := txn.rpc.commitSecondaryRow(r.tbl, r.row, r.mutationList(false), txn.startTs, txn.commitTs) 220 if err != nil { 221 // fail of secondary commit will not stop the commits of next 222 // secondaries 223 log.Warning(err) 224 } 225 } 226 } 227 228 func (txn *themisTxn) batchCommitSecondary(wait bool) error { 229 //will batch commit all rows in a region 230 rsRowMap, err := txn.groupByRegion() 231 if err != nil { 232 return errors.Trace(err) 233 } 234 235 wg := sync.WaitGroup{} 236 for _, regionRowMap := range rsRowMap { 237 wg.Add(1) 238 _, firstRowM := getFirstEntity(regionRowMap) 239 go func(cli *themisRPC, tbl string, rMap map[string]*rowMutation, startTs, commitTs uint64) { 240 defer wg.Done() 241 err := cli.batchCommitSecondaryRows([]byte(tbl), rMap, startTs, commitTs) 242 if err != nil { 243 // fail of secondary commit will not stop the commits of next 244 // secondaries 245 if isWrongRegionErr(err) { 246 txn.client.CleanAllRegionCache() 247 log.Warn("region info outdated when committing secondary rows, don't panic") 248 } 249 } 250 }(txn.rpc, string(firstRowM.tbl), regionRowMap, txn.startTs, txn.commitTs) 251 } 252 if wait { 253 wg.Wait() 254 } 255 return nil 256 } 257 258 func (txn *themisTxn) groupByRegion() (map[string]map[string]*rowMutation, error) { 259 rsRowMap := make(map[string]map[string]*rowMutation) 260 for _, rm := range txn.secondaryRows { 261 region, err := txn.client.LocateRegion(rm.tbl, rm.row, true) 262 if err != nil { 263 return nil, errors.Trace(err) 264 } 265 key := getBatchGroupKey(region, string(rm.tbl)) 266 if _, exists := rsRowMap[key]; !exists { 267 rsRowMap[key] = map[string]*rowMutation{} 268 } 269 rsRowMap[key][string(rm.row)] = rm 270 } 271 return rsRowMap, nil 272 } 273 274 func (txn *themisTxn) commitPrimary() error { 275 if txn.conf.brokenCommitPrimaryTest { 276 return txn.brokenCommitPrimary() 277 } 278 return txn.rpc.commitRow(txn.primary.Table, txn.primary.Row, 279 txn.primaryRow.mutationList(false), 280 txn.startTs, txn.commitTs, txn.primaryRowOffset) 281 } 282 283 func (txn *themisTxn) selectPrimaryAndSecondaries() { 284 txn.secondary = nil 285 for tblName, rowMutations := range txn.mutationCache.mutations { 286 for _, rowMutation := range rowMutations { 287 row := rowMutation.row 288 findPrimaryInRow := false 289 for i, mutation := range rowMutation.mutationList(true) { 290 colcord := hbase.NewColumnCoordinate([]byte(tblName), row, mutation.Family, mutation.Qual) 291 // set the first column as primary if primary is not set by user 292 if txn.primaryRowOffset == -1 && 293 (txn.primary == nil || txn.primary.Equal(colcord)) { 294 txn.primary = colcord 295 txn.primaryRowOffset = i 296 txn.primaryRow = rowMutation 297 findPrimaryInRow = true 298 } else { 299 txn.secondary = append(txn.secondary, colcord) 300 } 301 } 302 if !findPrimaryInRow { 303 txn.secondaryRows = append(txn.secondaryRows, rowMutation) 304 } 305 } 306 } 307 308 // hook for test 309 if bypass, _, _ := txn.hooks.afterChoosePrimaryAndSecondary(txn, nil); !bypass { 310 return 311 } 312 313 if len(txn.secondaryRows) == 0 { 314 txn.singleRowTxn = true 315 } 316 // construct secondary lock 317 secondaryLock := txn.constructSecondaryLock(hbase.TypePut) 318 if secondaryLock != nil { 319 txn.secondaryLockBytes = secondaryLock.Encode() 320 } else { 321 txn.secondaryLockBytes = nil 322 } 323 } 324 325 func (txn *themisTxn) constructSecondaryLock(typ hbase.Type) *themisSecondaryLock { 326 if txn.primaryRow.getSize() <= 1 && len(txn.secondaryRows) == 0 { 327 return nil 328 } 329 l := newThemisSecondaryLock() 330 l.primaryCoordinate = txn.primary 331 l.ts = txn.startTs 332 // TODO set client addr 333 return l 334 } 335 336 func (txn *themisTxn) constructPrimaryLock() *themisPrimaryLock { 337 l := newThemisPrimaryLock() 338 l.typ = txn.primaryRow.getType(txn.primary.Column) 339 l.ts = txn.startTs 340 for _, c := range txn.secondary { 341 l.addSecondary(c, txn.mutationCache.getMutation(c).typ) 342 } 343 return l 344 } 345 346 func (txn *themisTxn) constructLockAndClean(tbl []byte, lockKvs []*hbase.Kv) error { 347 locks, err := getLocksFromResults([]byte(tbl), lockKvs, txn.rpc) 348 if err != nil { 349 return errors.Trace(err) 350 } 351 for _, lock := range locks { 352 err := txn.cleanLockWithRetry(lock) 353 if err != nil { 354 return errors.Trace(err) 355 } 356 } 357 return nil 358 } 359 360 func (txn *themisTxn) tryToCleanLockAndGetAgain(tbl []byte, g *hbase.Get, lockKvs []*hbase.Kv) (*hbase.ResultRow, error) { 361 // try to clean locks 362 err := txn.constructLockAndClean(tbl, lockKvs) 363 if err != nil { 364 return nil, errors.Trace(err) 365 } 366 // get again, ignore lock 367 r, err := txn.rpc.themisGet([]byte(tbl), g, txn.startTs, true) 368 if err != nil { 369 return nil, errors.Trace(err) 370 } 371 return r, nil 372 } 373 374 func (txn *themisTxn) commitSecondaryAndCleanLock(lock *themisSecondaryLock, commitTs uint64) error { 375 cc := lock.Coordinate() 376 mutation := &columnMutation{ 377 Column: &cc.Column, 378 mutationValuePair: &mutationValuePair{ 379 typ: lock.typ, 380 }, 381 } 382 err := txn.rpc.commitSecondaryRow(cc.Table, cc.Row, 383 []*columnMutation{mutation}, lock.Timestamp(), commitTs) 384 if err != nil { 385 return errors.Trace(err) 386 } 387 return nil 388 } 389 390 func (txn *themisTxn) cleanLockWithRetry(lock Lock) error { 391 for i := 0; i < maxCleanLockRetryCount; i++ { 392 if exists, err := txn.lockCleaner.IsLockExists(lock.Coordinate(), 0, lock.Timestamp()); err != nil || !exists { 393 return errors.Trace(err) 394 } 395 log.Warnf("lock exists txn: %v lock-txn: %v row: %q", txn.startTs, lock.Timestamp(), lock.Coordinate().Row) 396 // try clean lock 397 err := txn.tryToCleanLock(lock) 398 if errorEqual(err, ErrLockNotExpired) { 399 log.Warn("sleep a while, and retry clean lock", txn.startTs) 400 // TODO(dongxu) use cleverer retry sleep time interval 401 time.Sleep(pauseTime) 402 continue 403 } else if err != nil { 404 return errors.Trace(err) 405 } 406 // lock cleaned successfully 407 return nil 408 } 409 return ErrCleanLockFailed 410 } 411 412 func (txn *themisTxn) tryToCleanLock(lock Lock) error { 413 // if it's secondary lock, first we'll check if its primary lock has been released. 414 if lock.Role() == RoleSecondary { 415 // get primary lock 416 pl := lock.Primary() 417 // check primary lock is exists 418 exists, err := txn.lockCleaner.IsLockExists(pl.Coordinate(), 0, pl.Timestamp()) 419 if err != nil { 420 return errors.Trace(err) 421 } 422 if !exists { 423 // primary row is committed, commit this row 424 cc := pl.Coordinate() 425 commitTs, err := txn.lockCleaner.GetCommitTimestamp(cc, pl.Timestamp()) 426 if err != nil { 427 return errors.Trace(err) 428 } 429 if commitTs > 0 { 430 // if this transction has been committed 431 log.Info("txn has been committed, ts:", commitTs, "prewriteTs:", pl.Timestamp()) 432 // commit secondary rows 433 err := txn.commitSecondaryAndCleanLock(lock.(*themisSecondaryLock), commitTs) 434 if err != nil { 435 return errors.Trace(err) 436 } 437 return nil 438 } 439 } 440 } 441 expired, err := txn.rpc.checkAndSetLockIsExpired(lock) 442 if err != nil { 443 return errors.Trace(err) 444 } 445 // only clean expired lock 446 if expired { 447 // try to clean primary lock 448 pl := lock.Primary() 449 commitTs, cleanedLock, err := txn.lockCleaner.CleanLock(pl.Coordinate(), pl.Timestamp()) 450 if err != nil { 451 return errors.Trace(err) 452 } 453 if cleanedLock != nil { 454 pl = cleanedLock 455 } 456 log.Info("try clean secondary locks", pl.Timestamp()) 457 // clean secondary locks 458 // erase lock and data if commitTs is 0; otherwise, commit it. 459 for k, v := range pl.(*themisPrimaryLock).secondaries { 460 cc := &hbase.ColumnCoordinate{} 461 if err = cc.ParseFromString(k); err != nil { 462 return errors.Trace(err) 463 } 464 if commitTs == 0 { 465 // commitTs == 0, means clean primary lock successfully 466 // expire trx havn't committed yet, we must delete lock and 467 // dirty data 468 err = txn.lockCleaner.EraseLockAndData(cc, pl.Timestamp()) 469 if err != nil { 470 return errors.Trace(err) 471 } 472 } else { 473 // primary row is committed, so we must commit other 474 // secondary rows 475 mutation := &columnMutation{ 476 Column: &cc.Column, 477 mutationValuePair: &mutationValuePair{ 478 typ: v, 479 }, 480 } 481 err = txn.rpc.commitSecondaryRow(cc.Table, cc.Row, 482 []*columnMutation{mutation}, pl.Timestamp(), commitTs) 483 if err != nil { 484 return errors.Trace(err) 485 } 486 } 487 } 488 } else { 489 return ErrLockNotExpired 490 } 491 return nil 492 } 493 494 func (txn *themisTxn) batchPrewriteSecondaryRowsWithLockClean(tbl []byte, rowMs map[string]*rowMutation) error { 495 locks, err := txn.batchPrewriteSecondaryRows(tbl, rowMs) 496 if err != nil { 497 return errors.Trace(err) 498 } 499 500 // lock clean 501 if locks != nil && len(locks) > 0 { 502 // hook for test 503 if bypass, _, err := txn.hooks.onSecondaryOccursLock(txn, locks); !bypass { 504 return errors.Trace(err) 505 } 506 // try one more time after clean lock successfully 507 for _, lock := range locks { 508 err = txn.cleanLockWithRetry(lock) 509 if err != nil { 510 return errors.Trace(err) 511 } 512 513 // prewrite all secondary rows 514 locks, err = txn.batchPrewriteSecondaryRows(tbl, rowMs) 515 if err != nil { 516 return errors.Trace(err) 517 } 518 if len(locks) > 0 { 519 for _, l := range locks { 520 log.Errorf("can't clean lock, column:%q; conflict lock: %+v, lock ts: %d", l.Coordinate(), l, l.Timestamp()) 521 } 522 return ErrRetryable 523 } 524 } 525 } 526 return nil 527 } 528 529 func (txn *themisTxn) prewriteRowWithLockClean(tbl []byte, mutation *rowMutation, containPrimary bool) error { 530 lock, err := txn.prewriteRow(tbl, mutation, containPrimary) 531 if err != nil { 532 return errors.Trace(err) 533 } 534 // lock clean 535 if lock != nil { 536 // hook for test 537 if bypass, _, err := txn.hooks.beforePrewriteLockClean(txn, lock); !bypass { 538 return errors.Trace(err) 539 } 540 err = txn.cleanLockWithRetry(lock) 541 if err != nil { 542 return errors.Trace(err) 543 } 544 // try one more time after clean lock successfully 545 lock, err = txn.prewriteRow(tbl, mutation, containPrimary) 546 if err != nil { 547 return errors.Trace(err) 548 } 549 if lock != nil { 550 log.Errorf("can't clean lock, column:%q; conflict lock: %+v, lock ts: %d", lock.Coordinate(), lock, lock.Timestamp()) 551 return ErrRetryable 552 } 553 } 554 return nil 555 } 556 557 func (txn *themisTxn) batchPrewriteSecondaryRows(tbl []byte, rowMs map[string]*rowMutation) (map[string]Lock, error) { 558 return txn.rpc.batchPrewriteSecondaryRows(tbl, rowMs, txn.startTs, txn.secondaryLockBytes) 559 } 560 561 func (txn *themisTxn) prewriteRow(tbl []byte, mutation *rowMutation, containPrimary bool) (Lock, error) { 562 // hook for test 563 if bypass, ret, err := txn.hooks.onPrewriteRow(txn, []interface{}{mutation, containPrimary}); !bypass { 564 return ret.(Lock), errors.Trace(err) 565 } 566 if containPrimary { 567 // try to get lock 568 return txn.rpc.prewriteRow(tbl, mutation.row, 569 mutation.mutationList(true), 570 txn.startTs, 571 txn.constructPrimaryLock().Encode(), 572 txn.secondaryLockBytes, txn.primaryRowOffset) 573 } 574 return txn.rpc.prewriteSecondaryRow(tbl, mutation.row, 575 mutation.mutationList(true), 576 txn.startTs, 577 txn.secondaryLockBytes) 578 } 579 580 func (txn *themisTxn) prewritePrimary() error { 581 // hook for test 582 if bypass, _, err := txn.hooks.beforePrewritePrimary(txn, nil); !bypass { 583 return err 584 } 585 err := txn.prewriteRowWithLockClean(txn.primary.Table, txn.primaryRow, true) 586 if err != nil { 587 log.Debugf("prewrite primary %v %q failed: %v", txn.startTs, txn.primaryRow.row, err.Error()) 588 return errors.Trace(err) 589 } 590 log.Debugf("prewrite primary %v %q successfully", txn.startTs, txn.primaryRow.row) 591 return nil 592 } 593 594 func (txn *themisTxn) prewriteSecondary() error { 595 // hook for test 596 if bypass, _, err := txn.hooks.beforePrewriteSecondary(txn, nil); !bypass { 597 return err 598 } 599 if txn.conf.brokenPrewriteSecondaryTest { 600 return txn.brokenPrewriteSecondary() 601 } 602 if txn.conf.ConcurrentPrewriteAndCommit { 603 return txn.batchPrewriteSecondaries() 604 } 605 return txn.prewriteSecondarySync() 606 } 607 608 func (txn *themisTxn) prewriteSecondarySync() error { 609 for i, mu := range txn.secondaryRows { 610 err := txn.prewriteRowWithLockClean(mu.tbl, mu, false) 611 if err != nil { 612 // rollback 613 txn.rollbackRow(txn.primaryRow.tbl, txn.primaryRow) 614 txn.rollbackSecondaryRow(i) 615 return errors.Trace(err) 616 } 617 } 618 return nil 619 } 620 621 // just for test 622 func (txn *themisTxn) brokenCommitPrimary() error { 623 // do nothing 624 log.Warn("Simulating primary commit failed") 625 return nil 626 } 627 628 // just for test 629 func (txn *themisTxn) brokenCommitSecondary() { 630 // do nothing 631 log.Warn("Simulating secondary commit failed") 632 } 633 634 func (txn *themisTxn) brokenPrewriteSecondary() error { 635 log.Warn("Simulating prewrite secondary failed") 636 for i, rm := range txn.secondaryRows { 637 if i == len(txn.secondary)-1 { 638 if !txn.conf.brokenPrewriteSecondaryAndRollbackTest { 639 // simulating prewrite failed, need rollback 640 txn.rollbackRow(txn.primaryRow.tbl, txn.primaryRow) 641 txn.rollbackSecondaryRow(i) 642 } 643 // maybe rollback occurs error too 644 return ErrSimulated 645 } 646 txn.prewriteRowWithLockClean(rm.tbl, rm, false) 647 } 648 return nil 649 } 650 651 func (txn *themisTxn) batchPrewriteSecondaries() error { 652 wg := sync.WaitGroup{} 653 //will batch prewrite all rows in a region 654 rsRowMap, err := txn.groupByRegion() 655 if err != nil { 656 return errors.Trace(err) 657 } 658 659 errChan := make(chan error, len(rsRowMap)) 660 defer close(errChan) 661 successChan := make(chan map[string]*rowMutation, len(rsRowMap)) 662 defer close(successChan) 663 664 for _, regionRowMap := range rsRowMap { 665 wg.Add(1) 666 _, firstRowM := getFirstEntity(regionRowMap) 667 go func(tbl []byte, rMap map[string]*rowMutation) { 668 defer wg.Done() 669 err := txn.batchPrewriteSecondaryRowsWithLockClean(tbl, rMap) 670 if err != nil { 671 errChan <- err 672 } else { 673 successChan <- rMap 674 } 675 }(firstRowM.tbl, regionRowMap) 676 } 677 wg.Wait() 678 679 if len(errChan) != 0 { 680 // occur error, clean success prewrite mutations 681 log.Warnf("batch prewrite secondary rows error, rolling back %d %d", len(successChan), txn.startTs) 682 txn.rollbackRow(txn.primaryRow.tbl, txn.primaryRow) 683 L: 684 for { 685 select { 686 case succMutMap := <-successChan: 687 { 688 for _, rowMut := range succMutMap { 689 txn.rollbackRow(rowMut.tbl, rowMut) 690 } 691 } 692 default: 693 break L 694 } 695 } 696 697 err := <-errChan 698 if err != nil { 699 log.Error("batch prewrite secondary rows error, txn:", txn.startTs, err) 700 } 701 return errors.Trace(err) 702 } 703 return nil 704 } 705 706 func getFirstEntity(rowMap map[string]*rowMutation) (string, *rowMutation) { 707 for row, rowM := range rowMap { 708 return row, rowM 709 } 710 return "", nil 711 } 712 713 func getBatchGroupKey(rInfo *hbase.RegionInfo, tblName string) string { 714 return rInfo.Server + "_" + rInfo.Name 715 } 716 717 func (txn *themisTxn) rollbackRow(tbl []byte, mutation *rowMutation) error { 718 l := fmt.Sprintf("\nrolling back %q %d {\n", mutation.row, txn.startTs) 719 for _, v := range mutation.getColumns() { 720 l += fmt.Sprintf("\t%s:%s\n", string(v.Family), string(v.Qual)) 721 } 722 l += "}\n" 723 log.Warn(l) 724 for _, col := range mutation.getColumns() { 725 cc := &hbase.ColumnCoordinate{ 726 Table: tbl, 727 Row: mutation.row, 728 Column: col, 729 } 730 err := txn.lockCleaner.EraseLockAndData(cc, txn.startTs) 731 if err != nil { 732 return errors.Trace(err) 733 } 734 } 735 return nil 736 } 737 738 func (txn *themisTxn) rollbackSecondaryRow(successIndex int) error { 739 for i := successIndex; i >= 0; i-- { 740 r := txn.secondaryRows[i] 741 err := txn.rollbackRow(r.tbl, r) 742 if err != nil { 743 return errors.Trace(err) 744 } 745 } 746 return nil 747 } 748 749 func (txn *themisTxn) GetScanner(tbl []byte, startKey, endKey []byte, batchSize int) *ThemisScanner { 750 scanner := newThemisScanner(tbl, txn, batchSize, txn.client) 751 if startKey != nil { 752 scanner.setStartRow(startKey) 753 } 754 if endKey != nil { 755 scanner.setStopRow(endKey) 756 } 757 return scanner 758 } 759 760 func (txn *themisTxn) Release() { 761 txn.primary = nil 762 txn.primaryRow = nil 763 txn.secondary = nil 764 txn.secondaryRows = nil 765 txn.startTs = 0 766 txn.commitTs = 0 767 } 768 769 func (txn *themisTxn) String() string { 770 return fmt.Sprintf("%d", txn.startTs) 771 } 772 773 func (txn *themisTxn) GetCommitTS() uint64 { 774 return txn.commitTs 775 } 776 777 func (txn *themisTxn) GetStartTS() uint64 { 778 return txn.startTs 779 } 780 781 func (txn *themisTxn) LockRow(tbl string, rowkey []byte) error { 782 g := hbase.NewGet(rowkey) 783 r, err := txn.Get(tbl, g) 784 if err != nil { 785 log.Warnf("get row error, table:%s, row:%q, error:%v", tbl, rowkey, err) 786 return errors.Trace(err) 787 } 788 if r == nil { 789 log.Warnf("has not data to lock, table:%s, row:%q", tbl, rowkey) 790 return nil 791 } 792 for _, v := range r.Columns { 793 txn.mutationCache.addMutation([]byte(tbl), rowkey, &v.Column, hbase.TypeMinimum, nil, true) 794 } 795 return nil 796 }