github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/go-themis/txn_test.go (about) 1 package themis 2 3 import ( 4 "fmt" 5 "strconv" 6 "testing" 7 "time" 8 9 "github.com/insionng/yougam/libraries/ngaut/log" 10 . "github.com/insionng/yougam/libraries/pingcap/check" 11 "github.com/insionng/yougam/libraries/pingcap/go-hbase" 12 ) 13 14 type TransactionTestSuit struct { 15 cli hbase.HBaseClient 16 } 17 18 var _ = Suite(&TransactionTestSuit{}) 19 20 func Test(t *testing.T) { TestingT(t) } 21 22 func (s *TransactionTestSuit) SetUpSuite(c *C) { 23 var err error 24 s.cli, err = createHBaseClient() 25 c.Assert(err, Equals, nil) 26 27 log.Warn("new test, reset tables") 28 err = createNewTableAndDropOldTable(s.cli, themisTestTableName, string(cf), nil) 29 c.Assert(err, IsNil) 30 } 31 32 func (s *TransactionTestSuit) SetUpTest(c *C) { 33 } 34 35 func getTestRowKey(c *C) []byte { 36 return []byte("test_row_" + c.TestName()) 37 } 38 39 func (s *TransactionTestSuit) TestAsyncCommit(c *C) { 40 conf := defaultTxnConf 41 conf.brokenCommitSecondaryTest = true 42 43 tx := newTxn(s.cli, conf) 44 // simulating broken commit 45 for i := 0; i < 10; i++ { 46 p := hbase.NewPut([]byte(fmt.Sprintf("test_%d", i))) 47 p.AddValue(cf, q, []byte(fmt.Sprintf("%d", tx.(*themisTxn).GetStartTS()))) 48 tx.Put(themisTestTableName, p) 49 } 50 err := tx.Commit() 51 c.Assert(err, Equals, nil) 52 53 // wait until lock expired. 54 log.Warn("Wait for lock expired. Sleep...") 55 tick := 6 56 for tick > 0 { 57 time.Sleep(1 * time.Second) 58 tick-- 59 log.Infof("remain %ds...", tick) 60 } 61 62 log.Warn("Try commit again") 63 // new transction will not see lock 64 for { 65 tx = newTxn(s.cli, defaultTxnConf) 66 for i := 0; i < 5; i++ { 67 p := hbase.NewPut([]byte(fmt.Sprintf("test_%d", i))) 68 p.AddValue(cf, q, []byte(fmt.Sprintf("%d", tx.(*themisTxn).GetStartTS()))) 69 tx.Put(themisTestTableName, p) 70 } 71 err = tx.Commit() 72 if err == nil || !errorEqual(err, ErrRetryable) { 73 break 74 } 75 time.Sleep(100 * time.Millisecond) 76 } 77 c.Assert(err, Equals, nil) 78 79 for { 80 tx = newTxn(s.cli, defaultTxnConf) 81 for i := 5; i < 10; i++ { 82 p := hbase.NewPut([]byte(fmt.Sprintf("test_%d", i))) 83 p.AddValue(cf, q, []byte(fmt.Sprintf("%d", tx.(*themisTxn).GetStartTS()))) 84 tx.Put(themisTestTableName, p) 85 } 86 err = tx.Commit() 87 if err == nil || !errorEqual(err, ErrRetryable) { 88 break 89 } 90 time.Sleep(100 * time.Millisecond) 91 } 92 c.Assert(err, Equals, nil) 93 } 94 95 func (s *TransactionTestSuit) TestBrokenPrewriteSecondary(c *C) { 96 tx := newTxn(s.cli, defaultTxnConf) 97 ts := tx.(*themisTxn).GetStartTS() 98 // simulating broken commit 99 for i := 0; i < 10; i++ { 100 p := hbase.NewPut([]byte(fmt.Sprintf("test_%d", i))) 101 p.AddValue(cf, q, []byte(fmt.Sprintf("%d", ts))) 102 tx.Put(themisTestTableName, p) 103 } 104 err := tx.Commit() 105 c.Assert(err, IsNil) 106 107 // TODO: check rallback & cleanup locks 108 conf := defaultTxnConf 109 conf.brokenPrewriteSecondaryTest = true 110 111 tx = newTxn(s.cli, conf) 112 ts = tx.GetStartTS() 113 // simulating broken commit 114 for i := 0; i < 10; i++ { 115 p := hbase.NewPut([]byte(fmt.Sprintf("test_%d", i))) 116 p.AddValue(cf, q, []byte(fmt.Sprintf("%d", ts))) 117 tx.Put(themisTestTableName, p) 118 } 119 err = tx.Commit() 120 c.Assert(err, NotNil) 121 122 // check if locks are cleaned successfully 123 tx = newTxn(s.cli, defaultTxnConf) 124 for i := 0; i < 10; i++ { 125 g := hbase.NewGet([]byte(fmt.Sprintf("test_%d", i))) 126 r, err := tx.Get(themisTestTableName, g) 127 c.Assert(err, Equals, nil) 128 c.Assert(r == nil || string(r.SortedColumns[0].Value) != fmt.Sprintf("%d", ts), Equals, true) 129 } 130 } 131 132 func (s *TransactionTestSuit) TestPrimaryLockTimeout(c *C) { 133 // TODO: check if lock can be cleaned up when secondary prewrite failed and 134 // rollback is also failed 135 conf := defaultTxnConf 136 conf.brokenPrewriteSecondaryTest = true 137 conf.brokenPrewriteSecondaryAndRollbackTest = true 138 tx := newTxn(s.cli, conf) 139 ts := tx.GetStartTS() 140 // simulating broken commit 141 for i := 0; i < 2; i++ { 142 p := hbase.NewPut([]byte(fmt.Sprintf("test_%d", i))) 143 p.AddValue(cf, q, []byte(fmt.Sprintf("%d", ts))) 144 tx.Put(themisTestTableName, p) 145 } 146 err := tx.Commit() 147 c.Assert(err, NotNil) 148 log.Error(err) 149 150 // wait until lock expired. 151 log.Warn("Wait for lock expired. Sleep...") 152 tick := 6 153 for tick > 0 { 154 time.Sleep(1 * time.Second) 155 tick-- 156 log.Infof("remain %ds...", tick) 157 } 158 159 // check if locks are cleaned successfully 160 tx = newTxn(s.cli, defaultTxnConf) 161 for i := 0; i < 2; i++ { 162 g := hbase.NewGet([]byte(fmt.Sprintf("test_%d", i))) 163 r, err := tx.Get(themisTestTableName, g) 164 c.Assert(err, Equals, nil) 165 // this commit must rollback 166 c.Assert(r == nil || string(r.SortedColumns[0].Value) != fmt.Sprintf("%d", ts), Equals, true) 167 } 168 } 169 170 func checkCommitSuccess(s *TransactionTestSuit, c *C, row []byte) { 171 tx := newTxn(s.cli, defaultTxnConf) 172 colMap := make(map[string]string) 173 colMap["#p:"+string(cf)+"#q"] = "" 174 colMap[string(cf)+":q"] = "" 175 r, err := tx.(*themisTxn).client.Get(themisTestTableName, hbase.NewGet(row)) 176 c.Assert(err, Equals, nil) 177 c.Assert(2, Equals, len(r.Columns)) 178 for _, v := range r.Columns { 179 _, exist := colMap[string(v.Family)+":"+string(v.Qual)] 180 c.Assert(exist, Equals, true) 181 } 182 } 183 184 func (s *TransactionTestSuit) TestLockRow(c *C) { 185 tx := newTxn(s.cli, defaultTxnConf) 186 row := []byte("lockRow") 187 put := hbase.NewPut(row) 188 put.AddValue(cf, q, []byte("v")) 189 tx.Put(themisTestTableName, put) 190 tx.Commit() 191 192 checkCommitSuccess(s, c, row) 193 194 tx = newTxn(s.cli, defaultTxnConf) 195 err := tx.LockRow(themisTestTableName, row) 196 c.Assert(err, Equals, nil) 197 198 tx.(*themisTxn).selectPrimaryAndSecondaries() 199 err = tx.(*themisTxn).prewritePrimary() 200 c.Assert(err, Equals, nil) 201 colMap := make(map[string]string) 202 colMap["#p:"+string(cf)+"#q"] = "" 203 colMap[string(cf)+":q"] = "" 204 colMap["L:"+string(cf)+"#q"] = "" 205 var r *hbase.ResultRow 206 r, err = tx.(*themisTxn).client.Get(themisTestTableName, hbase.NewGet(row)) 207 c.Assert(err, Equals, nil) 208 c.Assert(3, Equals, len(r.Columns)) 209 for _, v := range r.Columns { 210 _, exist := colMap[string(v.Family)+":"+string(v.Qual)] 211 c.Assert(exist, Equals, true) 212 } 213 tx.(*themisTxn).commitTs = tx.GetStartTS() + 1 214 tx.(*themisTxn).commitPrimary() 215 checkCommitSuccess(s, c, row) 216 } 217 218 func (s *TransactionTestSuit) TestBatchGet(c *C) { 219 batchGetTestTbl := "batch_get_test" 220 err := createNewTableAndDropOldTable(s.cli, batchGetTestTbl, string(cf), [][]byte{ 221 // split in middle 222 []byte("batch_test_5"), 223 }) 224 defer dropTable(s.cli, batchGetTestTbl) 225 // prepare data 226 tx := newTxn(s.cli, defaultTxnConf) 227 for i := 0; i < 10; i++ { 228 p := hbase.NewPut([]byte(fmt.Sprintf("batch_test_%d", i))).AddValue(cf, q, []byte("v")) 229 tx.Put(batchGetTestTbl, p) 230 } 231 err = tx.Commit() 232 c.Assert(err, IsNil) 233 234 // batch get 235 var gets []*hbase.Get 236 for i := 0; i < 10; i++ { 237 g := hbase.NewGet([]byte(fmt.Sprintf("batch_test_%d", i))).AddColumn(cf, q) 238 gets = append(gets, g) 239 } 240 for i := 5; i < 10; i++ { 241 g := hbase.NewGet([]byte(fmt.Sprintf("batch_test_no_such_row_%d", i))).AddColumn(cf, q) 242 gets = append(gets, g) 243 } 244 tx = newTxn(s.cli, defaultTxnConf) 245 _, err = tx.Gets(batchGetTestTbl, gets) 246 c.Assert(isWrongRegionErr(err), Equals, true) 247 248 gets = nil 249 for i := 0; i < 5; i++ { 250 g := hbase.NewGet([]byte(fmt.Sprintf("batch_test_%d", i))).AddColumn(cf, q) 251 gets = append(gets, g) 252 } 253 tx = newTxn(s.cli, defaultTxnConf) 254 rs, err := tx.Gets(batchGetTestTbl, gets) 255 c.Assert(err, IsNil) 256 c.Assert(len(rs), Equals, 5) 257 } 258 259 func (s *TransactionTestSuit) TestBatchGetWithLocks(c *C) { 260 // simulating locks 261 conf := defaultTxnConf 262 conf.brokenCommitSecondaryTest = true 263 264 tx := newTxn(s.cli, conf) 265 ts := tx.GetStartTS() 266 // simulating broken commit 267 for i := 0; i < 10; i++ { 268 p := hbase.NewPut([]byte(fmt.Sprintf("batch_test_with_lock_%d", i))) 269 p.AddValue(cf, q, []byte(fmt.Sprintf("%d", ts))) 270 tx.Put(themisTestTableName, p) 271 } 272 tx.Commit() 273 274 tx = newTxn(s.cli, defaultTxnConf) 275 276 var gets []*hbase.Get 277 for i := 0; i < 10; i++ { 278 g := hbase.NewGet([]byte(fmt.Sprintf("batch_test_with_lock_%d", i))).AddColumn(cf, q) 279 gets = append(gets, g) 280 } 281 rs, err := tx.Gets(themisTestTableName, gets) 282 c.Assert(err, IsNil) 283 // we had already cleaned secondary locks 284 c.Assert(len(rs), Equals, 10) 285 } 286 287 func (s *TransactionTestSuit) TestAsyncSecondaryCommit(c *C) { 288 conf := defaultTxnConf 289 conf.brokenCommitSecondaryTest = true 290 tx := newTxn(s.cli, conf) 291 for i := 0; i < 10; i++ { 292 p := hbase.NewPut([]byte(fmt.Sprintf("async_commit_test_%d", i))).AddValue(cf, q, []byte(fmt.Sprintf("%d", tx.GetStartTS()))) 293 tx.Put(themisTestTableName, p) 294 } 295 err := tx.Commit() 296 c.Assert(err, IsNil) 297 298 tx = newTxn(s.cli, conf) 299 for i := 0; i < 10; i++ { 300 g := hbase.NewGet([]byte(fmt.Sprintf("async_commit_test_%d", i))) 301 rs, err := tx.Get(themisTestTableName, g) 302 c.Assert(err, IsNil) 303 c.Assert(len(rs.SortedColumns), Greater, 0) 304 } 305 } 306 307 func (s *TransactionTestSuit) TestTTL(c *C) { 308 conf := defaultTxnConf 309 conf.brokenCommitPrimaryTest = true 310 tx := newTxn(s.cli, conf) 311 p := hbase.NewPut(getTestRowKey(c)).AddValue(cf, q, []byte("val")) 312 tx.Put(themisTestTableName, p) 313 tx.Commit() 314 315 startTs := time.Now() 316 conf = defaultTxnConf 317 conf.TTLInMs = 1000 318 tx = newTxn(s.cli, conf) 319 rs, err := tx.Get(themisTestTableName, hbase.NewGet(getTestRowKey(c)).AddColumn(cf, q)) 320 c.Assert(time.Since(startTs).Seconds(), Greater, float64(1)) 321 c.Assert(time.Since(startTs).Seconds(), Less, float64(1.5)) 322 // transction timeout, alreay rolled back. 323 c.Assert(rs, IsNil) 324 c.Assert(err, IsNil) 325 tx.Commit() 326 } 327 328 type mockOracle struct { 329 tick uint64 330 } 331 332 func (o *mockOracle) GetTimestamp() (uint64, error) { 333 return o.tick, nil 334 } 335 336 func (o *mockOracle) IsExpired(beginMs uint64, TTL uint64) bool { 337 return false 338 } 339 340 func (s *TransactionTestSuit) TestPhantomRead(c *C) { 341 o := &mockOracle{} 342 343 conf := defaultTxnConf 344 conf.brokenCommitPrimaryTest = true 345 o.tick = 1 346 tx, _ := NewTxnWithConf(s.cli, conf, o) 347 p := hbase.NewPut(getTestRowKey(c)).AddValue(cf, q, []byte("val")) 348 o.tick = 3 349 tx.Put(themisTestTableName, p) 350 tx.Commit() 351 352 o.tick = 2 353 tx, _ = NewTxn(s.cli, o) 354 rs, err := tx.Get(themisTestTableName, hbase.NewGet(getTestRowKey(c)).AddColumn(cf, q)) 355 c.Assert(err, NotNil) 356 c.Assert(rs, IsNil) 357 } 358 359 func (s *TransactionTestSuit) TestExceedMaxRows(c *C) { 360 conf := defaultTxnConf 361 tx := newTxn(s.cli, conf) 362 for i := 0; i < conf.MaxRowsInOneTxn+1; i++ { 363 tx.Put(themisTestTableName, hbase.NewPut([]byte(strconv.Itoa(i))).AddValue(cf, q, []byte("test"))) 364 } 365 err := tx.Commit() 366 c.Assert(err, NotNil) 367 } 368 369 func (s *TransactionTestSuit) TestCheckCommitStatus(c *C) { 370 conf := defaultTxnConf 371 hook := newHook() 372 hook.beforeCommitSecondary = func(txn *themisTxn, ctx interface{}) (bool, interface{}, error) { 373 // add before commit secondary hook, just return, do not commit 374 // secondaries 375 log.Info("before commit secondary") 376 return false, nil, nil 377 } 378 tx := newTxn(s.cli, conf) 379 tx.(*themisTxn).setHook(hook) 380 for i := 0; i < 10; i++ { 381 tx.Put(themisTestTableName, hbase.NewPut([]byte(fmt.Sprintf("%s_%d", getTestRowKey(c), i))).AddValue(cf, q, []byte("test"))) 382 } 383 tx.Commit() 384 commitTs := tx.(*themisTxn).commitTs 385 386 pRow := append([]byte(nil), tx.(*themisTxn).primaryRow.row...) 387 sRow := append([]byte(nil), tx.(*themisTxn).secondaryRows[0].row...) 388 389 for i := 0; i < 10; i++ { 390 conf = defaultTxnConf 391 tx = newTxn(s.cli, conf) 392 tx.Put(themisTestTableName, hbase.NewPut([]byte(pRow)).AddValue(cf, q, []byte("newVal"))) 393 tx.Commit() 394 } 395 396 hook = newHook() 397 hook.beforePrewriteLockClean = func(txn *themisTxn, ctx interface{}) (bool, interface{}, error) { 398 lock := ctx.(Lock) 399 cc := lock.Primary().Coordinate() 400 exists, err := txn.lockCleaner.IsLockExists(lock.Coordinate(), 0, lock.Timestamp()) 401 c.Assert(err, IsNil) 402 c.Assert(exists, Equals, true) 403 ts, err := txn.lockCleaner.GetCommitTimestamp(cc, lock.Timestamp()) 404 c.Assert(err, IsNil) 405 c.Assert(ts, Equals, commitTs) 406 return true, nil, nil 407 } 408 tx = newTxn(s.cli, conf) 409 tx.(*themisTxn).setHook(hook) 410 tx.Put(themisTestTableName, hbase.NewPut([]byte(sRow)).AddValue(cf, q, []byte("newVal"))) 411 tx.Commit() 412 } 413 414 func createChoosePrimaryRowHook(target string) *txnHook { 415 hook := newHook() 416 hook.afterChoosePrimaryAndSecondary = func(txn *themisTxn, ctx interface{}) (bool, interface{}, error) { 417 txn.secondary = nil 418 txn.secondaryRows = nil 419 for tblName, rowMutations := range txn.mutationCache.mutations { 420 for _, rowMutation := range rowMutations { 421 row := rowMutation.row 422 for i, mutation := range rowMutation.mutationList(true) { 423 colcord := hbase.NewColumnCoordinate([]byte(tblName), row, mutation.Family, mutation.Qual) 424 // set the first column as primary if primary is not set by user 425 if string(row) == target { 426 txn.primary = colcord 427 txn.primaryRowOffset = i 428 txn.primaryRow = rowMutation 429 } else { 430 txn.secondary = append(txn.secondary, colcord) 431 } 432 } 433 if string(row) != target { 434 txn.secondaryRows = append(txn.secondaryRows, rowMutation) 435 } 436 } 437 } 438 return true, nil, nil 439 } 440 return hook 441 } 442 443 // Fix https://yougam/libraries/pingcap/go-themis/issues/19 444 func (s *TransactionTestSuit) TestPrewriteSecondaryMissingRows(c *C) { 445 conf := defaultTxnConf 446 hook := createChoosePrimaryRowHook("A") 447 hook.beforePrewriteSecondary = func(txn *themisTxn, ctx interface{}) (bool, interface{}, error) { 448 go func() { 449 hook2 := createChoosePrimaryRowHook("B") 450 hook2.onSecondaryOccursLock = func(txn *themisTxn, ctx interface{}) (bool, interface{}, error) { 451 log.Info("tx2 occurs secondary lock", ctx) 452 return true, nil, nil 453 } 454 tx2 := newTxn(s.cli, conf) 455 tx2.(*themisTxn).setHook(hook2) 456 tx2.Put(themisTestTableName, hbase.NewPut([]byte("A")).AddValue(cf, q, []byte("A"))) 457 tx2.Put(themisTestTableName, hbase.NewPut([]byte("B")).AddValue(cf, q, []byte("B"))) 458 tx2.Put(themisTestTableName, hbase.NewPut([]byte("C")).AddValue(cf, q, []byte("C"))) 459 tx2.Commit() 460 }() 461 time.Sleep(500 * time.Millisecond) 462 return true, nil, nil 463 } 464 465 hook.onSecondaryOccursLock = func(txn *themisTxn, ctx interface{}) (bool, interface{}, error) { 466 log.Info("tx1", ctx) 467 return true, nil, nil 468 } 469 470 hook.onPrewriteRow = func(txn *themisTxn, ctx interface{}) (bool, interface{}, error) { 471 containPrimary := ctx.([]interface{})[1].(bool) 472 if !containPrimary { 473 rm := ctx.([]interface{})[0].(*rowMutation) 474 log.Info(string(rm.row)) 475 } 476 477 return true, nil, nil 478 } 479 480 tx1 := newTxn(s.cli, conf) 481 tx1.(*themisTxn).setHook(hook) 482 tx1.Put(themisTestTableName, hbase.NewPut([]byte("A")).AddValue(cf, q, []byte("A"))) 483 tx1.Put(themisTestTableName, hbase.NewPut([]byte("B")).AddValue(cf, q, []byte("B"))) 484 tx1.Put(themisTestTableName, hbase.NewPut([]byte("C")).AddValue(cf, q, []byte("C"))) 485 err := tx1.Commit() 486 c.Assert(err, IsNil) 487 488 tx3 := newTxn(s.cli, conf) 489 rs, err := tx3.Get(themisTestTableName, hbase.NewGet([]byte("C")).AddColumn(cf, q)) 490 c.Assert(rs, NotNil) 491 c.Assert(err, IsNil) 492 tx3.Commit() 493 }