github.com/matrixorigin/matrixone@v1.2.0/pkg/hakeeper/bootstrap/bootstrap_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 bootstrap 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 22 "github.com/matrixorigin/matrixone/pkg/common/moerr" 23 "github.com/matrixorigin/matrixone/pkg/common/runtime" 24 "github.com/matrixorigin/matrixone/pkg/hakeeper/checkers/util" 25 "github.com/matrixorigin/matrixone/pkg/logutil" 26 pb "github.com/matrixorigin/matrixone/pkg/pb/logservice" 27 "github.com/matrixorigin/matrixone/pkg/pb/metadata" 28 "github.com/stretchr/testify/assert" 29 ) 30 31 func TestMain(m *testing.M) { 32 logutil.SetupMOLogger(&logutil.LogConfig{ 33 Level: "debug", 34 Format: "console", 35 }) 36 37 runtime.SetupProcessLevelRuntime(runtime.NewRuntime(metadata.ServiceType_LOG, "test", logutil.GetGlobalLogger())) 38 m.Run() 39 } 40 41 func TestNewBootstrapManager(t *testing.T) { 42 cases := []struct { 43 cluster pb.ClusterInfo 44 expected *Manager 45 }{ 46 { 47 cluster: pb.ClusterInfo{}, 48 expected: &Manager{}, 49 }, 50 { 51 cluster: pb.ClusterInfo{ 52 TNShards: []metadata.TNShardRecord{{ 53 ShardID: 1, 54 LogShardID: 1, 55 }}, 56 LogShards: []metadata.LogShardRecord{{ShardID: 2}}, 57 }, 58 expected: &Manager{ 59 cluster: pb.ClusterInfo{ 60 TNShards: []metadata.TNShardRecord{{ 61 ShardID: 1, 62 LogShardID: 1, 63 }}, 64 LogShards: []metadata.LogShardRecord{{ShardID: 2}}, 65 }, 66 }, 67 }, 68 } 69 70 for _, c := range cases { 71 bm := NewBootstrapManager(c.cluster) 72 assert.Equal(t, c.expected.cluster, bm.cluster) 73 c.expected.cluster = pb.ClusterInfo{XXX_sizecache: 1} 74 assert.NotEqual(t, c.expected.cluster, bm.cluster) 75 } 76 } 77 78 func TestBootstrap(t *testing.T) { 79 cases := []struct { 80 desc string 81 82 cluster pb.ClusterInfo 83 tn pb.TNState 84 log pb.LogState 85 86 expectedNum int 87 expectedInitialMembers map[uint64]string 88 expectedTNLogShardID uint64 89 err error 90 }{ 91 { 92 desc: "1 log shard with 1 replicas", 93 94 cluster: pb.ClusterInfo{ 95 LogShards: []metadata.LogShardRecord{{ 96 ShardID: 1, 97 NumberOfReplicas: 1, 98 }}, 99 }, 100 log: pb.LogState{ 101 Stores: map[string]pb.LogStoreInfo{ 102 "log-a": {Tick: 100}, 103 }, 104 }, 105 106 expectedNum: 1, 107 expectedInitialMembers: map[uint64]string{ 108 1: "log-a", 109 }, 110 }, 111 { 112 desc: "1 log shard with 3 replicas and 1 tn shard", 113 114 cluster: pb.ClusterInfo{ 115 TNShards: []metadata.TNShardRecord{{ShardID: 1, LogShardID: 1}}, 116 LogShards: []metadata.LogShardRecord{{ 117 ShardID: 1, 118 NumberOfReplicas: 3, 119 }}, 120 }, 121 tn: pb.TNState{ 122 Stores: map[string]pb.TNStoreInfo{"dn-a": {}}, 123 }, 124 log: pb.LogState{ 125 Stores: map[string]pb.LogStoreInfo{ 126 "log-a": {Tick: 100}, 127 "log-b": {Tick: 110}, 128 "log-c": {Tick: 120}, 129 "log-d": {Tick: 130}, 130 }, 131 }, 132 133 expectedNum: 4, 134 expectedInitialMembers: map[uint64]string{ 135 1: "log-d", 136 2: "log-c", 137 3: "log-b", 138 }, 139 expectedTNLogShardID: 1, 140 }, 141 { 142 desc: "ignore shard 0", 143 144 cluster: pb.ClusterInfo{ 145 TNShards: []metadata.TNShardRecord{}, 146 LogShards: []metadata.LogShardRecord{{ 147 ShardID: 0, 148 NumberOfReplicas: 3, 149 }}, 150 }, 151 tn: pb.TNState{ 152 Stores: map[string]pb.TNStoreInfo{}, 153 }, 154 log: pb.LogState{ 155 Stores: map[string]pb.LogStoreInfo{ 156 "log-a": {Tick: 100}, 157 "log-b": {Tick: 110}, 158 }, 159 }, 160 161 expectedNum: 0, 162 err: nil, 163 }, 164 { 165 desc: "1 log shard with 3 replicas and 1 tn shard", 166 167 cluster: pb.ClusterInfo{ 168 TNShards: []metadata.TNShardRecord{{ShardID: 1, LogShardID: 1}}, 169 LogShards: []metadata.LogShardRecord{{ 170 ShardID: 1, 171 NumberOfReplicas: 3, 172 }}, 173 }, 174 tn: pb.TNState{ 175 Stores: map[string]pb.TNStoreInfo{"dn-a": {}}, 176 }, 177 log: pb.LogState{ 178 Stores: map[string]pb.LogStoreInfo{ 179 "log-a": {Tick: 100}, 180 "log-b": {Tick: 110}, 181 "log-c": {Tick: 120}, 182 "log-d": {Tick: 130}, 183 }, 184 }, 185 186 expectedNum: 4, 187 expectedInitialMembers: map[uint64]string{ 188 1: "log-d", 189 2: "log-c", 190 3: "log-b", 191 }, 192 expectedTNLogShardID: 1, 193 }, 194 } 195 196 for i, c := range cases { 197 fmt.Printf("case %v: %s\n", i, c.desc) 198 199 alloc := util.NewTestIDAllocator(0) 200 bm := NewBootstrapManager(c.cluster) 201 output, err := bm.Bootstrap(alloc, c.tn, c.log) 202 assert.Equal(t, c.err, err) 203 if err != nil { 204 continue 205 } 206 assert.Equal(t, c.expectedNum, len(output)) 207 if len(output) != 0 { 208 assert.Equal(t, c.expectedInitialMembers, output[0].ConfigChange.InitialMembers) 209 assert.Equal(t, pb.StartReplica, output[0].ConfigChange.ChangeType) 210 } 211 212 for _, command := range output { 213 if command.ServiceType == pb.TNService { 214 assert.Equal(t, c.expectedTNLogShardID, command.ConfigChange.Replica.LogShardID) 215 } 216 } 217 } 218 } 219 220 func TestCheckBootstrap(t *testing.T) { 221 cases := []struct { 222 desc string 223 224 cluster pb.ClusterInfo 225 log pb.LogState 226 227 expected bool 228 }{ 229 { 230 desc: "failed to start 1 replica", 231 cluster: pb.ClusterInfo{ 232 LogShards: []metadata.LogShardRecord{ 233 {ShardID: 1, NumberOfReplicas: 1}, 234 }, 235 }, 236 log: pb.LogState{ 237 Shards: map[uint64]pb.LogShardInfo{ 238 1: {ShardID: 1, Replicas: map[uint64]string{}}, 239 }, 240 }, 241 expected: false, 242 }, 243 { 244 desc: "successfully started 1 replica", 245 cluster: pb.ClusterInfo{ 246 LogShards: []metadata.LogShardRecord{ 247 {ShardID: 1, NumberOfReplicas: 1}, 248 }, 249 }, 250 log: pb.LogState{ 251 Shards: map[uint64]pb.LogShardInfo{ 252 1: {ShardID: 1, Replicas: map[uint64]string{1: "a"}}, 253 }, 254 }, 255 expected: true, 256 }, 257 { 258 desc: "successfully started 3 replicas", 259 cluster: pb.ClusterInfo{ 260 LogShards: []metadata.LogShardRecord{ 261 {ShardID: 1, NumberOfReplicas: 3}, 262 {ShardID: 2, NumberOfReplicas: 3}, 263 {ShardID: 3, NumberOfReplicas: 3}, 264 }, 265 }, 266 log: pb.LogState{ 267 Shards: map[uint64]pb.LogShardInfo{ 268 1: {ShardID: 1, Replicas: map[uint64]string{1: "a", 2: "b"}}, 269 2: {ShardID: 2, Replicas: map[uint64]string{1: "a", 3: "c"}}, 270 3: {ShardID: 3, Replicas: map[uint64]string{2: "b", 3: "c"}}, 271 }, 272 }, 273 expected: true, 274 }, 275 { 276 desc: "shard 1 not started", 277 cluster: pb.ClusterInfo{ 278 LogShards: []metadata.LogShardRecord{ 279 {ShardID: 1, NumberOfReplicas: 3}, 280 {ShardID: 2, NumberOfReplicas: 3}, 281 {ShardID: 3, NumberOfReplicas: 3}, 282 }, 283 }, 284 log: pb.LogState{ 285 Shards: map[uint64]pb.LogShardInfo{ 286 1: {ShardID: 1, Replicas: map[uint64]string{1: "a"}}, 287 2: {ShardID: 2, Replicas: map[uint64]string{1: "a", 3: "c"}}, 288 3: {ShardID: 3, Replicas: map[uint64]string{2: "b", 3: "c"}}, 289 }, 290 }, 291 expected: false, 292 }, 293 { 294 desc: "shard 1 not exists in log state", 295 cluster: pb.ClusterInfo{ 296 LogShards: []metadata.LogShardRecord{ 297 {ShardID: 1, NumberOfReplicas: 3}, 298 }, 299 }, 300 log: pb.LogState{ 301 Shards: map[uint64]pb.LogShardInfo{}, 302 }, 303 expected: false, 304 }, 305 } 306 307 for i, c := range cases { 308 fmt.Printf("case %v: %s\n", i, c.desc) 309 bm := NewBootstrapManager(c.cluster) 310 output := bm.CheckBootstrap(c.log) 311 assert.Equal(t, c.expected, output) 312 } 313 } 314 315 func TestSortLogStores(t *testing.T) { 316 cases := []struct { 317 logStores map[string]pb.LogStoreInfo 318 expected []string 319 }{{ 320 logStores: map[string]pb.LogStoreInfo{ 321 "a": {Tick: 100}, 322 "b": {Tick: 120}, 323 "c": {Tick: 90}, 324 "d": {Tick: 95}, 325 }, 326 expected: []string{"b", "a", "d", "c"}, 327 }} 328 329 for _, c := range cases { 330 output := logStoresSortedByTick(c.logStores) 331 assert.Equal(t, c.expected, output) 332 } 333 } 334 335 func TestSortTNStores(t *testing.T) { 336 cases := []struct { 337 tnStores map[string]pb.TNStoreInfo 338 expected []string 339 }{{ 340 tnStores: map[string]pb.TNStoreInfo{ 341 "a": {Tick: 100}, 342 "b": {Tick: 120}, 343 "c": {Tick: 90}, 344 "d": {Tick: 95}, 345 }, 346 expected: []string{"b", "a", "d", "c"}, 347 }} 348 349 for _, c := range cases { 350 output := tnStoresSortedByTick(c.tnStores) 351 assert.Equal(t, c.expected, output) 352 } 353 } 354 355 func TestIssue3814(t *testing.T) { 356 cases := []struct { 357 desc string 358 359 cluster pb.ClusterInfo 360 tn pb.TNState 361 log pb.LogState 362 363 expected error 364 }{ 365 { 366 desc: "case not enough log store", 367 cluster: pb.ClusterInfo{ 368 LogShards: []metadata.LogShardRecord{{ 369 ShardID: 1, 370 NumberOfReplicas: 3, 371 }}, 372 }, 373 log: pb.LogState{}, 374 expected: moerr.NewInternalError(context.TODO(), "not enough log stores"), 375 }, 376 { 377 desc: "case not enough tn stores", 378 cluster: pb.ClusterInfo{ 379 TNShards: []metadata.TNShardRecord{{ 380 ShardID: 1, 381 LogShardID: 1, 382 }}, 383 }, 384 tn: pb.TNState{ 385 Stores: map[string]pb.TNStoreInfo{}, 386 }, 387 expected: nil, 388 }, 389 } 390 391 for _, c := range cases { 392 alloc := util.NewTestIDAllocator(0) 393 bm := NewBootstrapManager(c.cluster) 394 _, err := bm.Bootstrap(alloc, c.tn, c.log) 395 assert.Equal(t, c.expected, err) 396 } 397 } 398 399 func TestIssue3845(t *testing.T) { 400 cases := []struct { 401 desc string 402 403 cluster pb.ClusterInfo 404 log pb.LogState 405 406 expected bool 407 }{ 408 { 409 desc: "shardID is 0", 410 cluster: pb.ClusterInfo{ 411 LogShards: []metadata.LogShardRecord{{ 412 ShardID: 0, 413 NumberOfReplicas: 1, 414 }}, 415 }, 416 log: pb.LogState{ 417 Shards: map[uint64]pb.LogShardInfo{0: { 418 ShardID: 0, 419 Replicas: map[uint64]string{1: "a"}, 420 }}, 421 Stores: map[string]pb.LogStoreInfo{"a": { 422 Tick: 0, 423 Replicas: []pb.LogReplicaInfo{{ 424 LogShardInfo: pb.LogShardInfo{ 425 ShardID: 0, 426 Replicas: map[uint64]string{1: "a"}, 427 }, 428 ReplicaID: 1, 429 }}, 430 }}, 431 }, 432 expected: true, 433 }, 434 } 435 436 for _, c := range cases { 437 bm := NewBootstrapManager(c.cluster) 438 output := bm.CheckBootstrap(c.log) 439 assert.Equal(t, c.expected, output) 440 } 441 }