github.com/matrixorigin/matrixone@v0.7.0/cmd/mo-service/main.go (about) 1 // Copyright 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 main 16 17 import ( 18 "context" 19 crand "crypto/rand" 20 "encoding/binary" 21 "errors" 22 "flag" 23 "fmt" 24 "log" 25 "math/rand" 26 "net/http" 27 "os" 28 "os/signal" 29 "strings" 30 "sync" 31 "syscall" 32 33 "github.com/google/uuid" 34 "github.com/matrixorigin/matrixone/pkg/cnservice" 35 "github.com/matrixorigin/matrixone/pkg/cnservice/cnclient" 36 "github.com/matrixorigin/matrixone/pkg/common/moerr" 37 "github.com/matrixorigin/matrixone/pkg/common/runtime" 38 "github.com/matrixorigin/matrixone/pkg/common/stopper" 39 "github.com/matrixorigin/matrixone/pkg/defines" 40 "github.com/matrixorigin/matrixone/pkg/dnservice" 41 "github.com/matrixorigin/matrixone/pkg/fileservice" 42 "github.com/matrixorigin/matrixone/pkg/logservice" 43 "github.com/matrixorigin/matrixone/pkg/logutil" 44 "github.com/matrixorigin/matrixone/pkg/pb/metadata" 45 "github.com/matrixorigin/matrixone/pkg/sql/compile" 46 "github.com/matrixorigin/matrixone/pkg/util" 47 "github.com/matrixorigin/matrixone/pkg/util/export" 48 "github.com/matrixorigin/matrixone/pkg/util/export/table" 49 "github.com/matrixorigin/matrixone/pkg/util/metric" 50 "github.com/matrixorigin/matrixone/pkg/util/trace/impl/motrace" 51 "go.uber.org/zap" 52 ) 53 54 var ( 55 configFile = flag.String("cfg", "", "toml configuration used to start mo-service") 56 launchFile = flag.String("launch", "", "toml configuration used to launch mo cluster") 57 version = flag.Bool("version", false, "print version information") 58 daemon = flag.Bool("daemon", false, "run mo-service in daemon mode") 59 ) 60 61 func main() { 62 flag.Parse() 63 maybePrintVersion() 64 maybeRunInDaemonMode() 65 66 if *cpuProfilePathFlag != "" { 67 stop := startCPUProfile() 68 defer stop() 69 } 70 if *allocsProfilePathFlag != "" { 71 defer writeAllocsProfile() 72 } 73 if *httpListenAddr != "" { 74 go func() { 75 http.ListenAndServe(*httpListenAddr, nil) 76 }() 77 } 78 79 var seed int64 80 if err := binary.Read(crand.Reader, binary.LittleEndian, &seed); err != nil { 81 panic(err) 82 } 83 rand.Seed(seed) 84 85 stopper := stopper.NewStopper("main", stopper.WithLogger(logutil.GetGlobalLogger())) 86 if *launchFile != "" { 87 if err := startCluster(stopper); err != nil { 88 panic(err) 89 } 90 } else if *configFile != "" { 91 cfg := &Config{} 92 if err := parseConfigFromFile(*configFile, cfg); err != nil { 93 panic(fmt.Sprintf("failed to parse config from %s, error: %s", *configFile, err.Error())) 94 } 95 if err := startService(cfg, stopper); err != nil { 96 panic(err) 97 } 98 } else { 99 panic(errors.New("no configuration specified")) 100 } 101 102 waitSignalToStop(stopper) 103 logutil.GetGlobalLogger().Info("Shutdown complete") 104 } 105 106 func waitSignalToStop(stopper *stopper.Stopper) { 107 sigchan := make(chan os.Signal, 1) 108 signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT) 109 sig := <-sigchan 110 logutil.GetGlobalLogger().Info("Starting shutdown...", zap.String("signal", sig.String())) 111 stopper.Stop() 112 if cnProxy != nil { 113 if err := cnProxy.Stop(); err != nil { 114 logutil.GetGlobalLogger().Error("shutdown cn proxy failed", zap.Error(err)) 115 } 116 } 117 } 118 119 func startService(cfg *Config, stopper *stopper.Stopper) error { 120 if err := cfg.validate(); err != nil { 121 return err 122 } 123 if err := cfg.resolveGossipSeedAddresses(); err != nil { 124 return err 125 } 126 setupProcessLevelRuntime(cfg, stopper) 127 128 fs, err := cfg.createFileService(defines.LocalFileServiceName) 129 if err != nil { 130 return err 131 } 132 133 st, err := cfg.getServiceType() 134 if err != nil { 135 return err 136 } 137 138 if err = initTraceMetric(context.Background(), st, cfg, stopper, fs); err != nil { 139 return err 140 } 141 142 switch st { 143 case metadata.ServiceType_CN: 144 return startCNService(cfg, stopper, fs) 145 case metadata.ServiceType_DN: 146 return startDNService(cfg, stopper, fs) 147 case metadata.ServiceType_LOG: 148 return startLogService(cfg, stopper, fs) 149 default: 150 panic("unknown service type") 151 } 152 } 153 154 func startCNService( 155 cfg *Config, 156 stopper *stopper.Stopper, 157 fileService fileservice.FileService, 158 ) error { 159 if err := waitClusterCondition(cfg.HAKeeperClient, waitAnyShardReady); err != nil { 160 return err 161 } 162 return stopper.RunNamedTask("cn-service", func(ctx context.Context) { 163 c := cfg.getCNServiceConfig() 164 s, err := cnservice.NewService( 165 &c, 166 ctx, 167 fileService, 168 cnservice.WithLogger(logutil.GetGlobalLogger().Named("cn-service").With(zap.String("uuid", cfg.CN.UUID))), 169 cnservice.WithMessageHandle(compile.CnServerMessageHandler), 170 ) 171 if err != nil { 172 panic(err) 173 } 174 if err := s.Start(); err != nil { 175 panic(err) 176 } 177 // TODO: global client need to refactor 178 err = cnclient.NewCNClient(&cnclient.ClientConfig{RPC: cfg.getCNServiceConfig().RPC}) 179 if err != nil { 180 panic(err) 181 } 182 183 <-ctx.Done() 184 if err := s.Close(); err != nil { 185 panic(err) 186 } 187 if err := cnclient.CloseCNClient(); err != nil { 188 panic(err) 189 } 190 }) 191 } 192 193 func startDNService( 194 cfg *Config, 195 stopper *stopper.Stopper, 196 fileService fileservice.FileService, 197 ) error { 198 if err := waitClusterCondition(cfg.HAKeeperClient, waitHAKeeperRunning); err != nil { 199 return err 200 } 201 r, err := getRuntime(metadata.ServiceType_DN, cfg, stopper) 202 if err != nil { 203 return err 204 } 205 return stopper.RunNamedTask("dn-service", func(ctx context.Context) { 206 c := cfg.getDNServiceConfig() 207 s, err := dnservice.NewService( 208 &c, 209 r, 210 fileService) 211 if err != nil { 212 panic(err) 213 } 214 if err := s.Start(); err != nil { 215 panic(err) 216 } 217 218 <-ctx.Done() 219 if err := s.Close(); err != nil { 220 panic(err) 221 } 222 }) 223 } 224 225 func startLogService( 226 cfg *Config, 227 stopper *stopper.Stopper, 228 fileService fileservice.FileService, 229 ) error { 230 lscfg := cfg.getLogServiceConfig() 231 s, err := logservice.NewService(lscfg, fileService, 232 logservice.WithRuntime(runtime.ProcessLevelRuntime())) 233 if err != nil { 234 panic(err) 235 } 236 if err := s.Start(); err != nil { 237 panic(err) 238 } 239 return stopper.RunNamedTask("log-service", func(ctx context.Context) { 240 if cfg.LogService.BootstrapConfig.BootstrapCluster { 241 logutil.Infof("bootstrapping hakeeper...") 242 if err := s.BootstrapHAKeeper(ctx, cfg.LogService); err != nil { 243 panic(err) 244 } 245 } 246 247 <-ctx.Done() 248 if err := s.Close(); err != nil { 249 panic(err) 250 } 251 }) 252 } 253 254 func initTraceMetric(ctx context.Context, st metadata.ServiceType, cfg *Config, stopper *stopper.Stopper, fs fileservice.FileService) error { 255 var writerFactory table.WriterFactory 256 var err error 257 var UUID string 258 var initWG sync.WaitGroup 259 SV := cfg.getObservabilityConfig() 260 261 nodeRole := st.String() 262 if *launchFile != "" { 263 nodeRole = "ALL" 264 } 265 switch st { 266 case metadata.ServiceType_CN: 267 // validate node_uuid 268 var uuidErr error 269 var nodeUUID uuid.UUID 270 if nodeUUID, uuidErr = uuid.Parse(cfg.CN.UUID); uuidErr != nil { 271 nodeUUID = uuid.New() 272 } 273 if err := util.SetUUIDNodeID(ctx, nodeUUID[:]); err != nil { 274 return moerr.ConvertPanicError(ctx, err) 275 } 276 UUID = nodeUUID.String() 277 case metadata.ServiceType_DN: 278 UUID = cfg.DN.UUID 279 case metadata.ServiceType_LOG: 280 UUID = cfg.LogService.UUID 281 } 282 UUID = strings.ReplaceAll(UUID, " ", "_") // remove space in UUID for filename 283 284 if !SV.DisableTrace || !SV.DisableMetric { 285 writerFactory = export.GetWriterFactory(fs, UUID, nodeRole, SV.LogsExtension) 286 _ = table.SetPathBuilder(ctx, SV.PathBuilder) 287 } 288 if !SV.DisableTrace { 289 initWG.Add(1) 290 collector := export.NewMOCollector(ctx) 291 stopper.RunNamedTask("trace", func(ctx context.Context) { 292 if err = motrace.InitWithConfig(ctx, 293 &SV, 294 motrace.WithNode(UUID, nodeRole), 295 motrace.WithBatchProcessor(collector), 296 motrace.WithFSWriterFactory(writerFactory), 297 motrace.WithSQLExecutor(nil), 298 ); err != nil { 299 panic(err) 300 } 301 initWG.Done() 302 <-ctx.Done() 303 // flush trace/log/error framework 304 if err = motrace.Shutdown(ctx); err != nil { 305 logutil.Warn("Shutdown trace", logutil.ErrorField(err), logutil.NoReportFiled()) 306 } 307 }) 308 initWG.Wait() 309 } 310 if !SV.DisableMetric { 311 stopper.RunNamedTask("metric", func(ctx context.Context) { 312 metric.InitMetric(ctx, nil, &SV, UUID, nodeRole, metric.WithWriterFactory(writerFactory)) 313 <-ctx.Done() 314 metric.StopMetricSync() 315 }) 316 } 317 if err = export.InitMerge(ctx, &SV); err != nil { 318 return err 319 } 320 return nil 321 } 322 323 func maybeRunInDaemonMode() { 324 if _, isChild := os.LookupEnv("daemon"); *daemon && !isChild { 325 childENV := []string{"daemon=true"} 326 pwd, err := os.Getwd() 327 if err != nil { 328 panic(err) 329 } 330 cpid, err := syscall.ForkExec(os.Args[0], os.Args, &syscall.ProcAttr{ 331 Dir: pwd, 332 Env: append(os.Environ(), childENV...), 333 Sys: &syscall.SysProcAttr{ 334 Setsid: true, 335 }, 336 Files: []uintptr{0, 1, 2}, // print message to the same pty 337 }) 338 if err != nil { 339 panic(err) 340 } 341 log.Printf("mo-service is running in daemon mode, child process is %d", cpid) 342 os.Exit(0) 343 } 344 }