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  }