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 }