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