github.com/matrixorigin/matrixone@v1.2.0/cmd/mo-service/dynamic.go (about) 1 // Copyright 2021 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 main 16 17 import ( 18 "context" 19 "fmt" 20 "net/http" 21 "os" 22 "path/filepath" 23 "syscall" 24 "time" 25 26 "github.com/fagongzi/goetty/v2" 27 "github.com/fagongzi/util/format" 28 "github.com/matrixorigin/matrixone/pkg/common/chaos" 29 "github.com/matrixorigin/matrixone/pkg/common/stopper" 30 "github.com/matrixorigin/matrixone/pkg/logutil" 31 ) 32 33 var ( 34 baseUUID = 0 35 basePort = 18000 36 baseFrontendPort = 16001 37 baseUnixSocket = 0 38 ) 39 40 var ( 41 dynamicCNServicePIDs []int 42 dynamicCNServiceCommands [][]string 43 ) 44 45 func startDynamicCluster( 46 ctx context.Context, 47 cfg *LaunchConfig, 48 stopper *stopper.Stopper, 49 shutdownC chan struct{}, 50 ) error { 51 if err := startLogServiceCluster(ctx, cfg.LogServiceConfigFiles, stopper, shutdownC); err != nil { 52 return err 53 } 54 if err := startTNServiceCluster(ctx, cfg.TNServiceConfigsFiles, stopper, shutdownC); err != nil { 55 return err 56 } 57 if err := startDynamicCNServices("./mo-data", cfg.Dynamic); err != nil { 58 return err 59 } 60 if *withProxy { 61 if err := startProxyServiceCluster(ctx, cfg.ProxyServiceConfigsFiles, stopper, shutdownC); err != nil { 62 return err 63 } 64 } 65 66 // TODO: make configurable for 6001 67 cnProxy = goetty.NewProxy("0.0.0.0:6001", logutil.GetGlobalLogger().Named("mysql-proxy")) 68 for i := 0; i < cfg.Dynamic.ServiceCount; i++ { 69 port := baseFrontendPort + i 70 cnProxy.AddUpStream(fmt.Sprintf("127.0.0.1:%d", port), time.Second*10) 71 } 72 if err := cnProxy.Start(); err != nil { 73 return err 74 } 75 // } 76 return startDynamicCtlHTTPServer(cfg.Dynamic.CtlAddress) 77 } 78 79 func startDynamicCNServices( 80 baseDir string, 81 cfg Dynamic) error { 82 if err := genDynamicCNConfigs(baseDir, cfg); err != nil { 83 return err 84 } 85 86 dynamicCNServiceCommands = make([][]string, cfg.ServiceCount) 87 dynamicCNServicePIDs = make([]int, cfg.ServiceCount) 88 for i := 0; i < cfg.ServiceCount; i++ { 89 dynamicCNServiceCommands[i] = []string{ 90 os.Args[0], 91 "-cfg", "./mo-data/cn-" + fmt.Sprintf("%d", i) + ".toml", 92 "-max-processor", fmt.Sprintf("%d", cfg.CpuCount), 93 "-debug-http", fmt.Sprintf("127.0.0.1:606%d", i), 94 } 95 if err := startDynamicCNByIndex(i); err != nil { 96 return err 97 } 98 } 99 if !cfg.Chaos.Enable { 100 return nil 101 } 102 cfg.Chaos.Restart.KillFunc = stopDynamicCNByIndex 103 cfg.Chaos.Restart.RestartFunc = startDynamicCNByIndex 104 chaosTester := chaos.NewChaosTester(cfg.Chaos) 105 return chaosTester.Start() 106 } 107 108 func genDynamicCNConfigs( 109 baseDir string, 110 cfg Dynamic) error { 111 baseCNConfig, err := os.ReadFile(cfg.CNTemplate) 112 if err != nil { 113 return err 114 } 115 116 temps := make([]string, 0, cfg.ServiceCount) 117 for i := 0; i < cfg.ServiceCount; i++ { 118 uuid := baseUUID + i 119 port := basePort + i*100 120 frontendPort := baseFrontendPort + i 121 unixSocketPort := baseUnixSocket + i 122 123 cfgFile := fmt.Sprintf( 124 string(baseCNConfig), 125 uuid, 126 port, 127 i, 128 i, 129 frontendPort, 130 unixSocketPort) 131 f, err := os.CreateTemp( 132 baseDir, 133 "*.tmp") 134 if err != nil { 135 return err 136 } 137 if _, err := f.WriteString(cfgFile); err != nil { 138 return err 139 } 140 if err := f.Sync(); err != nil { 141 return err 142 } 143 if err := f.Close(); err != nil { 144 return err 145 } 146 temps = append(temps, f.Name()) 147 } 148 149 d, err := os.Open(baseDir) 150 if err != nil { 151 return err 152 } 153 defer func() { 154 if err := d.Close(); err != nil { 155 panic(err) 156 } 157 }() 158 for i := 0; i < cfg.ServiceCount; i++ { 159 if err := os.Rename( 160 filepath.Join(temps[i]), 161 filepath.Join(baseDir, fmt.Sprintf("cn-%d.toml", i))); err != nil { 162 return err 163 } 164 } 165 if err := d.Sync(); err != nil { 166 return err 167 } 168 return nil 169 } 170 171 func startDynamicCtlHTTPServer(addr string) error { 172 if addr == "" { 173 return nil 174 } 175 176 http.HandleFunc("/dynamic/cn", 177 func(resp http.ResponseWriter, req *http.Request) { 178 cn := req.URL.Query().Get("cn") 179 action := req.URL.Query().Get("action") 180 if cn == "" || action == "" { 181 resp.WriteHeader(http.StatusBadRequest) 182 resp.Write([]byte("invalid request")) 183 return 184 } 185 186 index := format.MustParseStringInt(cn) 187 if index < 0 || index >= len(dynamicCNServiceCommands) { 188 resp.WriteHeader(http.StatusBadRequest) 189 resp.Write([]byte("invalid request")) 190 return 191 } 192 193 switch action { 194 case "start": 195 if dynamicCNServicePIDs[index] != 0 { 196 resp.WriteHeader(http.StatusBadRequest) 197 resp.Write([]byte("already started")) 198 return 199 } 200 if err := startDynamicCNByIndex(index); err != nil { 201 resp.Write([]byte(err.Error())) 202 } else { 203 resp.Write([]byte("OK")) 204 } 205 case "stop": 206 if dynamicCNServicePIDs[index] == 0 { 207 resp.WriteHeader(http.StatusBadRequest) 208 resp.Write([]byte("already stopped")) 209 return 210 } 211 212 if err := syscall.Kill(dynamicCNServicePIDs[index], syscall.SIGKILL); err != nil { 213 resp.Write([]byte(err.Error())) 214 } else { 215 resp.Write([]byte("OK")) 216 dynamicCNServicePIDs[index] = 0 217 } 218 default: 219 resp.WriteHeader(http.StatusBadRequest) 220 resp.Write([]byte("invalid request")) 221 return 222 } 223 }) 224 go func() { 225 http.ListenAndServe(*httpListenAddr, nil) 226 }() 227 return nil 228 } 229 230 func stopDynamicCNByIndex(index int) error { 231 return syscall.Kill(dynamicCNServicePIDs[index], syscall.SIGKILL) 232 } 233 234 func startDynamicCNByIndex(index int) error { 235 pwd, err := os.Getwd() 236 if err != nil { 237 return err 238 } 239 pid, err := syscall.ForkExec( 240 dynamicCNServiceCommands[index][0], 241 dynamicCNServiceCommands[index], 242 &syscall.ProcAttr{ 243 Dir: pwd, 244 Env: os.Environ(), 245 Sys: &syscall.SysProcAttr{ 246 Setsid: true, 247 }, 248 Files: []uintptr{0, 1, 2}, // print message to the same pty 249 }) 250 if err != nil { 251 return err 252 } 253 dynamicCNServicePIDs[index] = pid 254 return nil 255 } 256 257 func stopAllDynamicCNServices() { 258 for _, pid := range dynamicCNServicePIDs { 259 syscall.Kill(pid, syscall.SIGKILL) 260 } 261 }