github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/pkg/config/config.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package config
    15  
    16  import (
    17  	"encoding/json"
    18  	"fmt"
    19  	"net"
    20  	"strings"
    21  	"sync/atomic"
    22  	"time"
    23  
    24  	"github.com/pingcap/errors"
    25  	"github.com/pingcap/log"
    26  	"github.com/pingcap/ticdc/pkg/config/outdated"
    27  	cerror "github.com/pingcap/ticdc/pkg/errors"
    28  	"github.com/pingcap/ticdc/pkg/security"
    29  	"go.uber.org/zap"
    30  )
    31  
    32  const (
    33  	// NewReplicaImpl is true if we using new processor
    34  	// new owner should be also switched on after it implemented
    35  	NewReplicaImpl = false
    36  	// DefaultSortDir is the default value of sort-dir, it will be s sub directory of data-dir.
    37  	DefaultSortDir = "/tmp/sorter"
    38  )
    39  
    40  func init() {
    41  	StoreGlobalServerConfig(GetDefaultServerConfig())
    42  }
    43  
    44  func init() {
    45  	StoreGlobalServerConfig(GetDefaultServerConfig())
    46  }
    47  
    48  var defaultReplicaConfig = &ReplicaConfig{
    49  	CaseSensitive:    true,
    50  	EnableOldValue:   true,
    51  	CheckGCSafePoint: true,
    52  	Filter: &FilterConfig{
    53  		Rules: []string{"*.*"},
    54  	},
    55  	Mounter: &MounterConfig{
    56  		WorkerNum: 16,
    57  	},
    58  	Sink: &SinkConfig{
    59  		Protocol: "default",
    60  	},
    61  	Cyclic: &CyclicConfig{
    62  		Enable: false,
    63  	},
    64  	Scheduler: &SchedulerConfig{
    65  		Tp:          "table-number",
    66  		PollingTime: -1,
    67  	},
    68  }
    69  
    70  // ReplicaConfig represents some addition replication config for a changefeed
    71  type ReplicaConfig replicaConfig
    72  
    73  type replicaConfig struct {
    74  	CaseSensitive    bool             `toml:"case-sensitive" json:"case-sensitive"`
    75  	EnableOldValue   bool             `toml:"enable-old-value" json:"enable-old-value"`
    76  	ForceReplicate   bool             `toml:"force-replicate" json:"force-replicate"`
    77  	CheckGCSafePoint bool             `toml:"check-gc-safe-point" json:"check-gc-safe-point"`
    78  	Filter           *FilterConfig    `toml:"filter" json:"filter"`
    79  	Mounter          *MounterConfig   `toml:"mounter" json:"mounter"`
    80  	Sink             *SinkConfig      `toml:"sink" json:"sink"`
    81  	Cyclic           *CyclicConfig    `toml:"cyclic-replication" json:"cyclic-replication"`
    82  	Scheduler        *SchedulerConfig `toml:"scheduler" json:"scheduler"`
    83  }
    84  
    85  // Marshal returns the json marshal format of a ReplicationConfig
    86  func (c *ReplicaConfig) Marshal() (string, error) {
    87  	cfg, err := json.Marshal(c)
    88  	if err != nil {
    89  		return "", cerror.WrapError(cerror.ErrEncodeFailed, errors.Annotatef(err, "Unmarshal data: %v", c))
    90  	}
    91  	return string(cfg), nil
    92  }
    93  
    94  // Unmarshal unmarshals into *ReplicationConfig from json marshal byte slice
    95  func (c *ReplicaConfig) Unmarshal(data []byte) error {
    96  	return c.UnmarshalJSON(data)
    97  }
    98  
    99  // UnmarshalJSON unmarshals into *ReplicationConfig from json marshal byte slice
   100  func (c *ReplicaConfig) UnmarshalJSON(data []byte) error {
   101  	// The purpose of casting ReplicaConfig to replicaConfig is to avoid recursive calls UnmarshalJSON,
   102  	// resulting in stack overflow
   103  	r := (*replicaConfig)(c)
   104  	err := json.Unmarshal(data, &r)
   105  	if err != nil {
   106  		return cerror.WrapError(cerror.ErrDecodeFailed, err)
   107  	}
   108  	v1 := outdated.ReplicaConfigV1{}
   109  	err = v1.Unmarshal(data)
   110  	if err != nil {
   111  		return cerror.WrapError(cerror.ErrDecodeFailed, err)
   112  	}
   113  	r.fillFromV1(&v1)
   114  	return nil
   115  }
   116  
   117  // Clone clones a replication
   118  func (c *ReplicaConfig) Clone() *ReplicaConfig {
   119  	str, err := c.Marshal()
   120  	if err != nil {
   121  		log.Panic("failed to marshal replica config",
   122  			zap.Error(cerror.WrapError(cerror.ErrDecodeFailed, err)))
   123  	}
   124  	clone := new(ReplicaConfig)
   125  	err = clone.Unmarshal([]byte(str))
   126  	if err != nil {
   127  		log.Panic("failed to unmarshal replica config",
   128  			zap.Error(cerror.WrapError(cerror.ErrDecodeFailed, err)))
   129  	}
   130  	return clone
   131  }
   132  
   133  func (c *replicaConfig) fillFromV1(v1 *outdated.ReplicaConfigV1) {
   134  	if v1 == nil || v1.Sink == nil {
   135  		return
   136  	}
   137  	for _, dispatch := range v1.Sink.DispatchRules {
   138  		c.Sink.DispatchRules = append(c.Sink.DispatchRules, &DispatchRule{
   139  			Matcher:    []string{fmt.Sprintf("%s.%s", dispatch.Schema, dispatch.Name)},
   140  			Dispatcher: dispatch.Rule,
   141  		})
   142  	}
   143  }
   144  
   145  // GetDefaultReplicaConfig returns the default replica config
   146  func GetDefaultReplicaConfig() *ReplicaConfig {
   147  	return defaultReplicaConfig.Clone()
   148  }
   149  
   150  // SecurityConfig represents security config for server
   151  type SecurityConfig = security.Credential
   152  
   153  // LogFileConfig represents log file config for server
   154  type LogFileConfig struct {
   155  	MaxSize    int `toml:"max-size" json:"max-size"`
   156  	MaxDays    int `toml:"max-days" json:"max-days"`
   157  	MaxBackups int `toml:"max-backups" json:"max-backups"`
   158  }
   159  
   160  // LogConfig represents log config for server
   161  type LogConfig struct {
   162  	File *LogFileConfig `toml:"file" json:"file"`
   163  }
   164  
   165  var defaultServerConfig = &ServerConfig{
   166  	Addr:          "127.0.0.1:8300",
   167  	AdvertiseAddr: "",
   168  	LogFile:       "",
   169  	LogLevel:      "info",
   170  	Log: &LogConfig{
   171  		File: &LogFileConfig{
   172  			MaxSize:    300,
   173  			MaxDays:    0,
   174  			MaxBackups: 0,
   175  		},
   176  	},
   177  	DataDir: "",
   178  	GcTTL:   24 * 60 * 60, // 24H
   179  	TZ:      "System",
   180  	// The default election-timeout in PD is 3s and minimum session TTL is 5s,
   181  	// which is calculated by `math.Ceil(3 * election-timeout / 2)`, we choose
   182  	// default capture session ttl to 10s to increase robust to PD jitter,
   183  	// however it will decrease RTO when single TiCDC node error happens.
   184  	CaptureSessionTTL:      10,
   185  	OwnerFlushInterval:     TomlDuration(200 * time.Millisecond),
   186  	ProcessorFlushInterval: TomlDuration(100 * time.Millisecond),
   187  	Sorter: &SorterConfig{
   188  		NumConcurrentWorker:    4,
   189  		ChunkSizeLimit:         128 * 1024 * 1024,       // 128MB
   190  		MaxMemoryPressure:      30,                      // 30% is safe on machines with memory capacity <= 16GB
   191  		MaxMemoryConsumption:   16 * 1024 * 1024 * 1024, // 16GB
   192  		NumWorkerPoolGoroutine: 16,
   193  		SortDir:                DefaultSortDir,
   194  	},
   195  	Security:            &SecurityConfig{},
   196  	PerTableMemoryQuota: 20 * 1024 * 1024, // 20MB
   197  	KVClient: &KVClientConfig{
   198  		WorkerConcurrent: 8,
   199  		WorkerPoolSize:   0, // 0 will use NumCPU() * 2
   200  		RegionScanLimit:  40,
   201  	},
   202  }
   203  
   204  // ServerConfig represents a config for server
   205  type ServerConfig struct {
   206  	Addr          string `toml:"addr" json:"addr"`
   207  	AdvertiseAddr string `toml:"advertise-addr" json:"advertise-addr"`
   208  
   209  	LogFile  string     `toml:"log-file" json:"log-file"`
   210  	LogLevel string     `toml:"log-level" json:"log-level"`
   211  	Log      *LogConfig `toml:"log" json:"log"`
   212  
   213  	DataDir string `toml:"data-dir" json:"data-dir"`
   214  
   215  	GcTTL int64  `toml:"gc-ttl" json:"gc-ttl"`
   216  	TZ    string `toml:"tz" json:"tz"`
   217  
   218  	CaptureSessionTTL int `toml:"capture-session-ttl" json:"capture-session-ttl"`
   219  
   220  	OwnerFlushInterval     TomlDuration `toml:"owner-flush-interval" json:"owner-flush-interval"`
   221  	ProcessorFlushInterval TomlDuration `toml:"processor-flush-interval" json:"processor-flush-interval"`
   222  
   223  	Sorter              *SorterConfig   `toml:"sorter" json:"sorter"`
   224  	Security            *SecurityConfig `toml:"security" json:"security"`
   225  	PerTableMemoryQuota uint64          `toml:"per-table-memory-quota" json:"per-table-memory-quota"`
   226  	KVClient            *KVClientConfig `toml:"kv-client" json:"kv-client"`
   227  }
   228  
   229  // Marshal returns the json marshal format of a ServerConfig
   230  func (c *ServerConfig) Marshal() (string, error) {
   231  	cfg, err := json.Marshal(c)
   232  	if err != nil {
   233  		return "", cerror.WrapError(cerror.ErrEncodeFailed, errors.Annotatef(err, "Unmarshal data: %v", c))
   234  	}
   235  	return string(cfg), nil
   236  }
   237  
   238  // Unmarshal unmarshals into *ServerConfig from json marshal byte slice
   239  func (c *ServerConfig) Unmarshal(data []byte) error {
   240  	err := json.Unmarshal(data, c)
   241  	if err != nil {
   242  		return cerror.WrapError(cerror.ErrDecodeFailed, err)
   243  	}
   244  	return nil
   245  }
   246  
   247  // String implements the Stringer interface
   248  func (c *ServerConfig) String() string {
   249  	s, _ := c.Marshal()
   250  	return s
   251  }
   252  
   253  // Clone clones a replication
   254  func (c *ServerConfig) Clone() *ServerConfig {
   255  	str, err := c.Marshal()
   256  	if err != nil {
   257  		log.Panic("failed to marshal replica config",
   258  			zap.Error(cerror.WrapError(cerror.ErrDecodeFailed, err)))
   259  	}
   260  	clone := new(ServerConfig)
   261  	err = clone.Unmarshal([]byte(str))
   262  	if err != nil {
   263  		log.Panic("failed to unmarshal replica config",
   264  			zap.Error(cerror.WrapError(cerror.ErrDecodeFailed, err)))
   265  	}
   266  	return clone
   267  }
   268  
   269  // ValidateAndAdjust validates and adjusts the server configuration
   270  func (c *ServerConfig) ValidateAndAdjust() error {
   271  	if c.Addr == "" {
   272  		return cerror.ErrInvalidServerOption.GenWithStack("empty address")
   273  	}
   274  	if c.AdvertiseAddr == "" {
   275  		c.AdvertiseAddr = c.Addr
   276  	}
   277  	// Advertise address must be specified.
   278  	if idx := strings.LastIndex(c.AdvertiseAddr, ":"); idx >= 0 {
   279  		ip := net.ParseIP(c.AdvertiseAddr[:idx])
   280  		// Skip nil as it could be a domain name.
   281  		if ip != nil && ip.IsUnspecified() {
   282  			return cerror.ErrInvalidServerOption.GenWithStack("advertise address must be specified as a valid IP")
   283  		}
   284  	} else {
   285  		return cerror.ErrInvalidServerOption.GenWithStack("advertise address or address does not contain a port")
   286  	}
   287  	if c.GcTTL == 0 {
   288  		return cerror.ErrInvalidServerOption.GenWithStack("empty GC TTL is not allowed")
   289  	}
   290  	// 5s is minimum lease ttl in etcd(PD)
   291  	if c.CaptureSessionTTL < 5 {
   292  		log.Warn("capture session ttl too small, set to default value 10s")
   293  		c.CaptureSessionTTL = 10
   294  	}
   295  
   296  	if c.Security != nil && c.Security.IsTLSEnabled() {
   297  		var err error
   298  		_, err = c.Security.ToTLSConfig()
   299  		if err != nil {
   300  			return errors.Annotate(err, "invalidate TLS config")
   301  		}
   302  		_, err = c.Security.ToGRPCDialOption()
   303  		if err != nil {
   304  			return errors.Annotate(err, "invalidate TLS config")
   305  		}
   306  	}
   307  
   308  	if c.Sorter == nil {
   309  		c.Sorter = defaultServerConfig.Sorter
   310  	}
   311  	c.Sorter.SortDir = DefaultSortDir
   312  
   313  	if c.Sorter.ChunkSizeLimit < 1*1024*1024 {
   314  		return cerror.ErrIllegalUnifiedSorterParameter.GenWithStackByArgs("chunk-size-limit should be at least 1MB")
   315  	}
   316  	if c.Sorter.NumConcurrentWorker < 1 {
   317  		return cerror.ErrIllegalUnifiedSorterParameter.GenWithStackByArgs("num-concurrent-worker should be at least 1")
   318  	}
   319  	if c.Sorter.NumWorkerPoolGoroutine > 4096 {
   320  		return cerror.ErrIllegalUnifiedSorterParameter.GenWithStackByArgs("num-workerpool-goroutine should be at most 4096")
   321  	}
   322  	if c.Sorter.NumConcurrentWorker > c.Sorter.NumWorkerPoolGoroutine {
   323  		return cerror.ErrIllegalUnifiedSorterParameter.GenWithStackByArgs("num-concurrent-worker larger than num-workerpool-goroutine is useless")
   324  	}
   325  	if c.Sorter.NumWorkerPoolGoroutine < 1 {
   326  		return cerror.ErrIllegalUnifiedSorterParameter.GenWithStackByArgs("num-workerpool-goroutine should be at least 1, larger than 8 is recommended")
   327  	}
   328  	if c.Sorter.MaxMemoryPressure < 0 || c.Sorter.MaxMemoryPressure > 100 {
   329  		return cerror.ErrIllegalUnifiedSorterParameter.GenWithStackByArgs("max-memory-percentage should be a percentage")
   330  	}
   331  
   332  	if c.PerTableMemoryQuota == 0 {
   333  		c.PerTableMemoryQuota = defaultServerConfig.PerTableMemoryQuota
   334  	}
   335  	if c.PerTableMemoryQuota < 6*1024*1024 {
   336  		return cerror.ErrInvalidServerOption.GenWithStackByArgs("per-table-memory-quota should be at least 6MB")
   337  	}
   338  
   339  	if c.KVClient == nil {
   340  		c.KVClient = defaultServerConfig.KVClient
   341  	}
   342  	if c.KVClient.WorkerConcurrent <= 0 {
   343  		return cerror.ErrInvalidServerOption.GenWithStackByArgs("region-scan-limit should be at least 1")
   344  	}
   345  	if c.KVClient.RegionScanLimit <= 0 {
   346  		return cerror.ErrInvalidServerOption.GenWithStackByArgs("region-scan-limit should be at least 1")
   347  	}
   348  
   349  	return nil
   350  }
   351  
   352  // GetDefaultServerConfig returns the default server config
   353  func GetDefaultServerConfig() *ServerConfig {
   354  	return defaultServerConfig.Clone()
   355  }
   356  
   357  var globalServerConfig atomic.Value
   358  
   359  // GetGlobalServerConfig returns the global configuration for this server.
   360  // It should store configuration from command line and configuration file.
   361  // Other parts of the system can read the global configuration use this function.
   362  func GetGlobalServerConfig() *ServerConfig {
   363  	return globalServerConfig.Load().(*ServerConfig)
   364  }
   365  
   366  // StoreGlobalServerConfig stores a new config to the globalServerConfig. It mostly uses in the test to avoid some data races.
   367  func StoreGlobalServerConfig(config *ServerConfig) {
   368  	globalServerConfig.Store(config)
   369  }
   370  
   371  // TomlDuration is a duration with a custom json decoder and toml decoder
   372  type TomlDuration time.Duration
   373  
   374  // UnmarshalText is the toml decoder
   375  func (d *TomlDuration) UnmarshalText(text []byte) error {
   376  	stdDuration, err := time.ParseDuration(string(text))
   377  	if err != nil {
   378  		return err
   379  	}
   380  	*d = TomlDuration(stdDuration)
   381  	return nil
   382  }
   383  
   384  // UnmarshalJSON is the json decoder
   385  func (d *TomlDuration) UnmarshalJSON(b []byte) error {
   386  	var stdDuration time.Duration
   387  	if err := json.Unmarshal(b, &stdDuration); err != nil {
   388  		return err
   389  	}
   390  	*d = TomlDuration(stdDuration)
   391  	return nil
   392  }