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  }