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  }