github.com/matrixorigin/matrixone@v1.2.0/pkg/hakeeper/rsm_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 hakeeper 16 17 import ( 18 "bytes" 19 "sort" 20 "testing" 21 22 sm "github.com/lni/dragonboat/v4/statemachine" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 26 pb "github.com/matrixorigin/matrixone/pkg/pb/logservice" 27 "github.com/matrixorigin/matrixone/pkg/pb/metadata" 28 ) 29 30 func TestAssignID(t *testing.T) { 31 tsm := NewStateMachine(0, 1).(*stateMachine) 32 assert.Equal(t, uint64(0), tsm.state.NextID) 33 assert.Equal(t, uint64(1), tsm.assignID()) 34 assert.Equal(t, uint64(1), tsm.state.NextID) 35 } 36 37 func TestHAKeeperStateMachineCanBeCreated(t *testing.T) { 38 defer func() { 39 if r := recover(); r == nil { 40 t.Fatalf("failed to panic") 41 } 42 }() 43 tsm := NewStateMachine(0, 1).(*stateMachine) 44 assert.Equal(t, uint64(1), tsm.replicaID) 45 NewStateMachine(1, 1) 46 } 47 48 func TestHAKeeperStateMachineSnapshot(t *testing.T) { 49 tsm1 := NewStateMachine(0, 1).(*stateMachine) 50 tsm2 := NewStateMachine(0, 2).(*stateMachine) 51 tsm1.state.NextID = 12345 52 tsm1.state.LogShards["test1"] = 23456 53 tsm1.state.LogShards["test2"] = 34567 54 55 buf := bytes.NewBuffer(nil) 56 assert.Nil(t, tsm1.SaveSnapshot(buf, nil, nil)) 57 assert.Nil(t, tsm2.RecoverFromSnapshot(buf, nil, nil)) 58 assert.Equal(t, tsm1.state.NextID, tsm2.state.NextID) 59 assert.Equal(t, tsm1.state.LogShards, tsm2.state.LogShards) 60 assert.True(t, tsm1.replicaID != tsm2.replicaID) 61 } 62 63 func TestHAKeeperCanBeClosed(t *testing.T) { 64 tsm1 := NewStateMachine(0, 1).(*stateMachine) 65 assert.Nil(t, tsm1.Close()) 66 } 67 68 func TestHAKeeperTick(t *testing.T) { 69 tsm1 := NewStateMachine(0, 1).(*stateMachine) 70 assert.Equal(t, uint64(0), tsm1.state.Tick) 71 cmd := GetTickCmd() 72 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 73 assert.NoError(t, err) 74 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 75 assert.NoError(t, err) 76 assert.Equal(t, uint64(2), tsm1.state.Tick) 77 } 78 79 func TestHandleLogHeartbeat(t *testing.T) { 80 tsm1 := NewStateMachine(0, 1).(*stateMachine) 81 cmd := GetTickCmd() 82 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 83 assert.NoError(t, err) 84 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 85 assert.NoError(t, err) 86 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 87 assert.NoError(t, err) 88 89 hb := pb.LogStoreHeartbeat{ 90 UUID: "uuid1", 91 RaftAddress: "localhost:9090", 92 ServiceAddress: "localhost:9091", 93 GossipAddress: "localhost:9092", 94 Replicas: []pb.LogReplicaInfo{ 95 { 96 LogShardInfo: pb.LogShardInfo{ 97 ShardID: 100, 98 Replicas: map[uint64]string{ 99 200: "localhost:8000", 100 300: "localhost:9000", 101 }, 102 Epoch: 200, 103 LeaderID: 200, 104 Term: 10, 105 }, 106 }, 107 { 108 LogShardInfo: pb.LogShardInfo{ 109 ShardID: 101, 110 Replicas: map[uint64]string{ 111 201: "localhost:8000", 112 301: "localhost:9000", 113 }, 114 Epoch: 202, 115 LeaderID: 201, 116 Term: 30, 117 }, 118 }, 119 }, 120 } 121 data, err := hb.Marshal() 122 require.NoError(t, err) 123 cmd = GetLogStoreHeartbeatCmd(data) 124 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 125 assert.NoError(t, err) 126 s := tsm1.state.LogState 127 assert.Equal(t, 1, len(s.Stores)) 128 lsinfo, ok := s.Stores[hb.UUID] 129 require.True(t, ok) 130 assert.Equal(t, uint64(3), lsinfo.Tick) 131 assert.Equal(t, hb.RaftAddress, lsinfo.RaftAddress) 132 assert.Equal(t, hb.ServiceAddress, lsinfo.ServiceAddress) 133 assert.Equal(t, hb.GossipAddress, lsinfo.GossipAddress) 134 assert.Equal(t, 2, len(lsinfo.Replicas)) 135 assert.Equal(t, hb.Replicas, lsinfo.Replicas) 136 } 137 138 func TestHandleTNHeartbeat(t *testing.T) { 139 tsm1 := NewStateMachine(0, 1).(*stateMachine) 140 cmd := GetTickCmd() 141 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 142 assert.NoError(t, err) 143 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 144 assert.NoError(t, err) 145 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 146 assert.NoError(t, err) 147 148 hb := pb.TNStoreHeartbeat{ 149 UUID: "uuid1", 150 Shards: []pb.TNShardInfo{ 151 {ShardID: 1, ReplicaID: 1}, 152 {ShardID: 2, ReplicaID: 1}, 153 {ShardID: 3, ReplicaID: 1}, 154 }, 155 } 156 data, err := hb.Marshal() 157 require.NoError(t, err) 158 cmd = GetTNStoreHeartbeatCmd(data) 159 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 160 assert.NoError(t, err) 161 s := tsm1.state.TNState 162 assert.Equal(t, 1, len(s.Stores)) 163 tninfo, ok := s.Stores[hb.UUID] 164 assert.True(t, ok) 165 assert.Equal(t, uint64(3), tninfo.Tick) 166 require.Equal(t, 3, len(tninfo.Shards)) 167 assert.Equal(t, hb.Shards, tninfo.Shards) 168 } 169 170 func TestHandleCNHeartbeat(t *testing.T) { 171 tsm1 := NewStateMachine(0, 1).(*stateMachine) 172 cmd := GetTickCmd() 173 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 174 assert.NoError(t, err) 175 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 176 assert.NoError(t, err) 177 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 178 assert.NoError(t, err) 179 180 hb := pb.CNStoreHeartbeat{ 181 UUID: "uuid1", 182 } 183 data, err := hb.Marshal() 184 require.NoError(t, err) 185 cmd = GetCNStoreHeartbeatCmd(data) 186 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 187 assert.NoError(t, err) 188 s := tsm1.state.CNState 189 assert.Equal(t, 1, len(s.Stores)) 190 cninfo, ok := s.Stores[hb.UUID] 191 assert.True(t, ok) 192 assert.Equal(t, uint64(3), cninfo.Tick) 193 } 194 195 func TestGetIDCmd(t *testing.T) { 196 tsm1 := NewStateMachine(0, 1).(*stateMachine) 197 tsm1.state.State = pb.HAKeeperRunning 198 cmd := GetAllocateIDCmd(pb.CNAllocateID{Batch: 100}) 199 result, err := tsm1.Update(sm.Entry{Cmd: cmd}) 200 assert.NoError(t, err) 201 assert.Equal(t, sm.Result{Value: 1}, result) 202 result, err = tsm1.Update(sm.Entry{Cmd: cmd}) 203 assert.NoError(t, err) 204 assert.Equal(t, sm.Result{Value: 101}, result) 205 assert.Equal(t, uint64(201), tsm1.assignID()) 206 207 result, err = tsm1.Update(sm.Entry{Cmd: cmd}) 208 assert.NoError(t, err) 209 assert.Equal(t, sm.Result{Value: 202}, result) 210 } 211 212 func TestAllocateIDByKeyCmd(t *testing.T) { 213 tsm1 := NewStateMachine(0, 1).(*stateMachine) 214 tsm1.state.State = pb.HAKeeperRunning 215 216 cmd := GetAllocateIDCmd(pb.CNAllocateID{Key: "k1", Batch: 100}) 217 218 result, err := tsm1.Update(sm.Entry{Cmd: cmd}) 219 assert.NoError(t, err) 220 assert.Equal(t, sm.Result{Value: 1}, result) 221 222 result, err = tsm1.Update(sm.Entry{Cmd: cmd}) 223 assert.NoError(t, err) 224 assert.Equal(t, sm.Result{Value: 101}, result) 225 226 assert.Equal(t, uint64(201), tsm1.assignIDByKey("k1")) 227 228 result, err = tsm1.Update(sm.Entry{Cmd: cmd}) 229 assert.NoError(t, err) 230 assert.Equal(t, sm.Result{Value: 202}, result) 231 232 cmd = GetAllocateIDCmd(pb.CNAllocateID{Key: "k2", Batch: 50}) 233 234 result, err = tsm1.Update(sm.Entry{Cmd: cmd}) 235 assert.NoError(t, err) 236 assert.Equal(t, sm.Result{Value: 1}, result) 237 238 result, err = tsm1.Update(sm.Entry{Cmd: cmd}) 239 assert.NoError(t, err) 240 assert.Equal(t, sm.Result{Value: 51}, result) 241 242 assert.Equal(t, uint64(101), tsm1.assignIDByKey("k2")) 243 } 244 245 func TestUpdateScheduleCommandsCmd(t *testing.T) { 246 tsm1 := NewStateMachine(0, 1).(*stateMachine) 247 sc1 := pb.ScheduleCommand{ 248 UUID: "uuid1", 249 ConfigChange: &pb.ConfigChange{ 250 Replica: pb.Replica{ 251 ShardID: 1, 252 }, 253 }, 254 } 255 sc2 := pb.ScheduleCommand{ 256 UUID: "uuid2", 257 ConfigChange: &pb.ConfigChange{ 258 Replica: pb.Replica{ 259 ShardID: 2, 260 }, 261 }, 262 } 263 sc3 := pb.ScheduleCommand{ 264 UUID: "uuid1", 265 ConfigChange: &pb.ConfigChange{ 266 Replica: pb.Replica{ 267 ShardID: 3, 268 }, 269 }, 270 } 271 sc4 := pb.ScheduleCommand{ 272 UUID: "uuid3", 273 ConfigChange: &pb.ConfigChange{ 274 Replica: pb.Replica{ 275 ShardID: 4, 276 }, 277 }, 278 } 279 280 b := pb.CommandBatch{ 281 Term: 101, 282 Commands: []pb.ScheduleCommand{sc1, sc2, sc3}, 283 } 284 cmd := GetUpdateCommandsCmd(b.Term, b.Commands) 285 result, err := tsm1.Update(sm.Entry{Cmd: cmd}) 286 require.NoError(t, err) 287 assert.Equal(t, sm.Result{}, result) 288 assert.Equal(t, b.Term, tsm1.state.Term) 289 require.Equal(t, 2, len(tsm1.state.ScheduleCommands)) 290 l1, ok := tsm1.state.ScheduleCommands["uuid1"] 291 assert.True(t, ok) 292 assert.Equal(t, pb.CommandBatch{Commands: []pb.ScheduleCommand{sc1, sc3}}, l1) 293 l2, ok := tsm1.state.ScheduleCommands["uuid2"] 294 assert.True(t, ok) 295 assert.Equal(t, pb.CommandBatch{Commands: []pb.ScheduleCommand{sc2}}, l2) 296 297 cmd2 := GetUpdateCommandsCmd(b.Term-1, 298 []pb.ScheduleCommand{sc1, sc2, sc3, sc4}) 299 result, err = tsm1.Update(sm.Entry{Cmd: cmd2}) 300 require.NoError(t, err) 301 assert.Equal(t, sm.Result{}, result) 302 assert.Equal(t, b.Term, tsm1.state.Term) 303 require.Equal(t, 2, len(tsm1.state.ScheduleCommands)) 304 l1, ok = tsm1.state.ScheduleCommands["uuid1"] 305 assert.True(t, ok) 306 assert.Equal(t, pb.CommandBatch{Commands: []pb.ScheduleCommand{sc1, sc3}}, l1) 307 l2, ok = tsm1.state.ScheduleCommands["uuid2"] 308 assert.True(t, ok) 309 assert.Equal(t, pb.CommandBatch{Commands: []pb.ScheduleCommand{sc2}}, l2) 310 } 311 312 func TestScheduleCommandQuery(t *testing.T) { 313 tsm1 := NewStateMachine(0, 1).(*stateMachine) 314 sc1 := pb.ScheduleCommand{ 315 UUID: "uuid1", 316 ConfigChange: &pb.ConfigChange{ 317 Replica: pb.Replica{ 318 ShardID: 1, 319 }, 320 }, 321 } 322 sc2 := pb.ScheduleCommand{ 323 UUID: "uuid2", 324 ConfigChange: &pb.ConfigChange{ 325 Replica: pb.Replica{ 326 ShardID: 2, 327 }, 328 }, 329 } 330 sc3 := pb.ScheduleCommand{ 331 UUID: "uuid1", 332 ConfigChange: &pb.ConfigChange{ 333 Replica: pb.Replica{ 334 ShardID: 3, 335 }, 336 }, 337 } 338 b := pb.CommandBatch{ 339 Term: 101, 340 Commands: []pb.ScheduleCommand{sc1, sc2, sc3}, 341 } 342 cmd := GetUpdateCommandsCmd(b.Term, b.Commands) 343 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 344 require.NoError(t, err) 345 r, err := tsm1.Lookup(&ScheduleCommandQuery{UUID: "uuid1"}) 346 require.NoError(t, err) 347 cb, ok := r.(*pb.CommandBatch) 348 require.True(t, ok) 349 assert.Equal(t, 2, len(cb.Commands)) 350 b = pb.CommandBatch{ 351 Commands: []pb.ScheduleCommand{sc1, sc3}, 352 } 353 assert.Equal(t, b, *cb) 354 } 355 356 func TestClusterDetailsQuery(t *testing.T) { 357 tsm := NewStateMachine(0, 1).(*stateMachine) 358 tsm.state.CNState = pb.CNState{ 359 Stores: make(map[string]pb.CNStoreInfo), 360 } 361 tsm.state.CNState.Stores["uuid1"] = pb.CNStoreInfo{ 362 Tick: 1, 363 ServiceAddress: "addr1", 364 } 365 tsm.state.CNState.Stores["uuid2"] = pb.CNStoreInfo{ 366 Tick: 2, 367 ServiceAddress: "addr2", 368 } 369 tsm.state.TNState = pb.TNState{ 370 Stores: make(map[string]pb.TNStoreInfo), 371 } 372 tsm.state.TNState.Stores["uuid3"] = pb.TNStoreInfo{ 373 Tick: 3, 374 ServiceAddress: "addr3", 375 Shards: []pb.TNShardInfo{ 376 { 377 ShardID: 2, 378 ReplicaID: 1, 379 }, 380 }, 381 LogtailServerAddress: "addr4", 382 } 383 tsm.state.LogState.Shards[1] = pb.LogShardInfo{ 384 ShardID: 1, 385 Replicas: map[uint64]string{1: "store-1", 2: "store-2", 3: "store-3"}, 386 Epoch: 1, LeaderID: 1, Term: 1, 387 } 388 389 tsm.state.LogState.Stores["store-1"] = pb.LogStoreInfo{ 390 Tick: 100, 391 ServiceAddress: "addr-log-1", 392 Replicas: []pb.LogReplicaInfo{{ 393 LogShardInfo: pb.LogShardInfo{ 394 ShardID: 1, 395 Replicas: map[uint64]string{1: "store-1", 2: "store-2", 3: "store-3"}, 396 Epoch: 1, LeaderID: 1, Term: 1, 397 }, ReplicaID: 1, 398 }}, 399 } 400 401 tsm.state.LogState.Stores["store-2"] = pb.LogStoreInfo{ 402 Tick: 100, 403 ServiceAddress: "addr-log-2", 404 Replicas: []pb.LogReplicaInfo{{ 405 LogShardInfo: pb.LogShardInfo{ 406 ShardID: 1, 407 Replicas: map[uint64]string{1: "store-1", 2: "store-2", 3: "store-3"}, 408 Epoch: 1, LeaderID: 1, Term: 1, 409 }, ReplicaID: 2, 410 }}, 411 } 412 413 tsm.state.LogState.Stores["store-3"] = pb.LogStoreInfo{ 414 Tick: 100, 415 ServiceAddress: "addr-log-3", 416 Replicas: []pb.LogReplicaInfo{{ 417 LogShardInfo: pb.LogShardInfo{ 418 ShardID: 1, 419 Replicas: map[uint64]string{1: "store-1", 2: "store-2", 3: "store-3"}, 420 Epoch: 1, LeaderID: 1, Term: 1, 421 }, ReplicaID: 3, 422 }}, 423 } 424 tsm.state.ProxyState.Stores["store-4"] = pb.ProxyStore{ 425 UUID: "store-4", 426 Tick: 100, 427 ListenAddress: "proxy-addr1", 428 } 429 430 v, err := tsm.Lookup(&ClusterDetailsQuery{}) 431 require.NoError(t, err) 432 expected := &pb.ClusterDetails{ 433 TNStores: []pb.TNStore{ 434 { 435 UUID: "uuid3", 436 Tick: 3, 437 ServiceAddress: "addr3", 438 Shards: []pb.TNShardInfo{ 439 { 440 ShardID: 2, 441 ReplicaID: 1, 442 }, 443 }, 444 LogtailServerAddress: "addr4", 445 }, 446 }, 447 CNStores: []pb.CNStore{ 448 { 449 UUID: "uuid1", 450 Tick: 1, 451 ServiceAddress: "addr1", 452 }, 453 { 454 UUID: "uuid2", 455 Tick: 2, 456 ServiceAddress: "addr2", 457 }, 458 }, 459 LogStores: []pb.LogStore{ 460 { 461 UUID: "store-1", 462 ServiceAddress: "addr-log-1", 463 Tick: 100, 464 State: 0, 465 Replicas: []pb.LogReplicaInfo{{ 466 LogShardInfo: pb.LogShardInfo{ 467 ShardID: 1, 468 Replicas: map[uint64]string{1: "store-1", 2: "store-2", 3: "store-3"}, 469 Epoch: 1, LeaderID: 1, Term: 1, 470 }, ReplicaID: 1, 471 }}, 472 }, 473 { 474 UUID: "store-2", 475 ServiceAddress: "addr-log-2", 476 Tick: 100, 477 State: 0, 478 Replicas: []pb.LogReplicaInfo{{ 479 LogShardInfo: pb.LogShardInfo{ 480 ShardID: 1, 481 Replicas: map[uint64]string{1: "store-1", 2: "store-2", 3: "store-3"}, 482 Epoch: 1, LeaderID: 1, Term: 1, 483 }, ReplicaID: 2, 484 }}, 485 }, 486 { 487 UUID: "store-3", 488 ServiceAddress: "addr-log-3", 489 Tick: 100, 490 State: 0, 491 Replicas: []pb.LogReplicaInfo{{ 492 LogShardInfo: pb.LogShardInfo{ 493 ShardID: 1, 494 Replicas: map[uint64]string{1: "store-1", 2: "store-2", 3: "store-3"}, 495 Epoch: 1, LeaderID: 1, Term: 1, 496 }, ReplicaID: 3, 497 }}, 498 }, 499 }, 500 ProxyStores: []pb.ProxyStore{ 501 { 502 UUID: "store-4", 503 Tick: 100, 504 ListenAddress: "proxy-addr1", 505 }, 506 }, 507 } 508 result := v.(*pb.ClusterDetails) 509 sort.Slice(result.CNStores, func(i, j int) bool { 510 return result.CNStores[i].UUID < result.CNStores[j].UUID 511 }) 512 sort.Slice(result.LogStores, func(i, j int) bool { 513 return result.LogStores[i].UUID < result.LogStores[j].UUID 514 }) 515 assert.Equal(t, expected, result) 516 } 517 518 func TestInitialState(t *testing.T) { 519 rsm := NewStateMachine(0, 1).(*stateMachine) 520 assert.Equal(t, pb.HAKeeperCreated, rsm.state.State) 521 } 522 523 func TestSetState(t *testing.T) { 524 tests := []struct { 525 initialState pb.HAKeeperState 526 newState pb.HAKeeperState 527 result pb.HAKeeperState 528 }{ 529 {pb.HAKeeperCreated, pb.HAKeeperBootstrapping, pb.HAKeeperCreated}, 530 {pb.HAKeeperCreated, pb.HAKeeperBootstrapFailed, pb.HAKeeperCreated}, 531 {pb.HAKeeperCreated, pb.HAKeeperRunning, pb.HAKeeperCreated}, 532 {pb.HAKeeperCreated, pb.HAKeeperCreated, pb.HAKeeperCreated}, 533 {pb.HAKeeperCreated, pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperCreated}, 534 535 {pb.HAKeeperBootstrapping, pb.HAKeeperCreated, pb.HAKeeperBootstrapping}, 536 {pb.HAKeeperBootstrapping, pb.HAKeeperBootstrapFailed, pb.HAKeeperBootstrapping}, 537 {pb.HAKeeperBootstrapping, pb.HAKeeperRunning, pb.HAKeeperBootstrapping}, 538 {pb.HAKeeperBootstrapping, pb.HAKeeperBootstrapping, pb.HAKeeperBootstrapping}, 539 {pb.HAKeeperBootstrapping, pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperBootstrapCommandsReceived}, 540 541 {pb.HAKeeperBootstrapFailed, pb.HAKeeperBootstrapFailed, pb.HAKeeperBootstrapFailed}, 542 {pb.HAKeeperBootstrapFailed, pb.HAKeeperCreated, pb.HAKeeperBootstrapFailed}, 543 {pb.HAKeeperBootstrapFailed, pb.HAKeeperBootstrapping, pb.HAKeeperBootstrapFailed}, 544 {pb.HAKeeperBootstrapFailed, pb.HAKeeperRunning, pb.HAKeeperBootstrapFailed}, 545 {pb.HAKeeperBootstrapFailed, pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperBootstrapFailed}, 546 547 {pb.HAKeeperRunning, pb.HAKeeperRunning, pb.HAKeeperRunning}, 548 {pb.HAKeeperRunning, pb.HAKeeperCreated, pb.HAKeeperRunning}, 549 {pb.HAKeeperRunning, pb.HAKeeperBootstrapping, pb.HAKeeperRunning}, 550 {pb.HAKeeperRunning, pb.HAKeeperBootstrapFailed, pb.HAKeeperRunning}, 551 {pb.HAKeeperRunning, pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperRunning}, 552 553 {pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperCreated, pb.HAKeeperBootstrapCommandsReceived}, 554 {pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperBootstrapping, pb.HAKeeperBootstrapCommandsReceived}, 555 {pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperBootstrapCommandsReceived}, 556 {pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperBootstrapFailed, pb.HAKeeperBootstrapFailed}, 557 {pb.HAKeeperBootstrapCommandsReceived, pb.HAKeeperRunning, pb.HAKeeperRunning}, 558 } 559 560 for _, tt := range tests { 561 rsm := stateMachine{ 562 state: pb.HAKeeperRSMState{ 563 State: tt.initialState, 564 }, 565 } 566 cmd := GetSetStateCmd(tt.newState) 567 _, err := rsm.Update(sm.Entry{Cmd: cmd}) 568 require.NoError(t, err) 569 assert.Equal(t, tt.result, rsm.state.State) 570 } 571 } 572 573 func TestSetTaskSchedulerState(t *testing.T) { 574 tests := []struct { 575 initialState pb.TaskSchedulerState 576 newState pb.TaskSchedulerState 577 result pb.TaskSchedulerState 578 }{ 579 {pb.TaskSchedulerCreated, pb.TaskSchedulerCreated, pb.TaskSchedulerCreated}, 580 {pb.TaskSchedulerCreated, pb.TaskSchedulerRunning, pb.TaskSchedulerCreated}, 581 {pb.TaskSchedulerCreated, pb.TaskSchedulerStopped, pb.TaskSchedulerCreated}, 582 583 {pb.TaskSchedulerRunning, pb.TaskSchedulerCreated, pb.TaskSchedulerRunning}, 584 {pb.TaskSchedulerRunning, pb.TaskSchedulerRunning, pb.TaskSchedulerRunning}, 585 {pb.TaskSchedulerRunning, pb.TaskSchedulerStopped, pb.TaskSchedulerStopped}, 586 587 {pb.TaskSchedulerStopped, pb.TaskSchedulerCreated, pb.TaskSchedulerStopped}, 588 {pb.TaskSchedulerStopped, pb.TaskSchedulerRunning, pb.TaskSchedulerRunning}, 589 {pb.TaskSchedulerStopped, pb.TaskSchedulerStopped, pb.TaskSchedulerStopped}, 590 } 591 592 for _, tt := range tests { 593 rsm := stateMachine{ 594 state: pb.HAKeeperRSMState{ 595 State: pb.HAKeeperRunning, 596 TaskSchedulerState: tt.initialState, 597 }, 598 } 599 cmd := GetSetTaskSchedulerStateCmd(tt.newState) 600 _, err := rsm.Update(sm.Entry{Cmd: cmd}) 601 require.NoError(t, err) 602 assert.Equal(t, tt.result, rsm.state.TaskSchedulerState) 603 } 604 } 605 606 func TestInitialClusterRequestCmd(t *testing.T) { 607 nextIDByKey := map[string]uint64{"a": 1, "b": 2} 608 cmd := GetInitialClusterRequestCmd(2, 2, 3, 10, nextIDByKey) 609 req := parseInitialClusterRequestCmd(cmd) 610 assert.Equal(t, uint64(2), req.NumOfLogShards) 611 assert.Equal(t, uint64(2), req.NumOfTNShards) 612 assert.Equal(t, uint64(3), req.NumOfLogReplicas) 613 assert.Equal(t, uint64(10), req.NextID) 614 assert.Equal(t, nextIDByKey, req.NextIDByKey) 615 } 616 617 func TestHandleInitialClusterRequestCmd(t *testing.T) { 618 nextIDByKey := map[string]uint64{"a": 1, "b": 2} 619 cmd := GetInitialClusterRequestCmd(2, 2, 3, K8SIDRangeEnd+10, nextIDByKey) 620 rsm := NewStateMachine(0, 1).(*stateMachine) 621 result, err := rsm.Update(sm.Entry{Cmd: cmd}) 622 require.NoError(t, err) 623 assert.Equal(t, sm.Result{Value: 0}, result) 624 625 expected := pb.ClusterInfo{ 626 LogShards: []metadata.LogShardRecord{ 627 { 628 ShardID: 0, 629 NumberOfReplicas: 3, 630 }, 631 { 632 ShardID: 1, 633 NumberOfReplicas: 3, 634 }, 635 { 636 ShardID: 3, 637 NumberOfReplicas: 3, 638 }, 639 }, 640 TNShards: []metadata.TNShardRecord{ 641 { 642 ShardID: 2, 643 LogShardID: 1, 644 }, 645 { 646 ShardID: 4, 647 LogShardID: 3, 648 }, 649 }, 650 } 651 652 assert.Equal(t, expected, rsm.state.ClusterInfo) 653 assert.Equal(t, pb.HAKeeperBootstrapping, rsm.state.State) 654 assert.Equal(t, K8SIDRangeEnd+10, rsm.state.NextID) 655 assert.Equal(t, nextIDByKey, rsm.state.NextIDByKey) 656 } 657 658 func TestGetCommandBatch(t *testing.T) { 659 rsm := NewStateMachine(0, 1).(*stateMachine) 660 cb := pb.CommandBatch{ 661 Term: 12345, 662 } 663 rsm.state.ScheduleCommands["uuid1"] = cb 664 result := rsm.getCommandBatch("uuid1") 665 var ncb pb.CommandBatch 666 require.NoError(t, ncb.Unmarshal(result.Data)) 667 assert.Equal(t, cb, ncb) 668 _, ok := rsm.state.ScheduleCommands["uuid1"] 669 assert.False(t, ok) 670 } 671 672 func TestHandleUpdateCNLabel(t *testing.T) { 673 uuid := "uuid1" 674 tsm1 := NewStateMachine(0, 1).(*stateMachine) 675 label := pb.CNStoreLabel{ 676 UUID: uuid, 677 Labels: map[string]metadata.LabelList{ 678 "account": {Labels: []string{"a", "b"}}, 679 "role": {Labels: []string{"1", "2"}}, 680 }, 681 } 682 cmd := GetUpdateCNLabelCmd(label) 683 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 684 assert.NoError(t, err) 685 686 s := tsm1.state.CNState 687 assert.Equal(t, 0, len(s.Stores)) 688 689 cmd = GetTickCmd() 690 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 691 assert.NoError(t, err) 692 693 hb := pb.CNStoreHeartbeat{ 694 UUID: uuid, 695 } 696 data, err := hb.Marshal() 697 require.NoError(t, err) 698 cmd = GetCNStoreHeartbeatCmd(data) 699 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 700 assert.NoError(t, err) 701 s = tsm1.state.CNState 702 assert.Equal(t, 1, len(s.Stores)) 703 704 label = pb.CNStoreLabel{ 705 UUID: uuid, 706 Labels: map[string]metadata.LabelList{ 707 "account": {Labels: []string{"a", "b"}}, 708 "role": {Labels: []string{"1", "2"}}, 709 }, 710 } 711 cmd = GetUpdateCNLabelCmd(label) 712 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 713 assert.NoError(t, err) 714 715 s = tsm1.state.CNState 716 assert.Equal(t, 1, len(s.Stores)) 717 info, ok := s.Stores[uuid] 718 assert.True(t, ok) 719 labels, ok := info.Labels["account"] 720 assert.True(t, ok) 721 assert.Equal(t, labels.Labels, []string{"a", "b"}) 722 labels, ok = info.Labels["role"] 723 assert.True(t, ok) 724 assert.Equal(t, labels.Labels, []string{"1", "2"}) 725 726 label = pb.CNStoreLabel{ 727 UUID: uuid, 728 Labels: map[string]metadata.LabelList{ 729 "role": {Labels: []string{"1", "2"}}, 730 }, 731 } 732 cmd = GetUpdateCNLabelCmd(label) 733 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 734 assert.NoError(t, err) 735 736 s = tsm1.state.CNState 737 assert.Equal(t, 1, len(s.Stores)) 738 info, ok = s.Stores[uuid] 739 assert.True(t, ok) 740 _, ok = info.Labels["account"] 741 assert.False(t, ok) 742 labels, ok = info.Labels["role"] 743 assert.True(t, ok) 744 assert.Equal(t, labels.Labels, []string{"1", "2"}) 745 } 746 747 func TestHandleUpdateCNWorkState(t *testing.T) { 748 uuid := "uuid1" 749 tsm1 := NewStateMachine(0, 1).(*stateMachine) 750 state := pb.CNWorkState{ 751 UUID: uuid, 752 State: metadata.WorkState_Unknown, 753 } 754 cmd := GetUpdateCNWorkStateCmd(state) 755 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 756 assert.NoError(t, err) 757 758 s := tsm1.state.CNState 759 assert.Equal(t, 0, len(s.Stores)) 760 761 cmd = GetTickCmd() 762 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 763 assert.NoError(t, err) 764 765 hb := pb.CNStoreHeartbeat{ 766 UUID: uuid, 767 } 768 data, err := hb.Marshal() 769 require.NoError(t, err) 770 cmd = GetCNStoreHeartbeatCmd(data) 771 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 772 assert.NoError(t, err) 773 s = tsm1.state.CNState 774 assert.Equal(t, 1, len(s.Stores)) 775 776 state = pb.CNWorkState{ 777 UUID: uuid, 778 State: metadata.WorkState_Working, 779 } 780 cmd = GetUpdateCNWorkStateCmd(state) 781 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 782 assert.NoError(t, err) 783 784 s = tsm1.state.CNState 785 assert.Equal(t, 1, len(s.Stores)) 786 info, ok := s.Stores[uuid] 787 assert.True(t, ok) 788 assert.Equal(t, metadata.WorkState_Working, info.WorkState) 789 790 state = pb.CNWorkState{ 791 UUID: uuid, 792 State: metadata.WorkState_Unknown, 793 } 794 cmd = GetUpdateCNWorkStateCmd(state) 795 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 796 assert.NoError(t, err) 797 798 s = tsm1.state.CNState 799 assert.Equal(t, 1, len(s.Stores)) 800 info, ok = s.Stores[uuid] 801 assert.True(t, ok) 802 assert.Equal(t, metadata.WorkState_Working, info.WorkState) 803 804 state = pb.CNWorkState{ 805 UUID: uuid, 806 State: metadata.WorkState_Draining, 807 } 808 cmd = GetUpdateCNWorkStateCmd(state) 809 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 810 assert.NoError(t, err) 811 812 s = tsm1.state.CNState 813 assert.Equal(t, 1, len(s.Stores)) 814 info, ok = s.Stores[uuid] 815 assert.True(t, ok) 816 assert.Equal(t, metadata.WorkState_Draining, info.WorkState) 817 } 818 819 func TestHandlePatchCNStore(t *testing.T) { 820 uuid := "uuid1" 821 tsm1 := NewStateMachine(0, 1).(*stateMachine) 822 stateLabel := pb.CNStateLabel{ 823 UUID: uuid, 824 State: metadata.WorkState_Unknown, 825 Labels: map[string]metadata.LabelList{ 826 "account": {Labels: []string{"a", "b"}}, 827 "role": {Labels: []string{"1", "2"}}, 828 }, 829 } 830 cmd := GetPatchCNStoreCmd(stateLabel) 831 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 832 assert.NoError(t, err) 833 834 s := tsm1.state.CNState 835 assert.Equal(t, 0, len(s.Stores)) 836 837 cmd = GetTickCmd() 838 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 839 assert.NoError(t, err) 840 841 hb := pb.CNStoreHeartbeat{ 842 UUID: uuid, 843 } 844 data, err := hb.Marshal() 845 require.NoError(t, err) 846 cmd = GetCNStoreHeartbeatCmd(data) 847 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 848 assert.NoError(t, err) 849 s = tsm1.state.CNState 850 assert.Equal(t, 1, len(s.Stores)) 851 852 cmd = GetPatchCNStoreCmd(stateLabel) 853 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 854 assert.NoError(t, err) 855 856 s = tsm1.state.CNState 857 assert.Equal(t, 1, len(s.Stores)) 858 info, ok := s.Stores[uuid] 859 assert.True(t, ok) 860 assert.Equal(t, metadata.WorkState_Working, info.WorkState) 861 labels, ok := info.Labels["account"] 862 assert.True(t, ok) 863 assert.Equal(t, labels.Labels, []string{"a", "b"}) 864 labels, ok = info.Labels["role"] 865 assert.True(t, ok) 866 assert.Equal(t, labels.Labels, []string{"1", "2"}) 867 868 stateLabel = pb.CNStateLabel{ 869 UUID: uuid, 870 State: metadata.WorkState_Working, 871 } 872 cmd = GetPatchCNStoreCmd(stateLabel) 873 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 874 assert.NoError(t, err) 875 876 s = tsm1.state.CNState 877 assert.Equal(t, 1, len(s.Stores)) 878 info, ok = s.Stores[uuid] 879 assert.True(t, ok) 880 assert.Equal(t, metadata.WorkState_Working, info.WorkState) 881 labels, ok = info.Labels["account"] 882 assert.True(t, ok) 883 assert.Equal(t, labels.Labels, []string{"a", "b"}) 884 labels, ok = info.Labels["role"] 885 assert.True(t, ok) 886 assert.Equal(t, labels.Labels, []string{"1", "2"}) 887 888 stateLabel = pb.CNStateLabel{ 889 UUID: uuid, 890 Labels: map[string]metadata.LabelList{ 891 "role": {Labels: []string{"1", "2"}}, 892 }, 893 } 894 cmd = GetPatchCNStoreCmd(stateLabel) 895 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 896 assert.NoError(t, err) 897 898 s = tsm1.state.CNState 899 assert.Equal(t, 1, len(s.Stores)) 900 info, ok = s.Stores[uuid] 901 assert.True(t, ok) 902 assert.Equal(t, metadata.WorkState_Working, info.WorkState) 903 _, ok = info.Labels["account"] 904 assert.False(t, ok) 905 labels, ok = info.Labels["role"] 906 assert.True(t, ok) 907 assert.Equal(t, labels.Labels, []string{"1", "2"}) 908 909 stateLabel = pb.CNStateLabel{ 910 UUID: uuid, 911 State: metadata.WorkState_Draining, 912 } 913 cmd = GetPatchCNStoreCmd(stateLabel) 914 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 915 assert.NoError(t, err) 916 s = tsm1.state.CNState 917 assert.Equal(t, 1, len(s.Stores)) 918 info, ok = s.Stores[uuid] 919 assert.True(t, ok) 920 assert.Equal(t, metadata.WorkState_Draining, info.WorkState) 921 _, ok = info.Labels["account"] 922 assert.False(t, ok) 923 labels, ok = info.Labels["role"] 924 assert.True(t, ok) 925 assert.Equal(t, labels.Labels, []string{"1", "2"}) 926 } 927 928 func TestHandleDeleteCNStore(t *testing.T) { 929 uuid := "uuid1" 930 tsm1 := NewStateMachine(0, 1).(*stateMachine) 931 932 cmd := GetTickCmd() 933 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 934 assert.NoError(t, err) 935 936 hb := pb.CNStoreHeartbeat{ 937 UUID: uuid, 938 } 939 data, err := hb.Marshal() 940 require.NoError(t, err) 941 cmd = GetCNStoreHeartbeatCmd(data) 942 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 943 assert.NoError(t, err) 944 s := tsm1.state.CNState 945 assert.Equal(t, 1, len(s.Stores)) 946 947 cnStore := pb.DeleteCNStore{ 948 StoreID: uuid, 949 } 950 cmd = GetDeleteCNStoreCmd(cnStore) 951 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 952 assert.NoError(t, err) 953 s = tsm1.state.CNState 954 assert.Equal(t, 0, len(s.Stores)) 955 } 956 957 func TestHandleProxyHeartbeat(t *testing.T) { 958 tsm1 := NewStateMachine(0, 1).(*stateMachine) 959 cmd := GetTickCmd() 960 _, err := tsm1.Update(sm.Entry{Cmd: cmd}) 961 assert.NoError(t, err) 962 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 963 assert.NoError(t, err) 964 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 965 assert.NoError(t, err) 966 967 hb := pb.ProxyHeartbeat{ 968 UUID: "uuid1", 969 } 970 data, err := hb.Marshal() 971 require.NoError(t, err) 972 cmd = GetProxyHeartbeatCmd(data) 973 _, err = tsm1.Update(sm.Entry{Cmd: cmd}) 974 assert.NoError(t, err) 975 s := tsm1.state.ProxyState 976 assert.Equal(t, 1, len(s.Stores)) 977 info, ok := s.Stores[hb.UUID] 978 assert.True(t, ok) 979 assert.Equal(t, uint64(3), info.Tick) 980 }