github.com/matrixorigin/matrixone@v0.7.0/cmd/mo-service/config.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  	"fmt"
    20  	"hash/fnv"
    21  	"math"
    22  	"net"
    23  	"os"
    24  	"path/filepath"
    25  	"strings"
    26  	"time"
    27  
    28  	"github.com/BurntSushi/toml"
    29  	"github.com/matrixorigin/matrixone/pkg/cnservice"
    30  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    31  	"github.com/matrixorigin/matrixone/pkg/config"
    32  	"github.com/matrixorigin/matrixone/pkg/defines"
    33  	"github.com/matrixorigin/matrixone/pkg/dnservice"
    34  	"github.com/matrixorigin/matrixone/pkg/fileservice"
    35  	"github.com/matrixorigin/matrixone/pkg/logservice"
    36  	"github.com/matrixorigin/matrixone/pkg/logutil"
    37  	"github.com/matrixorigin/matrixone/pkg/pb/metadata"
    38  	tomlutil "github.com/matrixorigin/matrixone/pkg/util/toml"
    39  )
    40  
    41  var (
    42  	defaultMaxClockOffset = time.Millisecond * 500
    43  	defaultMemoryLimit    = 1 << 40
    44  
    45  	supportServiceTypes = map[string]metadata.ServiceType{
    46  		metadata.ServiceType_CN.String():  metadata.ServiceType_CN,
    47  		metadata.ServiceType_DN.String():  metadata.ServiceType_DN,
    48  		metadata.ServiceType_LOG.String(): metadata.ServiceType_LOG,
    49  	}
    50  )
    51  
    52  // LaunchConfig Start a MO cluster with launch
    53  type LaunchConfig struct {
    54  	// LogServiceConfigFiles log service config files
    55  	LogServiceConfigFiles []string `toml:"logservices"`
    56  	// DNServiceConfigsFiles log service config files
    57  	DNServiceConfigsFiles []string `toml:"dnservices"`
    58  	// CNServiceConfigsFiles log service config files
    59  	CNServiceConfigsFiles []string `toml:"cnservices"`
    60  }
    61  
    62  // Config mo-service configuration
    63  type Config struct {
    64  	// DataDir data dir
    65  	DataDir string `toml:"data-dir"`
    66  	// Log log config
    67  	Log logutil.LogConfig `toml:"log"`
    68  	// ServiceType service type, select the corresponding configuration to start the
    69  	// service according to the service type. [CN|DN|LOG]
    70  	ServiceType string `toml:"service-type"`
    71  	// FileServices the config for file services
    72  	FileServices []fileservice.Config `toml:"fileservice"`
    73  	// HAKeeperClient hakeeper client config
    74  	HAKeeperClient logservice.HAKeeperClientConfig `toml:"hakeeper-client"`
    75  	// DN dn service config
    76  	DN dnservice.Config `toml:"dn"`
    77  	// LogService is the config for log service
    78  	LogService logservice.Config `toml:"logservice"`
    79  	// CN cn service config
    80  	CN cnservice.Config `toml:"cn"`
    81  	// Observability parameters for the metric/trace
    82  	Observability config.ObservabilityParameters `toml:"observability"`
    83  
    84  	// Clock txn clock type. [LOCAL|HLC]. Default is LOCAL.
    85  	Clock struct {
    86  		// Backend clock backend implementation. [LOCAL|HLC], default LOCAL.
    87  		Backend string `toml:"source"`
    88  		// MaxClockOffset max clock offset between two nodes. Default is 500ms.
    89  		// Only valid when enable-check-clock-offset is true
    90  		MaxClockOffset tomlutil.Duration `toml:"max-clock-offset"`
    91  		// EnableCheckMaxClockOffset enable local clock offset checker
    92  		EnableCheckMaxClockOffset bool `toml:"enable-check-clock-offset"`
    93  	}
    94  
    95  	// Limit limit configuration
    96  	Limit struct {
    97  		// Memory memory usage limit, see mpool for details
    98  		Memory tomlutil.ByteSize `toml:"memory"`
    99  	}
   100  }
   101  
   102  func parseConfigFromFile(file string, cfg any) error {
   103  	if file == "" {
   104  		return moerr.NewInternalError(context.Background(), "toml config file not set")
   105  	}
   106  	data, err := os.ReadFile(file)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	return parseFromString(string(data), cfg)
   111  }
   112  
   113  func parseFromString(data string, cfg any) error {
   114  	if _, err := toml.Decode(data, cfg); err != nil {
   115  		return err
   116  	}
   117  	return nil
   118  }
   119  
   120  func (c *Config) validate() error {
   121  	if c.DataDir == "" {
   122  		c.DataDir = "./mo-data"
   123  	}
   124  	if _, err := c.getServiceType(); err != nil {
   125  		return err
   126  	}
   127  	if c.Clock.MaxClockOffset.Duration == 0 {
   128  		c.Clock.MaxClockOffset.Duration = defaultMaxClockOffset
   129  	}
   130  	if c.Clock.Backend == "" {
   131  		c.Clock.Backend = localClockBackend
   132  	}
   133  	if _, ok := supportTxnClockBackends[strings.ToUpper(c.Clock.Backend)]; !ok {
   134  		return moerr.NewInternalError(context.Background(), "%s clock backend not support", c.Clock.Backend)
   135  	}
   136  	if !c.Clock.EnableCheckMaxClockOffset {
   137  		c.Clock.MaxClockOffset.Duration = 0
   138  	}
   139  	for idx := range c.FileServices {
   140  		switch c.FileServices[idx].Name {
   141  		case defines.LocalFileServiceName, defines.ETLFileServiceName:
   142  			if c.FileServices[idx].DataDir == "" {
   143  				c.FileServices[idx].DataDir = filepath.Join(c.DataDir, strings.ToLower(c.FileServices[idx].Name))
   144  			}
   145  		}
   146  	}
   147  	if c.Limit.Memory == 0 {
   148  		c.Limit.Memory = tomlutil.ByteSize(defaultMemoryLimit)
   149  	}
   150  	return nil
   151  }
   152  
   153  func (c *Config) createFileService(defaultName string) (*fileservice.FileServices, error) {
   154  	// create all services
   155  	services := make([]fileservice.FileService, 0, len(c.FileServices))
   156  	for _, config := range c.FileServices {
   157  
   158  		// for old config compatibility
   159  		if strings.EqualFold(config.Name, "s3") {
   160  			config.Name = defines.SharedFileServiceName
   161  		}
   162  
   163  		service, err := fileservice.NewFileService(config)
   164  		if err != nil {
   165  			return nil, err
   166  		}
   167  		services = append(services, service)
   168  	}
   169  
   170  	// create FileServices
   171  	fs, err := fileservice.NewFileServices(
   172  		defaultName,
   173  		services...,
   174  	)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	// validate default name
   180  	_, err = fileservice.Get[fileservice.FileService](fs, defaultName)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	// ensure local exists
   186  	_, err = fileservice.Get[fileservice.FileService](fs, defines.LocalFileServiceName)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	// ensure shared exists
   192  	_, err = fileservice.Get[fileservice.FileService](fs, defines.SharedFileServiceName)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	// ensure etl exists, for trace & metric
   198  	if !c.Observability.DisableMetric || !c.Observability.DisableTrace {
   199  		_, err = fileservice.Get[fileservice.FileService](fs, defines.ETLFileServiceName)
   200  		if err != nil {
   201  			return nil, moerr.ConvertPanicError(context.Background(), err)
   202  		}
   203  	}
   204  
   205  	return fs, nil
   206  }
   207  
   208  func (c *Config) getLogServiceConfig() logservice.Config {
   209  	cfg := c.LogService
   210  	logutil.Infof("hakeeper client cfg: %v", c.HAKeeperClient)
   211  	cfg.HAKeeperClientConfig = c.HAKeeperClient
   212  	cfg.DataDir = filepath.Join(c.DataDir, "logservice-data", cfg.UUID)
   213  	// Should sync directory structure with dragonboat.
   214  	hostname, err := os.Hostname()
   215  	if err != nil {
   216  		panic(fmt.Sprintf("cannot get hostname: %s", err))
   217  	}
   218  	cfg.SnapshotExportDir = filepath.Join(cfg.DataDir, hostname,
   219  		fmt.Sprintf("%020d", cfg.DeploymentID), "exported-snapshot")
   220  	return cfg
   221  }
   222  
   223  func (c *Config) getDNServiceConfig() dnservice.Config {
   224  	cfg := c.DN
   225  	cfg.HAKeeper.ClientConfig = c.HAKeeperClient
   226  	cfg.DataDir = filepath.Join(c.DataDir, "dn-data", cfg.UUID)
   227  	return cfg
   228  }
   229  
   230  func (c *Config) getCNServiceConfig() cnservice.Config {
   231  	cfg := c.CN
   232  	cfg.HAKeeper.ClientConfig = c.HAKeeperClient
   233  	cfg.Frontend.SetLogAndVersion(&c.Log, Version)
   234  	cfg.Frontend.StorePath = filepath.Join(c.DataDir, "cn-data", cfg.UUID)
   235  	return cfg
   236  }
   237  
   238  func (c *Config) getObservabilityConfig() config.ObservabilityParameters {
   239  	cfg := c.Observability
   240  	cfg.SetDefaultValues(Version)
   241  	return cfg
   242  }
   243  
   244  // memberlist requires all gossip seed addresses to be provided as IP:PORT
   245  func (c *Config) resolveGossipSeedAddresses() error {
   246  	result := make([]string, 0)
   247  	for _, addr := range c.LogService.GossipSeedAddresses {
   248  		host, port, err := net.SplitHostPort(addr)
   249  		if err != nil {
   250  			return err
   251  		}
   252  		ips, err := net.LookupIP(host)
   253  		if err != nil {
   254  			// the configured member may be failed currently, keep the host name anyway since
   255  			// memberlist would try to resolve it again
   256  			result = append(result, addr)
   257  			continue
   258  		}
   259  		// only keep IPv4 addresses
   260  		filtered := make([]string, 0)
   261  		for _, ip := range ips {
   262  			if ip.To4() != nil {
   263  				filtered = append(filtered, ip.String())
   264  			}
   265  		}
   266  		if len(filtered) != 1 {
   267  			return moerr.NewBadConfig(context.Background(), "GossipSeedAddress %s", addr)
   268  		}
   269  		result = append(result, net.JoinHostPort(filtered[0], port))
   270  	}
   271  	c.LogService.GossipSeedAddresses = result
   272  	return nil
   273  }
   274  
   275  func (c *Config) hashNodeID() uint16 {
   276  	st, err := c.getServiceType()
   277  	if err != nil {
   278  		panic(err)
   279  	}
   280  
   281  	uuid := ""
   282  	switch st {
   283  	case metadata.ServiceType_CN:
   284  		uuid = c.CN.UUID
   285  	case metadata.ServiceType_DN:
   286  		uuid = c.DN.UUID
   287  	case metadata.ServiceType_LOG:
   288  		uuid = c.LogService.UUID
   289  	}
   290  	if uuid == "" {
   291  		return 0
   292  	}
   293  
   294  	h := fnv.New32()
   295  	if _, err := h.Write([]byte(uuid)); err != nil {
   296  		panic(err)
   297  	}
   298  	v := h.Sum32()
   299  	return uint16(v % math.MaxUint16)
   300  }
   301  
   302  func (c *Config) getServiceType() (metadata.ServiceType, error) {
   303  	if v, ok := supportServiceTypes[strings.ToUpper(c.ServiceType)]; ok {
   304  		return v, nil
   305  	}
   306  	return metadata.ServiceType(0), moerr.NewInternalError(context.Background(), "service type %s not support", c.ServiceType)
   307  }
   308  
   309  func (c *Config) mustGetServiceType() metadata.ServiceType {
   310  	v, err := c.getServiceType()
   311  	if err != nil {
   312  		panic(err)
   313  	}
   314  	return v
   315  }
   316  
   317  func (c *Config) mustGetServiceUUID() string {
   318  	switch c.mustGetServiceType() {
   319  	case metadata.ServiceType_CN:
   320  		return c.CN.UUID
   321  	case metadata.ServiceType_DN:
   322  		return c.DN.UUID
   323  	case metadata.ServiceType_LOG:
   324  		return c.LogService.UUID
   325  	}
   326  	panic("impossible")
   327  }