github.com/matrixorigin/matrixone@v0.7.0/pkg/logservice/service_commands.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 "context" 19 "fmt" 20 "reflect" 21 "time" 22 23 "go.uber.org/zap" 24 25 "github.com/matrixorigin/matrixone/pkg/hakeeper" 26 pb "github.com/matrixorigin/matrixone/pkg/pb/logservice" 27 "github.com/matrixorigin/matrixone/pkg/util/trace" 28 ) 29 30 func (s *Service) handleCommands(cmds []pb.ScheduleCommand) { 31 for _, cmd := range cmds { 32 s.runtime.Logger().Info(fmt.Sprintf("%s applying cmd: %s", s.ID(), cmd.LogString())) 33 if cmd.GetConfigChange() != nil { 34 s.runtime.Logger().Debug("applying schedule command:", zap.String("command", cmd.LogString())) 35 switch cmd.ConfigChange.ChangeType { 36 case pb.AddReplica: 37 s.handleAddReplica(cmd) 38 case pb.RemoveReplica: 39 s.handleRemoveReplica(cmd) 40 case pb.StartReplica: 41 s.handleStartReplica(cmd) 42 case pb.StopReplica: 43 s.handleStopReplica(cmd) 44 case pb.KillZombie: 45 s.handleKillZombie(cmd) 46 default: 47 panic("unknown config change cmd type") 48 } 49 } else if cmd.GetShutdownStore() != nil { 50 s.handleShutdownStore(cmd) 51 } else if cmd.GetCreateTaskService() != nil { 52 s.createTaskService(cmd.CreateTaskService) 53 } else { 54 panic("unknown schedule command type") 55 } 56 } 57 } 58 59 func (s *Service) handleAddReplica(cmd pb.ScheduleCommand) { 60 shardID := cmd.ConfigChange.Replica.ShardID 61 replicaID := cmd.ConfigChange.Replica.ReplicaID 62 epoch := cmd.ConfigChange.Replica.Epoch 63 target := cmd.ConfigChange.Replica.UUID 64 if err := s.store.addReplica(shardID, replicaID, target, epoch); err != nil { 65 s.runtime.Logger().Error("failed to add replica", zap.Error(err)) 66 } 67 } 68 69 func (s *Service) handleRemoveReplica(cmd pb.ScheduleCommand) { 70 shardID := cmd.ConfigChange.Replica.ShardID 71 replicaID := cmd.ConfigChange.Replica.ReplicaID 72 epoch := cmd.ConfigChange.Replica.Epoch 73 if err := s.store.removeReplica(shardID, replicaID, epoch); err != nil { 74 s.runtime.Logger().Error("failed to remove replica", zap.Error(err)) 75 } 76 } 77 78 func (s *Service) handleStartReplica(cmd pb.ScheduleCommand) { 79 shardID := cmd.ConfigChange.Replica.ShardID 80 replicaID := cmd.ConfigChange.Replica.ReplicaID 81 join := len(cmd.ConfigChange.InitialMembers) == 0 82 if shardID == hakeeper.DefaultHAKeeperShardID { 83 if err := s.store.startHAKeeperReplica(replicaID, 84 cmd.ConfigChange.InitialMembers, join); err != nil { 85 s.runtime.Logger().Error("failed to start HAKeeper replica", zap.Error(err)) 86 } 87 } else { 88 if err := s.store.startReplica(shardID, 89 replicaID, cmd.ConfigChange.InitialMembers, join); err != nil { 90 s.runtime.Logger().Error("failed to start log replica", zap.Error(err)) 91 } 92 } 93 } 94 95 func (s *Service) handleStopReplica(cmd pb.ScheduleCommand) { 96 shardID := cmd.ConfigChange.Replica.ShardID 97 replicaID := cmd.ConfigChange.Replica.ReplicaID 98 if err := s.store.stopReplica(shardID, replicaID); err != nil { 99 s.runtime.Logger().Error("failed to stop replica", zap.Error(err)) 100 } 101 } 102 103 func (s *Service) handleKillZombie(cmd pb.ScheduleCommand) { 104 shardID := cmd.ConfigChange.Replica.ShardID 105 replicaID := cmd.ConfigChange.Replica.ReplicaID 106 s.handleStopReplica(cmd) 107 s.store.removeMetadata(shardID, replicaID) 108 } 109 110 func (s *Service) handleShutdownStore(_ pb.ScheduleCommand) { 111 if err := s.Close(); err != nil { 112 s.runtime.Logger().Error("failed to shutdown replica", zap.Error(err)) 113 } 114 } 115 116 func (s *Service) heartbeatWorker(ctx context.Context) { 117 // TODO: check tick interval 118 if s.cfg.HeartbeatInterval.Duration == 0 { 119 panic("invalid heartbeat interval") 120 } 121 defer func() { 122 s.runtime.Logger().Info("heartbeat worker stopped") 123 }() 124 ticker := time.NewTicker(s.cfg.HeartbeatInterval.Duration) 125 defer ticker.Stop() 126 ctx, span := trace.Start(ctx, "heartbeatWorker") 127 defer span.End() 128 129 for { 130 select { 131 case <-ctx.Done(): 132 return 133 case <-ticker.C: 134 s.heartbeat(ctx) 135 // I'd call this an ugly hack to just workaround select's 136 // policy of randomly picking a ready channel from the case list. 137 select { 138 case <-ctx.Done(): 139 return 140 default: 141 } 142 } 143 } 144 } 145 146 func (s *Service) heartbeat(ctx context.Context) { 147 ctx2, cancel := context.WithTimeout(ctx, 3*time.Second) 148 defer cancel() 149 150 if s.haClient == nil { 151 if reflect.DeepEqual(s.cfg.HAKeeperClientConfig, HAKeeperClientConfig{}) { 152 panic("empty HAKeeper client config") 153 } 154 cc, err := NewLogHAKeeperClient(ctx2, s.cfg.GetHAKeeperClientConfig()) 155 if err != nil { 156 s.runtime.Logger().Error("failed to create HAKeeper client", zap.Error(err)) 157 return 158 } 159 s.haClient = cc 160 } 161 162 hb := s.store.getHeartbeatMessage() 163 hb.TaskServiceCreated = s.taskServiceCreated() 164 cb, err := s.haClient.SendLogHeartbeat(ctx2, hb) 165 if err != nil { 166 s.runtime.Logger().Error("failed to send log service heartbeat", zap.Error(err)) 167 return 168 } 169 s.handleCommands(cb.Commands) 170 }