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 }