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  }