github.com/matrixorigin/matrixone@v0.7.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/common/moerr" 24 "github.com/matrixorigin/matrixone/pkg/common/runtime" 25 "github.com/matrixorigin/matrixone/pkg/pb/metadata" 26 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 27 "github.com/matrixorigin/matrixone/pkg/pb/txn" 28 "github.com/matrixorigin/matrixone/pkg/txn/rpc" 29 "github.com/stretchr/testify/assert" 30 ) 31 32 func TestRead(t *testing.T) { 33 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 34 result, err := tc.Read(ctx, []txn.TxnRequest{newDNRequest(1, 1), newDNRequest(2, 2)}) 35 assert.NoError(t, err) 36 assert.Equal(t, 2, len(result.Responses)) 37 assert.Equal(t, []byte("r-1"), result.Responses[0].CNOpResponse.Payload) 38 assert.Equal(t, []byte("r-2"), result.Responses[1].CNOpResponse.Payload) 39 }) 40 } 41 42 func TestWrite(t *testing.T) { 43 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 44 assert.Empty(t, tc.mu.txn.DNShards) 45 result, err := tc.Write(ctx, []txn.TxnRequest{newDNRequest(1, 1), newDNRequest(2, 2)}) 46 assert.NoError(t, err) 47 assert.Equal(t, 2, len(result.Responses)) 48 assert.Equal(t, []byte("w-1"), result.Responses[0].CNOpResponse.Payload) 49 assert.Equal(t, []byte("w-2"), result.Responses[1].CNOpResponse.Payload) 50 51 assert.Equal(t, uint64(1), tc.mu.txn.DNShards[0].ShardID) 52 assert.Equal(t, 2, len(tc.mu.txn.DNShards)) 53 }) 54 } 55 56 func TestWriteWithCacheWriteEnabled(t *testing.T) { 57 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 58 assert.Empty(t, tc.mu.txn.DNShards) 59 responses, err := tc.Write(ctx, []txn.TxnRequest{newDNRequest(1, 1), newDNRequest(2, 2)}) 60 assert.NoError(t, err) 61 assert.Empty(t, responses) 62 assert.Equal(t, uint64(1), tc.mu.txn.DNShards[0].ShardID) 63 assert.Equal(t, 2, len(tc.mu.txn.DNShards)) 64 assert.Empty(t, ts.getLastRequests()) 65 }, WithTxnCacheWrite()) 66 } 67 68 func TestRollback(t *testing.T) { 69 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 70 tc.mu.txn.DNShards = append(tc.mu.txn.DNShards, metadata.DNShard{DNShardRecord: metadata.DNShardRecord{ShardID: 1}}) 71 err := tc.Rollback(ctx) 72 assert.NoError(t, err) 73 74 requests := ts.getLastRequests() 75 assert.Equal(t, 1, len(requests)) 76 assert.Equal(t, txn.TxnMethod_Rollback, requests[0].Method) 77 }) 78 } 79 80 func TestRollbackWithClosedTxn(t *testing.T) { 81 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 82 ts.setManual(func(sr *rpc.SendResult, err error) (*rpc.SendResult, error) { 83 return nil, moerr.NewTxnClosed(ctx, tc.txnID) 84 }) 85 86 tc.mu.txn.DNShards = append(tc.mu.txn.DNShards, metadata.DNShard{DNShardRecord: metadata.DNShardRecord{ShardID: 1}}) 87 err := tc.Rollback(ctx) 88 assert.NoError(t, err) 89 }) 90 } 91 92 func TestRollbackWithNoWrite(t *testing.T) { 93 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 94 err := tc.Rollback(ctx) 95 assert.NoError(t, err) 96 assert.Empty(t, ts.getLastRequests()) 97 }) 98 } 99 100 func TestRollbackReadOnly(t *testing.T) { 101 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 102 err := tc.Rollback(ctx) 103 assert.NoError(t, err) 104 assert.Empty(t, ts.getLastRequests()) 105 }, WithTxnReadyOnly()) 106 } 107 108 func TestCommit(t *testing.T) { 109 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 110 tc.mu.txn.DNShards = append(tc.mu.txn.DNShards, metadata.DNShard{DNShardRecord: metadata.DNShardRecord{ShardID: 1}}) 111 err := tc.Commit(ctx) 112 assert.NoError(t, err) 113 114 requests := ts.getLastRequests() 115 assert.Equal(t, 1, len(requests)) 116 assert.Equal(t, txn.TxnMethod_Commit, requests[0].Method) 117 }) 118 } 119 120 func TestCommitWithNoWrite(t *testing.T) { 121 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 122 err := tc.Commit(ctx) 123 assert.NoError(t, err) 124 assert.Empty(t, ts.getLastRequests()) 125 }) 126 } 127 128 func TestCommitReadOnly(t *testing.T) { 129 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 130 err := tc.Commit(ctx) 131 assert.NoError(t, err) 132 assert.Empty(t, ts.getLastRequests()) 133 }, WithTxnReadyOnly()) 134 } 135 136 func TestContextWithoutDeadlineWillPanic(t *testing.T) { 137 runOperatorTests(t, func(_ context.Context, tc *txnOperator, _ *testTxnSender) { 138 defer func() { 139 if err := recover(); err != nil { 140 return 141 } 142 assert.Fail(t, "must panic") 143 }() 144 145 _, err := tc.Write(context.Background(), nil) 146 assert.NoError(t, err) 147 }) 148 } 149 150 func TestMissingSenderWillPanic(t *testing.T) { 151 defer func() { 152 if err := recover(); err != nil { 153 return 154 } 155 assert.Fail(t, "must panic") 156 }() 157 newTxnOperator(runtime.DefaultRuntime(), nil, txn.TxnMeta{}) 158 } 159 160 func TestMissingTxnIDWillPanic(t *testing.T) { 161 defer func() { 162 if err := recover(); err != nil { 163 return 164 } 165 assert.Fail(t, "must panic") 166 }() 167 newTxnOperator(runtime.DefaultRuntime(), newTestTxnSender(), txn.TxnMeta{}) 168 } 169 170 func TestEmptyTxnSnapshotTSWillPanic(t *testing.T) { 171 defer func() { 172 if err := recover(); err != nil { 173 return 174 } 175 assert.Fail(t, "must panic") 176 }() 177 newTxnOperator(runtime.DefaultRuntime(), newTestTxnSender(), txn.TxnMeta{ID: []byte{1}}) 178 } 179 180 func TestReadOnlyAndCacheWriteBothSetWillPanic(t *testing.T) { 181 defer func() { 182 if err := recover(); err != nil { 183 return 184 } 185 assert.Fail(t, "must panic") 186 }() 187 newTxnOperator( 188 runtime.DefaultRuntime(), 189 newTestTxnSender(), 190 txn.TxnMeta{ID: []byte{1}, SnapshotTS: timestamp.Timestamp{PhysicalTime: 1}}, 191 WithTxnReadyOnly(), 192 WithTxnCacheWrite()) 193 } 194 195 func TestWriteOnReadyOnlyTxnWillPanic(t *testing.T) { 196 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 197 defer func() { 198 if err := recover(); err != nil { 199 return 200 } 201 assert.Fail(t, "must panic") 202 }() 203 204 _, err := tc.Write(ctx, nil) 205 assert.NoError(t, err) 206 }, WithTxnReadyOnly()) 207 } 208 209 func TestWriteOnClosedTxnWillPanic(t *testing.T) { 210 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 211 tc.mu.closed = true 212 _, err := tc.Write(ctx, nil) 213 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 214 }) 215 } 216 217 func TestReadOnClosedTxnWillPanic(t *testing.T) { 218 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 219 tc.mu.closed = true 220 _, err := tc.Read(ctx, nil) 221 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 222 }) 223 } 224 225 func TestCacheWrites(t *testing.T) { 226 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 227 responses, err := tc.Write(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 228 assert.NoError(t, err) 229 assert.Empty(t, responses) 230 assert.Equal(t, 1, len(tc.mu.cachedWrites)) 231 assert.Equal(t, 1, len(tc.mu.cachedWrites[0])) 232 }, WithTxnCacheWrite()) 233 } 234 235 func TestCacheWritesWillInsertBeforeRead(t *testing.T) { 236 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 237 result, err := tc.Write(ctx, []txn.TxnRequest{newDNRequest(1, 1), newDNRequest(2, 2), newDNRequest(3, 3)}) 238 assert.NoError(t, err) 239 assert.Empty(t, result) 240 assert.Equal(t, 3, len(tc.mu.cachedWrites)) 241 assert.Equal(t, 1, len(tc.mu.cachedWrites[1])) 242 assert.Equal(t, 1, len(tc.mu.cachedWrites[2])) 243 assert.Equal(t, 1, len(tc.mu.cachedWrites[3])) 244 245 result, err = tc.Read(ctx, []txn.TxnRequest{newDNRequest(11, 1), newDNRequest(22, 2), newDNRequest(33, 3), newDNRequest(4, 4)}) 246 assert.NoError(t, err) 247 assert.Equal(t, 4, len(result.Responses)) 248 assert.Equal(t, []byte("r-11"), result.Responses[0].CNOpResponse.Payload) 249 assert.Equal(t, []byte("r-22"), result.Responses[1].CNOpResponse.Payload) 250 assert.Equal(t, []byte("r-33"), result.Responses[2].CNOpResponse.Payload) 251 assert.Equal(t, []byte("r-4"), result.Responses[3].CNOpResponse.Payload) 252 253 requests := ts.getLastRequests() 254 assert.Equal(t, 7, len(requests)) 255 assert.Equal(t, uint32(1), requests[0].CNRequest.OpCode) 256 assert.Equal(t, uint32(11), requests[1].CNRequest.OpCode) 257 assert.Equal(t, uint32(2), requests[2].CNRequest.OpCode) 258 assert.Equal(t, uint32(22), requests[3].CNRequest.OpCode) 259 assert.Equal(t, uint32(3), requests[4].CNRequest.OpCode) 260 assert.Equal(t, uint32(33), requests[5].CNRequest.OpCode) 261 assert.Equal(t, uint32(4), requests[6].CNRequest.OpCode) 262 263 assert.Equal(t, 0, len(tc.mu.cachedWrites)) 264 }, WithTxnCacheWrite()) 265 } 266 267 func TestReadOnAbortedTxn(t *testing.T) { 268 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 269 ts.setManual(func(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 270 for idx := range result.Responses { 271 result.Responses[idx].Txn = &txn.TxnMeta{Status: txn.TxnStatus_Aborted} 272 } 273 return result, err 274 }) 275 responses, err := tc.Read(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 276 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 277 assert.Empty(t, responses) 278 }) 279 } 280 281 func TestWriteOnAbortedTxn(t *testing.T) { 282 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 283 ts.setManual(func(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 284 for idx := range result.Responses { 285 result.Responses[idx].Txn = &txn.TxnMeta{Status: txn.TxnStatus_Aborted} 286 } 287 return result, err 288 }) 289 result, err := tc.Write(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 290 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 291 assert.Empty(t, result) 292 }) 293 } 294 295 func TestWriteOnCommittedTxn(t *testing.T) { 296 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 297 ts.setManual(func(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 298 for idx := range result.Responses { 299 result.Responses[idx].Txn = &txn.TxnMeta{Status: txn.TxnStatus_Committed} 300 } 301 return result, err 302 }) 303 result, err := tc.Write(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 304 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 305 assert.Empty(t, result) 306 }) 307 } 308 309 func TestWriteOnCommittingTxn(t *testing.T) { 310 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, ts *testTxnSender) { 311 ts.setManual(func(result *rpc.SendResult, err error) (*rpc.SendResult, error) { 312 for idx := range result.Responses { 313 result.Responses[idx].Txn = &txn.TxnMeta{Status: txn.TxnStatus_Committing} 314 } 315 return result, err 316 }) 317 result, err := tc.Write(ctx, []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1})}) 318 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnClosed)) 319 assert.Empty(t, result) 320 }) 321 } 322 323 func TestSnapshotTxnOperator(t *testing.T) { 324 runOperatorTests(t, func(_ context.Context, tc *txnOperator, _ *testTxnSender) { 325 v, err := tc.Snapshot() 326 assert.NoError(t, err) 327 328 tc2, err := newTxnOperatorWithSnapshot(tc.rt, tc.sender, v) 329 assert.NoError(t, err) 330 331 assert.Equal(t, tc.mu.txn, tc2.mu.txn) 332 assert.False(t, tc2.option.coordinator) 333 tc2.option.coordinator = true 334 assert.Equal(t, tc.option, tc2.option) 335 }, WithTxnReadyOnly(), WithTxnDisable1PCOpt()) 336 } 337 338 func TestApplySnapshotTxnOperator(t *testing.T) { 339 runOperatorTests(t, func(_ context.Context, tc *txnOperator, _ *testTxnSender) { 340 snapshot := &txn.CNTxnSnapshot{} 341 snapshot.Txn.ID = tc.mu.txn.ID 342 assert.NoError(t, tc.ApplySnapshot(protoc.MustMarshal(snapshot))) 343 assert.Equal(t, 0, len(tc.mu.txn.DNShards)) 344 345 snapshot.Txn.DNShards = append(snapshot.Txn.DNShards, metadata.DNShard{DNShardRecord: metadata.DNShardRecord{ShardID: 1}}) 346 assert.NoError(t, tc.ApplySnapshot(protoc.MustMarshal(snapshot))) 347 assert.Equal(t, 1, len(tc.mu.txn.DNShards)) 348 349 snapshot.Txn.DNShards = append(snapshot.Txn.DNShards, metadata.DNShard{DNShardRecord: metadata.DNShardRecord{ShardID: 2}}) 350 assert.NoError(t, tc.ApplySnapshot(protoc.MustMarshal(snapshot))) 351 assert.Equal(t, 2, len(tc.mu.txn.DNShards)) 352 }) 353 } 354 355 func TestDebugTxnOperator(t *testing.T) { 356 runOperatorTests(t, func(ctx context.Context, tc *txnOperator, _ *testTxnSender) { 357 responses, err := tc.Debug(ctx, 358 []txn.TxnRequest{txn.NewTxnRequest(&txn.CNOpRequest{OpCode: 1, Payload: []byte("OK")})}) 359 assert.NoError(t, err) 360 assert.Equal(t, len(responses.Responses), 1) 361 assert.Equal(t, responses.Responses[0].CNOpResponse.Payload, []byte("OK")) 362 }) 363 } 364 365 func runOperatorTests(t *testing.T, tc func(context.Context, *txnOperator, *testTxnSender), options ...TxnOption) { 366 ts := newTestTxnSender() 367 c := NewTxnClient( 368 runtime.DefaultRuntime(), 369 ts) 370 txn, err := c.New(options...) 371 assert.Nil(t, err) 372 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 373 defer cancel() 374 375 tc(ctx, txn.(*txnOperator), ts) 376 } 377 378 func newDNRequest(op uint32, dn uint64) txn.TxnRequest { 379 return txn.NewTxnRequest(&txn.CNOpRequest{ 380 OpCode: op, 381 Target: metadata.DNShard{ 382 DNShardRecord: metadata.DNShardRecord{ShardID: dn}, 383 }, 384 }) 385 }