github.com/matrixorigin/matrixone@v1.2.0/pkg/hakeeper/checkers/dnservice/parse_test.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 dnservice
    16  
    17  import (
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/stretchr/testify/require"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/hakeeper"
    24  	"github.com/matrixorigin/matrixone/pkg/hakeeper/checkers/util"
    25  	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    26  )
    27  
    28  func TestCheckInitiatingShards(t *testing.T) {
    29  	// clear all records, or other test would fail
    30  	defer func() {
    31  		waitingShards.clear()
    32  	}()
    33  
    34  	nextReplicaID := uint64(100)
    35  	enough := true
    36  	idAlloc := newMockIDAllocator(nextReplicaID, enough)
    37  
    38  	workingStores := []*util.Store{
    39  		util.NewStore("store1", 2, TnStoreCapacity),
    40  		util.NewStore("store2", 3, TnStoreCapacity),
    41  		util.NewStore("store3", 4, TnStoreCapacity),
    42  	}
    43  
    44  	reportedShard := uint64(10)
    45  	reportedReplica := uint64(21)
    46  	initialShard := uint64(11)
    47  
    48  	rs := newReportedShards()
    49  	// register a working replica => no need to operation
    50  	rs.registerReplica(newReplica(reportedReplica, reportedShard, "store11"), false)
    51  
    52  	// mock pb.ClusterInfo
    53  	cluster := mockClusterInfo(reportedShard, initialShard)
    54  
    55  	// mock hakeeper.Config
    56  	config := hakeeper.Config{
    57  		TickPerSecond:   10,
    58  		LogStoreTimeout: 10 * time.Second,
    59  		TNStoreTimeout:  10 * time.Second,
    60  	}
    61  
    62  	// mock ShardMapper
    63  	mapper := mockShardMapper()
    64  
    65  	earliestTick := uint64(10)
    66  	expiredTick := config.ExpiredTick(earliestTick, config.TNStoreTimeout) + 1
    67  
    68  	// discover an initial shard => no operators generated
    69  	ops := checkInitiatingShards(rs, mapper, workingStores, idAlloc, cluster, config, earliestTick)
    70  	require.Equal(t, 0, len(ops))
    71  
    72  	// waiting some time, but not long enough
    73  	ops = checkInitiatingShards(rs, mapper, workingStores, idAlloc, cluster, config, expiredTick-1)
    74  	require.Equal(t, 0, len(ops))
    75  
    76  	// waiting long enough
    77  	ops = checkInitiatingShards(rs, mapper, workingStores, idAlloc, cluster, config, expiredTick)
    78  	require.Equal(t, 1, len(ops))
    79  }
    80  
    81  func TestCheckReportedState(t *testing.T) {
    82  	nextReplicaID := uint64(100)
    83  	enough := true
    84  	idAlloc := newMockIDAllocator(nextReplicaID, enough)
    85  	mapper := mockShardMapper()
    86  
    87  	workingStores := []*util.Store{
    88  		util.NewStore("store1", 2, TnStoreCapacity),
    89  		util.NewStore("store2", 3, TnStoreCapacity),
    90  		util.NewStore("store3", 4, TnStoreCapacity),
    91  	}
    92  
    93  	shardID := uint64(10)
    94  
    95  	// register an expired replica => should add a new replica
    96  	rs := newReportedShards()
    97  	rs.registerReplica(newReplica(11, shardID, "store11"), true)
    98  	ops := checkReportedState(rs, mapper, workingStores, idAlloc)
    99  	require.Equal(t, 1, len(ops))
   100  	require.Equal(t, shardID, ops[0].ShardID())
   101  
   102  	// register a working replica => no more step
   103  	rs = newReportedShards()
   104  	rs.registerReplica(newReplica(12, shardID, "store12"), false)
   105  	ops = checkReportedState(rs, mapper, workingStores, idAlloc)
   106  	require.Equal(t, 0, len(ops))
   107  }
   108  
   109  func TestInitialShards(t *testing.T) {
   110  	waitingShards := newInitialShards()
   111  
   112  	// list all shard id
   113  	ids := waitingShards.listEligibleShards(func(tick uint64) bool {
   114  		return true
   115  	})
   116  	require.Equal(t, 0, len(ids))
   117  
   118  	// test register
   119  	shardID := uint64(1)
   120  	tick := uint64(10)
   121  	updated := waitingShards.register(shardID, tick)
   122  	require.True(t, updated)
   123  	updated = waitingShards.register(shardID, tick)
   124  	require.False(t, updated)
   125  	tick = 11
   126  	updated = waitingShards.register(shardID, tick)
   127  	require.False(t, updated)
   128  	tick = 9
   129  	updated = waitingShards.register(shardID, tick)
   130  	require.True(t, updated)
   131  	ids = waitingShards.listEligibleShards(func(tick uint64) bool {
   132  		return true
   133  	})
   134  	require.Equal(t, 1, len(ids))
   135  
   136  	// test delta
   137  	updated = waitingShards.remove(shardID + 1)
   138  	require.False(t, updated)
   139  	updated = waitingShards.remove(shardID)
   140  	require.True(t, updated)
   141  	ids = waitingShards.listEligibleShards(func(tick uint64) bool {
   142  		return true
   143  	})
   144  	require.Equal(t, 0, len(ids))
   145  }
   146  
   147  func TestParseTNState(t *testing.T) {
   148  	expiredTick := uint64(10)
   149  	// construct current tick in order to make heartbeat tick expired
   150  	cfg := hakeeper.Config{}
   151  	cfg.Fill()
   152  	currTick := cfg.ExpiredTick(expiredTick, cfg.TNStoreTimeout) + 1
   153  
   154  	// 1. no working tn stores
   155  	{
   156  		tnState := pb.TNState{
   157  			Stores: map[string]pb.TNStoreInfo{
   158  				"expired1": {
   159  					Tick: expiredTick,
   160  					Shards: []pb.TNShardInfo{
   161  						mockTnShardInfo(10, 12),
   162  					},
   163  				},
   164  				"expired2": {
   165  					Tick: expiredTick,
   166  					Shards: []pb.TNShardInfo{
   167  						mockTnShardInfo(11, 13),
   168  					},
   169  				},
   170  			},
   171  		}
   172  
   173  		stores, shards := parseTnState(cfg, tnState, currTick)
   174  
   175  		// check stores
   176  		require.Equal(t, len(stores.WorkingStores()), 0)
   177  		require.Equal(t, len(stores.ExpiredStores()), 2)
   178  
   179  		// check shards
   180  		shardIDs := shards.listShards()
   181  		require.Equal(t, len(shardIDs), 2)
   182  
   183  		shard10, err := shards.getShard(10)
   184  		require.NoError(t, err)
   185  		require.NotNil(t, shard10)
   186  		require.Equal(t, len(shard10.workingReplicas()), 0)
   187  		require.Equal(t, len(shard10.expiredReplicas()), 1)
   188  
   189  		_, err = shards.getShard(100)
   190  		require.Error(t, err)
   191  	}
   192  
   193  	// 2. verbose running shard replica
   194  	{
   195  		tnState := pb.TNState{
   196  			Stores: map[string]pb.TNStoreInfo{
   197  				"expired1": {
   198  					Tick: expiredTick,
   199  					Shards: []pb.TNShardInfo{
   200  						mockTnShardInfo(10, 11),
   201  						mockTnShardInfo(14, 17),
   202  					},
   203  				},
   204  				"working1": {
   205  					Tick: currTick,
   206  					Shards: []pb.TNShardInfo{
   207  						mockTnShardInfo(12, 13),
   208  					},
   209  				},
   210  				"working2": {
   211  					Tick: currTick,
   212  					Shards: []pb.TNShardInfo{
   213  						mockTnShardInfo(14, 15),
   214  						mockTnShardInfo(12, 16),
   215  					},
   216  				},
   217  			},
   218  		}
   219  
   220  		stores, shards := parseTnState(cfg, tnState, currTick)
   221  
   222  		// check stores
   223  		require.Equal(t, len(stores.WorkingStores()), 2)
   224  		require.Equal(t, len(stores.ExpiredStores()), 1)
   225  
   226  		// check shards
   227  		shardIDs := shards.listShards()
   228  		require.Equal(t, len(shardIDs), 3)
   229  
   230  		shard10, err := shards.getShard(10)
   231  		require.NoError(t, err)
   232  		require.NotNil(t, shard10)
   233  		require.Equal(t, len(shard10.workingReplicas()), 0)
   234  		require.Equal(t, len(shard10.expiredReplicas()), 1)
   235  
   236  		shard12, err := shards.getShard(12)
   237  		require.NoError(t, err)
   238  		require.NotNil(t, shard12)
   239  		require.Equal(t, len(shard12.workingReplicas()), 2)
   240  		require.Equal(t, len(shard12.expiredReplicas()), 0)
   241  
   242  		shard14, err := shards.getShard(14)
   243  		require.NoError(t, err)
   244  		require.NotNil(t, shard14)
   245  		require.Equal(t, len(shard14.workingReplicas()), 1)
   246  		require.Equal(t, len(shard14.expiredReplicas()), 1)
   247  	}
   248  }
   249  
   250  func mockTnShardInfo(shardID, replicaID uint64) pb.TNShardInfo {
   251  	return pb.TNShardInfo{
   252  		ShardID:   shardID,
   253  		ReplicaID: replicaID,
   254  	}
   255  }