github.com/matrixorigin/matrixone@v0.7.0/pkg/logservice/service_commands_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 logservice 16 17 import ( 18 "testing" 19 "time" 20 21 "github.com/google/uuid" 22 "github.com/lni/dragonboat/v4" 23 "github.com/lni/goutils/leaktest" 24 "github.com/lni/vfs" 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 28 "github.com/matrixorigin/matrixone/pkg/common/morpc" 29 "github.com/matrixorigin/matrixone/pkg/common/runtime" 30 pb "github.com/matrixorigin/matrixone/pkg/pb/logservice" 31 "github.com/matrixorigin/matrixone/pkg/testutil" 32 ) 33 34 func TestBackgroundTickAndHeartbeat(t *testing.T) { 35 defer leaktest.AfterTest(t)() 36 cfg := Config{ 37 UUID: uuid.New().String(), 38 FS: vfs.NewStrictMem(), 39 DeploymentID: 1, 40 RTTMillisecond: 5, 41 DataDir: "data-1", 42 ServiceAddress: "127.0.0.1:9002", 43 RaftAddress: "127.0.0.1:9000", 44 GossipAddress: "127.0.0.1:9001", 45 // below is an unreachable address intentionally set 46 GossipSeedAddresses: []string{"127.0.0.1:9010"}, 47 } 48 cfg.HeartbeatInterval.Duration = 5 * time.Millisecond 49 cfg.HAKeeperTickInterval.Duration = 5 * time.Millisecond 50 cfg.HAKeeperClientConfig.ServiceAddresses = []string{"127.0.0.1:9002"} 51 cfg.Fill() 52 service, err := NewService(cfg, 53 testutil.NewFS(), 54 WithBackendFilter(func(msg morpc.Message, backendAddr string) bool { 55 return true 56 }), 57 ) 58 require.NoError(t, err) 59 defer func() { 60 assert.NoError(t, service.Close()) 61 }() 62 peers := make(map[uint64]dragonboat.Target) 63 peers[1] = service.ID() 64 require.NoError(t, service.store.startHAKeeperReplica(1, peers, false)) 65 66 for i := 0; i < 500; i++ { 67 done := true 68 state, err := service.store.getCheckerState() 69 require.NoError(t, err) 70 if state.Tick < 10 { 71 done = false 72 } 73 si, ok := state.LogState.Stores[service.ID()] 74 if !ok { 75 done = false 76 } else { 77 if si.Tick < 10 { 78 done = false 79 } 80 } 81 if done { 82 return 83 } else { 84 time.Sleep(5 * time.Millisecond) 85 } 86 } 87 t.Fatalf("failed to tick/heartbeat") 88 } 89 90 func TestHandleKillZombie(t *testing.T) { 91 fn := func(t *testing.T, s *Service) { 92 has, err := hasMetadataRec(s.store.cfg.DataDir, logMetadataFilename, 1, 1, s.store.cfg.FS) 93 require.NoError(t, err) 94 assert.True(t, has) 95 96 cmd := pb.ScheduleCommand{ 97 ConfigChange: &pb.ConfigChange{ 98 ChangeType: pb.KillZombie, 99 Replica: pb.Replica{ 100 ShardID: 1, 101 ReplicaID: 1, 102 }, 103 }, 104 } 105 mustHaveReplica(t, s.store, 1, 1) 106 s.handleCommands([]pb.ScheduleCommand{cmd}) 107 assert.False(t, hasReplica(s.store, 1, 1)) 108 109 has, err = hasMetadataRec(s.store.cfg.DataDir, logMetadataFilename, 1, 1, s.store.cfg.FS) 110 require.NoError(t, err) 111 assert.False(t, has) 112 } 113 runServiceTest(t, false, true, fn) 114 } 115 116 func TestHandleStartReplica(t *testing.T) { 117 fn := func(t *testing.T, s *Service) { 118 cmd := pb.ScheduleCommand{ 119 ConfigChange: &pb.ConfigChange{ 120 ChangeType: pb.StartReplica, 121 Replica: pb.Replica{ 122 ShardID: 1, 123 ReplicaID: 1, 124 }, 125 InitialMembers: map[uint64]string{1: s.ID()}, 126 }, 127 } 128 s.handleCommands([]pb.ScheduleCommand{cmd}) 129 mustHaveReplica(t, s.store, 1, 1) 130 131 has, err := hasMetadataRec(s.store.cfg.DataDir, logMetadataFilename, 1, 1, s.store.cfg.FS) 132 require.NoError(t, err) 133 assert.True(t, has) 134 } 135 runServiceTest(t, false, false, fn) 136 } 137 138 func TestHandleStopReplica(t *testing.T) { 139 fn := func(t *testing.T, s *Service) { 140 cmd := pb.ScheduleCommand{ 141 ConfigChange: &pb.ConfigChange{ 142 ChangeType: pb.StartReplica, 143 Replica: pb.Replica{ 144 ShardID: 1, 145 ReplicaID: 1, 146 }, 147 InitialMembers: map[uint64]string{1: s.ID()}, 148 }, 149 } 150 s.handleCommands([]pb.ScheduleCommand{cmd}) 151 mustHaveReplica(t, s.store, 1, 1) 152 153 cmd = pb.ScheduleCommand{ 154 ConfigChange: &pb.ConfigChange{ 155 ChangeType: pb.StopReplica, 156 Replica: pb.Replica{ 157 ShardID: 1, 158 ReplicaID: 1, 159 }, 160 }, 161 } 162 s.handleCommands([]pb.ScheduleCommand{cmd}) 163 assert.False(t, hasReplica(s.store, 1, 1)) 164 165 has, err := hasMetadataRec(s.store.cfg.DataDir, logMetadataFilename, 1, 1, s.store.cfg.FS) 166 require.NoError(t, err) 167 assert.True(t, has) 168 } 169 runServiceTest(t, false, false, fn) 170 } 171 172 func TestHandleAddReplica(t *testing.T) { 173 store1, store2, err := getTestStores() 174 require.NoError(t, err) 175 defer func() { 176 require.NoError(t, store1.close()) 177 require.NoError(t, store2.close()) 178 }() 179 180 service1 := Service{ 181 store: store1, 182 runtime: runtime.DefaultRuntime(), 183 } 184 cmd := pb.ScheduleCommand{ 185 ConfigChange: &pb.ConfigChange{ 186 ChangeType: pb.AddReplica, 187 Replica: pb.Replica{ 188 UUID: uuid.New().String(), 189 ShardID: 1, 190 ReplicaID: 3, 191 Epoch: 2, 192 }, 193 }, 194 } 195 service1.handleCommands([]pb.ScheduleCommand{cmd}) 196 count, ok := checkReplicaCount(store1, 1) 197 require.True(t, ok) 198 assert.Equal(t, 3, count) 199 } 200 201 func TestHandleRemoveReplica(t *testing.T) { 202 store1, store2, err := getTestStores() 203 require.NoError(t, err) 204 defer func() { 205 require.NoError(t, store1.close()) 206 require.NoError(t, store2.close()) 207 }() 208 209 service1 := Service{ 210 store: store1, 211 runtime: runtime.DefaultRuntime(), 212 } 213 cmd := pb.ScheduleCommand{ 214 ConfigChange: &pb.ConfigChange{ 215 ChangeType: pb.RemoveReplica, 216 Replica: pb.Replica{ 217 ShardID: 1, 218 ReplicaID: 2, 219 Epoch: 2, 220 }, 221 }, 222 } 223 service1.handleCommands([]pb.ScheduleCommand{cmd}) 224 count, ok := checkReplicaCount(store1, 1) 225 require.True(t, ok) 226 assert.Equal(t, 1, count) 227 } 228 229 func checkReplicaCount(s *store, shardID uint64) (int, bool) { 230 hb := s.getHeartbeatMessage() 231 for _, info := range hb.Replicas { 232 if info.ShardID == shardID { 233 return len(info.Replicas), true 234 } 235 } 236 return 0, false 237 }