github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/client/operator_test.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 "context" 19 "testing" 20 "time" 21 22 "github.com/fagongzi/util/protoc" 23 "github.com/matrixorigin/matrixone/pkg/clusterservice" 24 "github.com/matrixorigin/matrixone/pkg/common/moerr" 25 "github.com/matrixorigin/matrixone/pkg/common/runtime" 26 "github.com/matrixorigin/matrixone/pkg/lockservice" 27 "github.com/matrixorigin/matrixone/pkg/pb/lock" 28 "github.com/matrixorigin/matrixone/pkg/pb/metadata" 29 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 30 "github.com/matrixorigin/matrixone/pkg/pb/txn" 31 "github.com/matrixorigin/matrixone/pkg/txn/rpc" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 "go.uber.org/zap" 35 ) 36 37 func TestRead(t *testing.T) { 38 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 39 result, err := tc.Read(ctx, []txn.TxnRequest{newTNRequest(1, 1), newTNRequest(2, 2)}) 40 assert.NoError(t, err) 41 assert.Equal(t, 2, len(result.Responses)) 42 assert.Equal(t, []byte("r-1"), result.Responses[0].CNOpResponse.Payload) 43 assert.Equal(t, []byte("r-2"), result.Responses[1].CNOpResponse.Payload) 44 }) 45 } 46 47 func TestWrite(t *testing.T) { 48 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 49 assert.Empty(t, tc.mu.txn.TNShards) 50 result, err := tc.Write(ctx, []txn.TxnRequest{newTNRequest(1, 1), newTNRequest(2, 2)}) 51 assert.NoError(t, err) 52 assert.Equal(t, 2, len(result.Responses)) 53 assert.Equal(t, []byte("w-1"), result.Responses[0].CNOpResponse.Payload) 54 assert.Equal(t, []byte("w-2"), result.Responses[1].CNOpResponse.Payload) 55 56 assert.Equal(t, uint64(1), tc.mu.txn.TNShards[0].ShardID) 57 assert.Equal(t, 2, len(tc.mu.txn.TNShards)) 58 }) 59 } 60 61 func TestWriteWithCacheWriteEnabled(t *testing.T) { 62 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 63 assert.Empty(t, tc.mu.txn.TNShards) 64 responses, err := tc.Write(ctx, []txn.TxnRequest{newTNRequest(1, 1), newTNRequest(2, 2)}) 65 assert.NoError(t, err) 66 assert.Empty(t, responses) 67 assert.Equal(t, uint64(1), tc.mu.txn.TNShards[0].ShardID) 68 assert.Equal(t, 2, len(tc.mu.txn.TNShards)) 69 assert.Empty(t, ts.getLastRequests()) 70 }, WithTxnCacheWrite()) 71 } 72 73 func TestRollback(t *testing.T) { 74 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 75 tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, metadata.TNShard{TNShardRecord: metadata.TNShardRecord{ShardID: 1}}) 76 err := tc.Rollback(ctx) 77 assert.NoError(t, err) 78 79 requests := ts.getLastRequests() 80 assert.Equal(t, 1, len(requests)) 81 assert.Equal(t, txn.TxnMethod_Rollback, requests[0].Method) 82 }) 83 } 84 85 func TestRollbackWithClosedTxn(t *testing.T) { 86 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 87 ts.setManual(func(sr *rpc.SendResult, err error) (*rpc.SendResult, error) { 88 return nil, moerr.NewTxnClosed(ctx, tc.txnID) 89 }) 90 91 tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, metadata.TNShard{TNShardRecord: metadata.TNShardRecord{ShardID: 1}}) 92 err := tc.Rollback(ctx) 93 assert.NoError(t, err) 94 }) 95 } 96 97 func TestRollbackWithNoWrite(t *testing.T) { 98 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 99 err := tc.Rollback(ctx) 100 assert.NoError(t, err) 101 assert.Empty(t, ts.getLastRequests()) 102 }) 103 } 104 105 func TestRollbackReadOnly(t *testing.T) { 106 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 107 err := tc.Rollback(ctx) 108 assert.NoError(t, err) 109 assert.Empty(t, ts.getLastRequests()) 110 }, WithTxnReadyOnly()) 111 } 112 113 func TestCommit(t *testing.T) { 114 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 115 tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, metadata.TNShard{TNShardRecord: metadata.TNShardRecord{ShardID: 1}}) 116 err := tc.Commit(ctx) 117 assert.NoError(t, err) 118 assert.Equal(t, tc.mu.txn.SnapshotTS.Next(), tc.mu.txn.CommitTS) 119 120 requests := ts.getLastRequests() 121 assert.Equal(t, 1, len(requests)) 122 assert.Equal(t, txn.TxnMethod_Commit, requests[0].Method) 123 }) 124 } 125 126 func TestCommitWithNoWrite(t *testing.T) { 127 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 128 err := tc.Commit(ctx) 129 assert.NoError(t, err) 130 assert.Empty(t, ts.getLastRequests()) 131 }) 132 } 133 134 func TestCommitReadOnly(t *testing.T) { 135 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 136 err := tc.Commit(ctx) 137 assert.NoError(t, err) 138 assert.Empty(t, ts.getLastRequests()) 139 }, WithTxnReadyOnly()) 140 } 141 142 func TestCommitWithLockTables(t *testing.T) { 143 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 144 r := runtime.DefaultRuntime() 145 runtime.SetupProcessLevelRuntime(r) 146 147 c := clusterservice.NewMOCluster(nil, time.Hour, clusterservice.WithDisableRefresh()) 148 defer c.Close() 149 r.SetGlobalVariables(runtime.ClusterService, c) 150 151 s := lockservice.NewLockService(lockservice.Config{ServiceID: "s1"}) 152 defer func() { 153 assert.NoError(t, s.Close()) 154 }() 155 156 tc.mu.txn.Mode = txn.TxnMode_Pessimistic 157 tc.lockService = s 158 tc.AddLockTable(lock.LockTable{Table: 1}) 159 tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, metadata.TNShard{TNShardRecord: metadata.TNShardRecord{ShardID: 1}}) 160 err := tc.Commit(ctx) 161 assert.NoError(t, err) 162 163 requests := ts.getLastRequests() 164 assert.Equal(t, 1, len(requests)) 165 assert.Equal(t, txn.TxnMethod_Commit, requests[0].Method) 166 assert.Equal(t, 1, len(requests[0].Txn.LockTables)) 167 }) 168 } 169 170 func TestCommitWithLockTablesChanged(t *testing.T) { 171 tableID1 := uint64(10) 172 tableID2 := uint64(20) 173 tableID3 := uint64(30) 174 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 175 lockservice.RunLockServicesForTest( 176 zap.DebugLevel, 177 []string{"s1"}, 178 time.Second, 179 func(lta lockservice.LockTableAllocator, ls []lockservice.LockService) { 180 s := ls[0] 181 182 _, err := s.Lock(ctx, tableID1, [][]byte{[]byte("k1")}, tc.txnID, lock.LockOptions{}) 183 assert.NoError(t, err) 184 _, err = s.Lock(ctx, tableID2, [][]byte{[]byte("k1")}, tc.txnID, lock.LockOptions{}) 185 assert.NoError(t, err) 186 _, err = s.Lock(ctx, tableID3, [][]byte{[]byte("k1")}, tc.txnID, lock.LockOptions{}) 187 assert.NoError(t, err) 188 189 ts.setManual(func(sr *rpc.SendResult, err error) (*rpc.SendResult, error) { 190 sr.Responses[0].TxnError = txn.WrapError(moerr.NewLockTableBindChanged(ctx), 0) 191 sr.Responses[0].CommitResponse = &txn.TxnCommitResponse{ 192 InvalidLockTables: []uint64{tableID1, tableID2}, 193 } 194 return sr, nil 195 }) 196 197 tc.mu.txn.Mode = txn.TxnMode_Pessimistic 198 tc.lockService = s 199 200 // table 1 hold bind same as lockservice, commit failed, will removed 201 tc.AddLockTable(lock.LockTable{Table: tableID1, ServiceID: s.GetServiceID(), Version: 1}) 202 // table 2 hold stale bind with lockservice, cannot remove bind in lockservice 203 tc.AddLockTable(lock.LockTable{Table: tableID2, ServiceID: s.GetServiceID(), Version: 0}) 204 // table 3 is valid 205 tc.AddLockTable(lock.LockTable{Table: tableID3, ServiceID: s.GetServiceID(), Version: 1}) 206 207 tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, metadata.TNShard{TNShardRecord: metadata.TNShardRecord{ShardID: 1}}) 208 err = tc.Commit(ctx) 209 assert.Error(t, err) 210 211 // table 1 will be removed 212 bind, err := s.GetLockTableBind(0, tableID1) 213 require.NoError(t, err) 214 require.Equal(t, lock.LockTable{}, bind) 215 216 // table 2 will be kept 217 bind, err = s.GetLockTableBind(0, tableID2) 218 require.NoError(t, err) 219 require.NotEqual(t, lock.LockTable{}, bind) 220 221 // table 3 will be kept 222 bind, err = s.GetLockTableBind(0, tableID3) 223 require.NoError(t, err) 224 require.NotEqual(t, lock.LockTable{}, bind) 225 }, 226 nil) 227 }) 228 } 229 230 func TestContextWithoutDeadlineWillPanic(t *testing.T) { 231 runOperatorTests(t, func(_ context.Context, tc *txnOperator, _ *testTxnSender) { 232 defer func() { 233 if err := recover(); err != nil { 234 return 235 } 236 assert.Fail(t, "must panic") 237 }() 238 239 _, err := tc.Write(context.Background(), nil) 240 assert.NoError(t, err) 241 }) 242 } 243 244 func TestMissingSenderWillPanic(t *testing.T) { 245 defer func() { 246 if err := recover(); err != nil { 247 return 248 } 249 assert.Fail(t, "must panic") 250 }() 251 runtime.SetupProcessLevelRuntime(runtime.DefaultRuntime()) 252 newTxnOperator(nil, nil, txn.TxnMeta{}) 253 } 254 255 func TestMissingTxnIDWillPanic(t *testing.T) { 256 defer func() { 257 if err := recover(); err != nil { 258 return 259 } 260 assert.Fail(t, "must panic") 261 }() 262 runtime.SetupProcessLevelRuntime(runtime.DefaultRuntime()) 263 newTxnOperator(nil, newTestTxnSender(), txn.TxnMeta{}) 264 } 265 266 func TestReadOnlyAndCacheWriteBothSetWillPanic(t *testing.T) { 267 defer func() { 268 if err := recover(); err != nil { 269 return 270 } 271 assert.Fail(t, "must panic") 272 }() 273 runtime.SetupProcessLevelRuntime(runtime.DefaultRuntime()) 274 newTxnOperator( 275 nil, 276 newTestTxnSender(), 277 txn.TxnMeta{ID: []byte{1}, SnapshotTS: timestamp.Timestamp{PhysicalTime: 1}}, 278 WithTxnReadyOnly(), 279 WithTxnCacheWrite()) 280 } 281 282 func TestWriteOnReadyOnlyTxnWillPanic(t *testing.T) { 283 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 284 defer func() { 285 if err := recover(); err != nil { 286 return 287 } 288 assert.Fail(t, "must panic") 289 }() 290 291 _, err := tc.Write(ctx, nil) 292 assert.NoError(t, err) 293 }, WithTxnReadyOnly()) 294 } 295 296 func TestWriteOnClosedTxnWillPanic(t *testing.T) { 297 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 298 tc.mu.closed = true 299 _, err := tc.Write(ctx, nil) 300 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 301 }) 302 } 303 304 func TestReadOnClosedTxnWillPanic(t *testing.T) { 305 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 306 tc.mu.closed = true 307 _, err := tc.Read(ctx, nil) 308 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 309 }) 310 } 311 312 func TestCacheWrites(t *testing.T) { 313 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 314 responses, err := tc.Write(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 315 assert.NoError(t, err) 316 assert.Empty(t, responses) 317 assert.Equal(t, 1, len(tc.mu.cachedWrites)) 318 assert.Equal(t, 1, len(tc.mu.cachedWrites[0])) 319 }, WithTxnCacheWrite()) 320 } 321 322 func TestCacheWritesWillInsertBeforeRead(t *testing.T) { 323 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 324 result, err := tc.Write(ctx, []txn.TxnRequest{newTNRequest(1, 1), newTNRequest(2, 2), newTNRequest(3, 3)}) 325 assert.NoError(t, err) 326 assert.Empty(t, result) 327 assert.Equal(t, 3, len(tc.mu.cachedWrites)) 328 assert.Equal(t, 1, len(tc.mu.cachedWrites[1])) 329 assert.Equal(t, 1, len(tc.mu.cachedWrites[2])) 330 assert.Equal(t, 1, len(tc.mu.cachedWrites[3])) 331 332 result, err = tc.Read(ctx, []txn.TxnRequest{newTNRequest(11, 1), newTNRequest(22, 2), newTNRequest(33, 3), newTNRequest(4, 4)}) 333 assert.NoError(t, err) 334 assert.Equal(t, 4, len(result.Responses)) 335 assert.Equal(t, []byte("r-11"), result.Responses[0].CNOpResponse.Payload) 336 assert.Equal(t, []byte("r-22"), result.Responses[1].CNOpResponse.Payload) 337 assert.Equal(t, []byte("r-33"), result.Responses[2].CNOpResponse.Payload) 338 assert.Equal(t, []byte("r-4"), result.Responses[3].CNOpResponse.Payload) 339 340 requests := ts.getLastRequests() 341 assert.Equal(t, 7, len(requests)) 342 assert.Equal(t, uint32(1), requests[0].CNRequest.OpCode) 343 assert.Equal(t, uint32(11), requests[1].CNRequest.OpCode) 344 assert.Equal(t, uint32(2), requests[2].CNRequest.OpCode) 345 assert.Equal(t, uint32(22), requests[3].CNRequest.OpCode) 346 assert.Equal(t, uint32(3), requests[4].CNRequest.OpCode) 347 assert.Equal(t, uint32(33), requests[5].CNRequest.OpCode) 348 assert.Equal(t, uint32(4), requests[6].CNRequest.OpCode) 349 350 assert.Equal(t, 0, len(tc.mu.cachedWrites)) 351 }, WithTxnCacheWrite()) 352 } 353 354 func TestReadOnAbortedTxn(t *testing.T) { 355 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 356 ts.setManual(func(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 357 for idx := range result.Responses { 358 result.Responses[idx].Txn = &txn.TxnMeta{Status: txn.TxnStatus_Aborted} 359 } 360 return result, err 361 }) 362 responses, err := tc.Read(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 363 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 364 assert.Empty(t, responses) 365 }) 366 } 367 368 func TestWriteOnAbortedTxn(t *testing.T) { 369 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 370 ts.setManual(func(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 371 for idx := range result.Responses { 372 result.Responses[idx].Txn = &txn.TxnMeta{Status: txn.TxnStatus_Aborted} 373 } 374 return result, err 375 }) 376 result, err := tc.Write(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 377 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 378 assert.Empty(t, result) 379 }) 380 } 381 382 func TestWriteOnCommittedTxn(t *testing.T) { 383 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 384 ts.setManual(func(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 385 for idx := range result.Responses { 386 result.Responses[idx].Txn = &txn.TxnMeta{Status: txn.TxnStatus_Committed} 387 } 388 return result, err 389 }) 390 result, err := tc.Write(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 391 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 392 assert.Empty(t, result) 393 }) 394 } 395 396 func TestWriteOnCommittingTxn(t *testing.T) { 397 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 398 ts.setManual(func(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 399 for idx := range result.Responses { 400 result.Responses[idx].Txn = &txn.TxnMeta{Status: txn.TxnStatus_Committing} 401 } 402 return result, err 403 }) 404 result, err := tc.Write(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 405 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 406 assert.Empty(t, result) 407 }) 408 } 409 410 func TestSnapshotTxnOperator(t *testing.T) { 411 runOperatorTests(t, func(_ context.Context, tc *txnOperator, _ *testTxnSender) { 412 assert.NoError(t, tc.AddLockTable(lock.LockTable{Table: 1})) 413 414 v, err := tc.Snapshot() 415 assert.NoError(t, err) 416 417 tc2, err := newTxnOperatorWithSnapshot(tc.sender, v) 418 assert.NoError(t, err) 419 assert.True(t, tc2.mu.txn.Mirror) 420 421 tc2.mu.txn.Mirror = false 422 assert.Equal(t, tc.mu.txn, tc2.mu.txn) 423 assert.False(t, tc2.coordinator) 424 tc2.coordinator = true 425 assert.Equal(t, tc.options, tc2.options) 426 assert.Equal(t, 1, len(tc2.mu.lockTables)) 427 }, WithTxnReadyOnly(), WithTxnDisable1PCOpt()) 428 } 429 430 func TestApplySnapshotTxnOperator(t *testing.T) { 431 runOperatorTests(t, func(_ context.Context, tc *txnOperator, _ *testTxnSender) { 432 snapshot := &txn.CNTxnSnapshot{} 433 snapshot.Txn.ID = tc.mu.txn.ID 434 assert.NoError(t, tc.ApplySnapshot(protoc.MustMarshal(snapshot))) 435 assert.Equal(t, 0, len(tc.mu.txn.TNShards)) 436 437 snapshot.Txn.TNShards = append(snapshot.Txn.TNShards, metadata.TNShard{TNShardRecord: metadata.TNShardRecord{ShardID: 1}}) 438 assert.NoError(t, tc.ApplySnapshot(protoc.MustMarshal(snapshot))) 439 assert.Equal(t, 1, len(tc.mu.txn.TNShards)) 440 441 snapshot.Txn.TNShards = append(snapshot.Txn.TNShards, metadata.TNShard{TNShardRecord: metadata.TNShardRecord{ShardID: 2}}) 442 assert.NoError(t, tc.ApplySnapshot(protoc.MustMarshal(snapshot))) 443 assert.Equal(t, 2, len(tc.mu.txn.TNShards)) 444 445 snapshot.LockTables = append(snapshot.LockTables, lock.LockTable{Table: 1}) 446 assert.NoError(t, tc.ApplySnapshot(protoc.MustMarshal(snapshot))) 447 assert.Equal(t, 1, len(tc.mu.lockTables)) 448 }) 449 } 450 451 func TestDebugTxnOperator(t *testing.T) { 452 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 453 responses, err := tc.Debug(ctx, 454 []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1, Payload: []byte("OK")})}) 455 assert.NoError(t, err) 456 assert.Equal(t, len(responses.Responses), 1) 457 assert.Equal(t, responses.Responses[0].CNOpResponse.Payload, []byte("OK")) 458 }) 459 } 460 461 func TestAddLockTable(t *testing.T) { 462 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 463 assert.NoError(t, tc.AddLockTable(lock.LockTable{Table: 1})) 464 assert.Equal(t, 1, len(tc.mu.lockTables)) 465 466 // same lock table 467 assert.NoError(t, tc.AddLockTable(lock.LockTable{Table: 1})) 468 assert.Equal(t, 1, len(tc.mu.lockTables)) 469 470 // changed lock table 471 assert.Error(t, tc.AddLockTable(lock.LockTable{Table: 1, Version: 2})) 472 }) 473 } 474 475 func TestUpdateSnapshotTSWithWaiter(t *testing.T) { 476 runTimestampWaiterTests(t, func(waiter *timestampWaiter) { 477 runOperatorTests(t, 478 func( 479 ctx context.Context, 480 tc *txnOperator, 481 _ *testTxnSender) { 482 tc.timestampWaiter = waiter 483 tc.mu.txn.SnapshotTS = newTestTimestamp(10) 484 tc.mu.txn.Isolation = txn.TxnIsolation_SI 485 486 ts := int64(100) 487 c := make(chan struct{}) 488 go func() { 489 defer close(c) 490 waiter.NotifyLatestCommitTS(newTestTimestamp(ts)) 491 }() 492 <-c 493 require.NoError(t, tc.UpdateSnapshot(context.Background(), newTestTimestamp(0))) 494 require.Equal(t, newTestTimestamp(ts).Next(), tc.Txn().SnapshotTS) 495 }) 496 }) 497 } 498 499 func TestRollbackMultiTimes(t *testing.T) { 500 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 501 require.NoError(t, tc.Rollback(ctx)) 502 require.NoError(t, tc.Rollback(ctx)) 503 }) 504 } 505 506 func TestWaitCommittedLogAppliedInRCMode(t *testing.T) { 507 lockservice.RunLockServicesForTest( 508 zap.InfoLevel, 509 []string{"s1"}, 510 time.Second, 511 func(lta lockservice.LockTableAllocator, ls []lockservice.LockService) { 512 l := ls[0] 513 tw := NewTimestampWaiter() 514 initTS := newTestTimestamp(1) 515 tw.NotifyLatestCommitTS(initTS) 516 runOperatorTestsWithOptions( 517 t, 518 func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 519 require.Equal(t, initTS.Next(), tc.mu.txn.SnapshotTS) 520 521 _, err := l.Lock(ctx, 1, [][]byte{[]byte("k1")}, tc.mu.txn.ID, lock.LockOptions{}) 522 require.NoError(t, err) 523 524 tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, metadata.TNShard{TNShardRecord: metadata.TNShardRecord{ShardID: 1}}) 525 526 ctx2, cancel := context.WithTimeout(context.Background(), time.Second*10) 527 defer cancel() 528 st := time.Now() 529 c := make(chan struct{}) 530 go func() { 531 defer close(c) 532 time.Sleep(time.Second) 533 tw.NotifyLatestCommitTS(initTS.Next().Next()) 534 }() 535 require.NoError(t, tc.Commit(ctx2)) 536 <-c 537 require.True(t, time.Since(st) > time.Second) 538 }, 539 newTestTimestamp(0).Next(), 540 []TxnOption{WithTxnMode(txn.TxnMode_Pessimistic), WithTxnIsolation(txn.TxnIsolation_RC)}, 541 WithTimestampWaiter(tw), 542 WithEnableSacrificingFreshness(), 543 WithLockService(l)) 544 }, 545 nil) 546 } 547 548 func runOperatorTests( 549 t *testing.T, 550 tc func(context.Context, *txnOperator, *testTxnSender), 551 options ...TxnOption) { 552 runOperatorTestsWithOptions(t, tc, newTestTimestamp(0), options) 553 } 554 555 func runOperatorTestsWithOptions( 556 t *testing.T, 557 tc func(context.Context, *txnOperator, *testTxnSender), 558 minTS timestamp.Timestamp, 559 options []TxnOption, 560 clientOptions ...TxnClientCreateOption) { 561 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 562 defer cancel() 563 564 RunTxnTests( 565 func( 566 c TxnClient, 567 ts rpc.TxnSender) { 568 txn, err := c.New(ctx, minTS, options...) 569 assert.Nil(t, err) 570 tc(ctx, txn.(*txnOperator), ts.(*testTxnSender)) 571 }, 572 clientOptions...) 573 } 574 575 func newTNRequest(op uint32, tn uint64) txn.TxnRequest { 576 return txn.NewTxnRequest(&txn.CNOpRequest{ 577 OpCode: op, 578 Target: metadata.TNShard{ 579 TNShardRecord: metadata.TNShardRecord{ShardID: tn}, 580 }, 581 }) 582 }