github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/service/service_testutil.go (about) 1 // Copyright 2021 - 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 service 16 17 import ( 18 "context" 19 "fmt" 20 "math" 21 "sync" 22 "sync/atomic" 23 "testing" 24 "time" 25 26 "github.com/matrixorigin/matrixone/pkg/common/runtime" 27 "github.com/matrixorigin/matrixone/pkg/lockservice" 28 "github.com/matrixorigin/matrixone/pkg/logservice" 29 "github.com/matrixorigin/matrixone/pkg/logutil" 30 "github.com/matrixorigin/matrixone/pkg/pb/metadata" 31 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 32 "github.com/matrixorigin/matrixone/pkg/pb/txn" 33 "github.com/matrixorigin/matrixone/pkg/txn/clock" 34 "github.com/matrixorigin/matrixone/pkg/txn/rpc" 35 "github.com/matrixorigin/matrixone/pkg/txn/storage" 36 "github.com/matrixorigin/matrixone/pkg/txn/storage/mem" 37 "go.uber.org/zap" 38 "go.uber.org/zap/zapcore" 39 ) 40 41 // NewTestTxnService create a test TxnService for test 42 func NewTestTxnService( 43 t *testing.T, 44 shard uint64, 45 sender rpc.TxnSender, 46 clock clock.Clock) TxnService { 47 return NewTestTxnServiceWithLog(t, shard, sender, clock, nil) 48 } 49 50 // NewTestTxnServiceWithAllocator create a test TxnService for test 51 func NewTestTxnServiceWithAllocator( 52 t *testing.T, 53 shard uint64, 54 sender rpc.TxnSender, 55 clock clock.Clock, 56 allocator lockservice.LockTableAllocator) TxnService { 57 return NewTestTxnServiceWithLogAndZombieAndLockTabkeAllocator( 58 t, 59 shard, 60 sender, 61 clock, 62 nil, 63 time.Minute, 64 allocator) 65 } 66 67 // NewTestTxnServiceWithLog is similar to NewTestTxnService, used to recovery tests 68 func NewTestTxnServiceWithLog( 69 t *testing.T, 70 shard uint64, 71 sender rpc.TxnSender, 72 clock clock.Clock, 73 log logservice.Client) TxnService { 74 return NewTestTxnServiceWithLogAndZombie( 75 t, 76 shard, 77 sender, 78 clock, 79 log, 80 time.Minute) 81 } 82 83 // NewTestTxnServiceWithLogAndZombie is similar to NewTestTxnService, but with more args 84 func NewTestTxnServiceWithLogAndZombie( 85 t *testing.T, 86 shard uint64, 87 sender rpc.TxnSender, 88 clock clock.Clock, 89 log logservice.Client, 90 zombie time.Duration) TxnService { 91 return NewTestTxnServiceWithLogAndZombieAndLockTabkeAllocator( 92 t, 93 shard, 94 sender, 95 clock, 96 log, 97 zombie, 98 nil, 99 ) 100 } 101 102 // NewTestTxnServiceWithLogAndZombieAndLockTabkeAllocator is similar to NewTestTxnService, but with more args 103 func NewTestTxnServiceWithLogAndZombieAndLockTabkeAllocator( 104 t *testing.T, 105 shard uint64, 106 sender rpc.TxnSender, 107 clock clock.Clock, 108 log logservice.Client, 109 zombie time.Duration, 110 allocator lockservice.LockTableAllocator) TxnService { 111 rt := runtime.NewRuntime( 112 metadata.ServiceType_TN, 113 "dn-uuid", 114 logutil.GetPanicLoggerWithLevel(zapcore.DebugLevel).With(zap.String("case", t.Name())), 115 runtime.WithClock(clock)) 116 runtime.SetupProcessLevelRuntime(rt) 117 return NewTxnService(NewTestTNShard(shard), 118 NewTestTxnStorage(log, clock), 119 sender, 120 zombie, 121 allocator).(*service) 122 } 123 124 // NewTestTxnStorage create a TxnStorage used to recovery tests 125 func NewTestTxnStorage(log logservice.Client, clock clock.Clock) storage.TxnStorage { 126 if log == nil { 127 log = mem.NewMemLog() 128 } 129 return mem.NewKVTxnStorage(1, log, clock) 130 } 131 132 // NewTestTNShard create a test DNShard 133 func NewTestTNShard(id uint64) metadata.TNShard { 134 return metadata.TNShard{ 135 TNShardRecord: metadata.TNShardRecord{ 136 ShardID: id, 137 LogShardID: id, 138 }, 139 ReplicaID: id, 140 Address: fmt.Sprintf("dn-%d", id), 141 } 142 } 143 144 // NewTestClock create test clock with start timestamp 145 func NewTestClock(start int64) clock.Clock { 146 ts := start 147 return clock.NewHLCClock(func() int64 { 148 return atomic.AddInt64(&ts, 1) 149 }, math.MaxInt64) 150 } 151 152 // NewTestSpecClock create test clock with timestamp factory 153 func NewTestSpecClock(fn func() int64) clock.Clock { 154 return clock.NewHLCClock(fn, math.MaxInt64) 155 } 156 157 // TestSender test TxnSender for sending messages between TxnServices 158 type TestSender struct { 159 router map[string]rpc.TxnRequestHandleFunc 160 filter func(*txn.TxnRequest) bool 161 162 mu struct { 163 sync.Mutex 164 cancels []context.CancelFunc 165 } 166 } 167 168 // NewTestSender create test TxnSender 169 func NewTestSender(services ...TxnService) *TestSender { 170 s := &TestSender{ 171 router: make(map[string]rpc.TxnRequestHandleFunc), 172 } 173 for _, ts := range services { 174 s.AddTxnService(ts) 175 } 176 return s 177 } 178 179 // AddTxnService add txnservice into test TxnSender 180 func (s *TestSender) AddTxnService(ts TxnService) { 181 s.router[s.getRouteKey(txn.TxnMethod_Read, ts.Shard())] = ts.Read 182 s.router[s.getRouteKey(txn.TxnMethod_Write, ts.Shard())] = ts.Write 183 s.router[s.getRouteKey(txn.TxnMethod_Commit, ts.Shard())] = ts.Commit 184 s.router[s.getRouteKey(txn.TxnMethod_Rollback, ts.Shard())] = ts.Rollback 185 s.router[s.getRouteKey(txn.TxnMethod_Prepare, ts.Shard())] = ts.Prepare 186 s.router[s.getRouteKey(txn.TxnMethod_GetStatus, ts.Shard())] = ts.GetStatus 187 s.router[s.getRouteKey(txn.TxnMethod_CommitTNShard, ts.Shard())] = ts.CommitTNShard 188 s.router[s.getRouteKey(txn.TxnMethod_RollbackTNShard, ts.Shard())] = ts.RollbackTNShard 189 s.router[s.getRouteKey(txn.TxnMethod_DEBUG, ts.Shard())] = ts.Debug 190 } 191 192 func (s *TestSender) setFilter(filter func(*txn.TxnRequest) bool) { 193 s.filter = filter 194 } 195 196 // Send TxnSender send 197 func (s *TestSender) Send(ctx context.Context, requests []txn.TxnRequest) (*rpc.SendResult, error) { 198 ctx, cancel := context.WithTimeout(ctx, time.Minute) 199 s.mu.Lock() 200 s.mu.cancels = append(s.mu.cancels, cancel) 201 s.mu.Unlock() 202 203 responses := make([]txn.TxnResponse, 0, len(requests)) 204 for _, req := range requests { 205 if s.filter != nil && !s.filter(&req) { 206 continue 207 } 208 209 resp := txn.TxnResponse{} 210 h := s.router[s.getRouteKey(req.Method, req.GetTargetTN())] 211 if err := h(ctx, &req, &resp); err != nil { 212 return nil, err 213 } 214 responses = append(responses, resp) 215 } 216 return &rpc.SendResult{Responses: responses}, nil 217 } 218 219 // Close close the test TxnSender 220 func (s *TestSender) Close() error { 221 s.mu.Lock() 222 defer s.mu.Unlock() 223 for _, cancel := range s.mu.cancels { 224 cancel() 225 } 226 return nil 227 } 228 229 func (s *TestSender) getRouteKey(method txn.TxnMethod, shard metadata.TNShard) string { 230 return fmt.Sprintf("%d-%s", shard.ShardID, method.String()) 231 } 232 233 // NewTestTxn create a transaction, specifying both the transaction snapshot time and the DNShard 234 // for the transaction operation. 235 func NewTestTxn(txnID byte, ts int64, shards ...uint64) txn.TxnMeta { 236 txnMeta := txn.TxnMeta{ 237 ID: []byte{txnID}, 238 Status: txn.TxnStatus_Active, 239 SnapshotTS: NewTestTimestamp(ts), 240 } 241 for _, shard := range shards { 242 txnMeta.TNShards = append(txnMeta.TNShards, NewTestTNShard(shard)) 243 } 244 return txnMeta 245 } 246 247 // NewTestTimestamp create a test timestamp and set only the PhysicalTime field 248 func NewTestTimestamp(ts int64) timestamp.Timestamp { 249 return timestamp.Timestamp{PhysicalTime: ts} 250 } 251 252 // NewTestWriteRequest create a Write request, using GetTestKey and GetTestValue as the KV data for 253 // the test. 254 func NewTestWriteRequest(k byte, wTxn txn.TxnMeta, toShard uint64) txn.TxnRequest { 255 key := GetTestKey(k) 256 value := GetTestValue(k, wTxn) 257 req := mem.NewSetTxnRequest([][]byte{key}, [][]byte{value}) 258 req.Txn = wTxn 259 req.CNRequest.Target = NewTestTNShard(toShard) 260 return req 261 } 262 263 // NewTestReadRequest create a read request, using GetTestKey as the KV data for the test. 264 func NewTestReadRequest(k byte, rTxn txn.TxnMeta, toShard uint64) txn.TxnRequest { 265 key := GetTestKey(k) 266 req := mem.NewGetTxnRequest([][]byte{key}) 267 req.Txn = rTxn 268 req.CNRequest.Target = NewTestTNShard(toShard) 269 return req 270 } 271 272 // NewTestCommitRequest create a commit request 273 func NewTestCommitRequest(wTxn txn.TxnMeta) txn.TxnRequest { 274 return txn.TxnRequest{ 275 Method: txn.TxnMethod_Commit, 276 Txn: wTxn, 277 CommitRequest: &txn.TxnCommitRequest{}, 278 } 279 } 280 281 // NewTestCommitShardRequest create a commit DNShard request 282 func NewTestCommitShardRequest(wTxn txn.TxnMeta) txn.TxnRequest { 283 return txn.TxnRequest{ 284 Method: txn.TxnMethod_CommitTNShard, 285 Txn: wTxn, 286 CommitTNShardRequest: &txn.TxnCommitTNShardRequest{ 287 TNShard: wTxn.TNShards[0], 288 }, 289 } 290 } 291 292 // NewTestRollbackShardRequest create a rollback DNShard request 293 func NewTestRollbackShardRequest(wTxn txn.TxnMeta) txn.TxnRequest { 294 return txn.TxnRequest{ 295 Method: txn.TxnMethod_RollbackTNShard, 296 Txn: wTxn, 297 RollbackTNShardRequest: &txn.TxnRollbackTNShardRequest{ 298 TNShard: wTxn.TNShards[0], 299 }, 300 } 301 } 302 303 // NewTestRollbackRequest create a rollback request 304 func NewTestRollbackRequest(wTxn txn.TxnMeta) txn.TxnRequest { 305 return txn.TxnRequest{ 306 Method: txn.TxnMethod_Rollback, 307 Txn: wTxn, 308 RollbackRequest: &txn.TxnRollbackRequest{}, 309 } 310 } 311 312 // NewTestPrepareRequest create a prepare request 313 func NewTestPrepareRequest(wTxn txn.TxnMeta, shard uint64) txn.TxnRequest { 314 return txn.TxnRequest{ 315 Method: txn.TxnMethod_Prepare, 316 Txn: wTxn, 317 PrepareRequest: &txn.TxnPrepareRequest{ 318 TNShard: NewTestTNShard(shard), 319 }, 320 } 321 } 322 323 // NewTestGetStatusRequest create a get status request 324 func NewTestGetStatusRequest(wTxn txn.TxnMeta, shard uint64) txn.TxnRequest { 325 return txn.TxnRequest{ 326 Method: txn.TxnMethod_GetStatus, 327 Txn: wTxn, 328 GetStatusRequest: &txn.TxnGetStatusRequest{ 329 TNShard: NewTestTNShard(shard), 330 }, 331 } 332 } 333 334 // GetTestKey encode test key 335 func GetTestKey(k byte) []byte { 336 return []byte{k} 337 } 338 339 // GetTestValue encode test value based on the key and txn's snapshot timestamp 340 func GetTestValue(k byte, wTxn txn.TxnMeta) []byte { 341 return []byte(fmt.Sprintf("%d-%d-%d", k, wTxn.ID[0], wTxn.SnapshotTS.PhysicalTime)) 342 }