github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cmd/dm-syncer/config.go (about) 1 // Copyright 2019 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 main 15 16 import ( 17 "flag" 18 "fmt" 19 "os" 20 21 "github.com/BurntSushi/toml" 22 "github.com/go-mysql-org/go-mysql/mysql" 23 "github.com/pingcap/errors" 24 "github.com/pingcap/tidb/pkg/util/filter" 25 router "github.com/pingcap/tidb/pkg/util/table-router" 26 "github.com/pingcap/tiflow/dm/config" 27 "github.com/pingcap/tiflow/dm/config/dbconfig" 28 "github.com/pingcap/tiflow/dm/pkg/log" 29 bf "github.com/pingcap/tiflow/pkg/binlog-filter" 30 "github.com/pingcap/tiflow/pkg/version" 31 ) 32 33 // commonConfig collects common item for both new config and old config. 34 type commonConfig struct { 35 *flag.FlagSet `json:"-"` 36 37 // task name 38 Name string 39 printVersion bool 40 ConfigFile string 41 ServerID int 42 Flavor string 43 WorkerCount int 44 Batch int 45 StatusAddr string 46 Meta string 47 48 LogLevel string 49 LogFile string 50 LogFormat string 51 LogRotate string 52 53 EnableGTID bool 54 SafeMode bool 55 MaxRetry int 56 57 // deprecated 58 TimezoneStr string 59 60 SyncerConfigFormat bool 61 } 62 63 func (c *commonConfig) newConfigFromSyncerConfig(args []string) (*config.SubTaskConfig, error) { 64 cfg := &syncerConfig{ 65 printVersion: c.printVersion, 66 ConfigFile: c.ConfigFile, 67 ServerID: c.ServerID, 68 Flavor: c.Flavor, 69 WorkerCount: c.WorkerCount, 70 Batch: c.Batch, 71 StatusAddr: c.StatusAddr, 72 Meta: c.Meta, 73 LogLevel: c.LogLevel, 74 LogFile: c.LogFile, 75 LogFormat: c.LogFormat, 76 LogRotate: c.LogRotate, 77 EnableGTID: c.EnableGTID, 78 SafeMode: c.SafeMode, 79 MaxRetry: c.MaxRetry, 80 } 81 82 cfg.FlagSet = flag.NewFlagSet("dm-syncer", flag.ContinueOnError) 83 fs := cfg.FlagSet 84 85 var SyncerConfigFormat bool 86 var timezoneStr string 87 88 fs.BoolVar(&cfg.printVersion, "V", false, "prints version and exit") 89 fs.StringVar(&cfg.Name, "name", "", "the task name") 90 fs.StringVar(&cfg.ConfigFile, "config", "", "path to config file") 91 fs.IntVar(&cfg.ServerID, "server-id", 101, "MySQL slave server ID") 92 fs.StringVar(&cfg.Flavor, "flavor", mysql.MySQLFlavor, "use flavor for different MySQL source versions; support \"mysql\", \"mariadb\" now; if you replicate from mariadb, please set it to \"mariadb\"") 93 fs.IntVar(&cfg.WorkerCount, "c", 16, "parallel worker count") 94 fs.IntVar(&cfg.Batch, "b", 100, "batch commit count") 95 fs.StringVar(&cfg.StatusAddr, "status-addr", ":8271", "status addr") 96 fs.StringVar(&cfg.Meta, "meta", "syncer.meta", "syncer meta info") 97 // fs.StringVar(&cfg.PersistentTableDir, "persistent-dir", "", "syncer history table structures persistent dir; set to non-empty string will choosing history table structure according to column length when constructing DML") 98 fs.StringVar(&cfg.LogLevel, "L", "info", "log level: debug, info, warn, error, fatal") 99 fs.StringVar(&cfg.LogFile, "log-file", "", "log file path") 100 fs.StringVar(&cfg.LogFormat, "log-format", "text", `the format of the log, "text" or "json"`) 101 fs.StringVar(&cfg.LogRotate, "log-rotate", "day", "log file rotate type, hour/day") 102 fs.BoolVar(&cfg.EnableGTID, "enable-gtid", false, "enable gtid mode") 103 fs.BoolVar(&cfg.SafeMode, "safe-mode", false, "enable safe mode to make syncer reentrant") 104 fs.IntVar(&cfg.MaxRetry, "max-retry", 100, "maxinum retry when network interruption") 105 fs.StringVar(&timezoneStr, "timezone", "", "target database timezone location string") 106 fs.BoolVar(&SyncerConfigFormat, "syncer-config-format", false, "read syncer config format") 107 108 if err := fs.Parse(args); err != nil { 109 return nil, errors.Trace(err) 110 } 111 112 if cfg.ConfigFile != "" { 113 _, err := toml.DecodeFile(cfg.ConfigFile, cfg) 114 if err != nil { 115 return nil, errors.Trace(err) 116 } 117 } 118 119 if err := fs.Parse(args); err != nil { 120 return nil, errors.Trace(err) 121 } 122 123 if timezoneStr != "" { 124 log.L().Warn("'--timezone' is deprecated, needn't set it anymore.") 125 } 126 127 return cfg.convertToNewFormat() 128 } 129 130 func (c *commonConfig) parse(args []string) (*config.SubTaskConfig, error) { 131 if err := c.FlagSet.Parse(args); err != nil { 132 return nil, errors.Trace(err) 133 } 134 if c.printVersion { 135 fmt.Println(version.GetRawInfo()) 136 return nil, flag.ErrHelp 137 } 138 139 if c.SyncerConfigFormat { 140 return c.newConfigFromSyncerConfig(args) 141 } 142 143 return c.newSubTaskConfig(args) 144 } 145 146 func (c *commonConfig) newSubTaskConfig(args []string) (*config.SubTaskConfig, error) { 147 cfg := &config.SubTaskConfig{} 148 cfg.SetFlagSet(flag.NewFlagSet("dm-syncer", flag.ContinueOnError)) 149 fs := cfg.GetFlagSet() 150 151 var syncerConfigFormat bool 152 var printVersion bool 153 var serverID uint 154 var timezoneStr string 155 156 fs.BoolVar(&printVersion, "V", false, "prints version and exit") 157 fs.StringVar(&cfg.Name, "name", "", "the task name") 158 fs.StringVar(&cfg.ConfigFile, "config", "", "path to config file") 159 fs.UintVar(&serverID, "server-id", 101, "MySQL slave server ID") 160 fs.StringVar(&cfg.Flavor, "flavor", mysql.MySQLFlavor, "use flavor for different MySQL source versions; support \"mysql\", \"mariadb\" now; if you replicate from mariadb, please set it to \"mariadb\"") 161 fs.IntVar(&cfg.WorkerCount, "c", 16, "parallel worker count") 162 fs.IntVar(&cfg.Batch, "b", 100, "batch commit count") 163 fs.StringVar(&cfg.StatusAddr, "status-addr", ":8271", "status addr") 164 // fs.StringVar(&cfg.PersistentTableDir, "persistent-dir", "", "syncer history table structures persistent dir; set to non-empty string will choosing history table structure according to column length when constructing DML") 165 fs.StringVar(&cfg.LogLevel, "L", "info", "log level: debug, info, warn, error, fatal") 166 fs.StringVar(&cfg.LogFile, "log-file", "", "log file path") 167 fs.StringVar(&cfg.LogFormat, "log-format", "text", `the format of the log, "text" or "json"`) 168 fs.StringVar(&cfg.LogRotate, "log-rotate", "day", "log file rotate type, hour/day") 169 fs.BoolVar(&cfg.EnableGTID, "enable-gtid", false, "enable gtid mode") 170 fs.BoolVar(&cfg.SafeMode, "safe-mode", false, "enable safe mode to make syncer reentrant") 171 fs.IntVar(&cfg.MaxRetry, "max-retry", 100, "maxinum retry when network interruption") 172 fs.StringVar(&timezoneStr, "timezone", "", "target database timezone location string") 173 fs.StringVar(&cfg.Name, "cp-table-prefix", "dm-syncer", "the prefix of the checkpoint table name") 174 fs.BoolVar(&syncerConfigFormat, "syncer-config-format", false, "read syncer config format") 175 176 cfg.ServerID = uint32(serverID) 177 178 if err := cfg.Parse(args, false); err != nil { 179 return nil, errors.Trace(err) 180 } 181 if timezoneStr != "" { 182 log.L().Warn("'--timezone' is deprecated, needn't set it anymore.") 183 } 184 185 if serverID != 101 { 186 cfg.ServerID = uint32(serverID) 187 } 188 189 return cfg, nil 190 } 191 192 func newCommonConfig() *commonConfig { 193 cfg := &commonConfig{} 194 cfg.FlagSet = flag.NewFlagSet("dm-syncer", flag.ContinueOnError) 195 fs := cfg.FlagSet 196 197 fs.BoolVar(&cfg.printVersion, "V", false, "prints version and exit") 198 fs.StringVar(&cfg.Name, "name", "", "the task name") 199 fs.StringVar(&cfg.ConfigFile, "config", "", "path to config file") 200 fs.IntVar(&cfg.ServerID, "server-id", 101, "MySQL slave server ID") 201 fs.StringVar(&cfg.Flavor, "flavor", mysql.MySQLFlavor, "use flavor for different MySQL source versions; support \"mysql\", \"mariadb\" now; if you replicate from mariadb, please set it to \"mariadb\"") 202 fs.IntVar(&cfg.WorkerCount, "c", 16, "parallel worker count") 203 fs.IntVar(&cfg.Batch, "b", 100, "batch commit count") 204 fs.StringVar(&cfg.StatusAddr, "status-addr", ":8271", "status addr") 205 fs.StringVar(&cfg.Meta, "meta", "syncer.meta", "syncer meta info") 206 // fs.StringVar(&cfg.PersistentTableDir, "persistent-dir", "", "syncer history table structures persistent dir; set to non-empty string will choosing history table structure according to column length when constructing DML") 207 fs.StringVar(&cfg.LogLevel, "L", "info", "log level: debug, info, warn, error, fatal") 208 fs.StringVar(&cfg.LogFile, "log-file", "", "log file path") 209 fs.StringVar(&cfg.LogFormat, "log-format", "text", `the format of the log, "text" or "json"`) 210 fs.StringVar(&cfg.LogRotate, "log-rotate", "day", "log file rotate type, hour/day") 211 fs.BoolVar(&cfg.EnableGTID, "enable-gtid", false, "enable gtid mode") 212 fs.BoolVar(&cfg.SafeMode, "safe-mode", false, "enable safe mode to make syncer reentrant") 213 fs.IntVar(&cfg.MaxRetry, "max-retry", 100, "maxinum retry when network interruption") 214 fs.BoolVar(&cfg.SyncerConfigFormat, "syncer-config-format", false, "read syncer config format") 215 216 return cfg 217 } 218 219 // syncerConfig is the format of syncer tools, eventually it will be converted to new SubTaskConfig format. 220 type syncerConfig struct { 221 *flag.FlagSet `json:"-"` 222 223 Name string `toml:"name" json:"name"` 224 LogLevel string `toml:"log-level" json:"log-level"` 225 LogFile string `toml:"log-file" json:"log-file"` 226 LogFormat string `toml:"log-format" json:"log-format"` 227 LogRotate string `toml:"log-rotate" json:"log-rotate"` 228 229 StatusAddr string `toml:"status-addr" json:"status-addr"` 230 231 ServerID int `toml:"server-id" json:"server-id"` 232 Meta string `toml:"meta" json:"meta"` 233 // NOTE: This item is deprecated. 234 PersistentTableDir string `toml:"persistent-dir" json:"persistent-dir"` 235 Flavor string `toml:"flavor" json:"flavor"` 236 237 WorkerCount int `toml:"worker-count" json:"worker-count"` 238 Batch int `toml:"batch" json:"batch"` 239 MaxRetry int `toml:"max-retry" json:"max-retry"` 240 241 // Ref: http://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html#option_mysqld_replicate-do-table 242 DoTables []*filter.Table `toml:"replicate-do-table" json:"replicate-do-table"` 243 DoDBs []string `toml:"replicate-do-db" json:"replicate-do-db"` 244 245 // Ref: http://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html#option_mysqld_replicate-ignore-db 246 IgnoreTables []*filter.Table `toml:"replicate-ignore-table" json:"replicate-ignore-table"` 247 IgnoreDBs []string `toml:"replicate-ignore-db" json:"replicate-ignore-db"` 248 249 SkipDDLs []string `toml:"skip-ddls" json:"skip-ddls"` 250 // NOTE: SkipSQL and SkipEvents are no longer used, leave the comments to remind others. 251 // SkipSQLs is deprecated, please use SkipDDLs instead. 252 // SkipSQLs []string `toml:"skip-sqls" json:"-"` // omit it since it's deprecated 253 // SkipEvents is deprecated, please use SkipDMLs instead. 254 // SkipEvents []string `toml:"skip-events" json:"-"` // omit it since it's deprecated 255 SkipDMLs []*SkipDML `toml:"skip-dmls" json:"skip-dmls"` 256 257 RouteRules []*RouteRule `toml:"route-rules" json:"route-rules"` 258 259 From dbconfig.DBConfig `toml:"from" json:"from"` 260 To dbconfig.DBConfig `toml:"to" json:"to"` 261 262 EnableGTID bool `toml:"enable-gtid" json:"enable-gtid"` 263 AutoFixGTID bool `toml:"auto-fix-gtid" json:"auto-fix-gtid"` 264 265 SafeMode bool `toml:"safe-mode" json:"safe-mode"` 266 ConfigFile string `json:"config-file"` 267 268 // NOTE: These four configs are all deprecated. 269 // We leave this items as comments to remind others there WERE old config items. 270 // stopOnDDL bool `toml:"stop-on-ddl" json:"stop-on-ddl"` 271 // MaxDDLConnectionTimeout string `toml:"execute-ddl-timeout" json:"execute-ddl-timeout"` 272 // MaxDMLConnectionTimeout string `toml:"execute-dml-timeout" json:"execute-dml-timeout"` 273 // ExecutionQueueLength int `toml:"execute-queue-length" json:"execute-queue-length"` 274 275 TimezoneStr string `toml:"timezone" json:"timezone"` 276 277 printVersion bool 278 } 279 280 // RouteRule is route rule that syncing 281 // schema/table to specified schema/table 282 // This config has been replaced by `router.TableRule`. 283 type RouteRule struct { 284 PatternSchema string `toml:"pattern-schema" json:"pattern-schema"` 285 PatternTable string `toml:"pattern-table" json:"pattern-table"` 286 TargetSchema string `toml:"target-schema" json:"target-schema"` 287 TargetTable string `toml:"target-table" json:"target-table"` 288 } 289 290 // SkipDML defines config rule of skipping dml. 291 // This config has been replaced by BinLog filter. 292 type SkipDML struct { 293 Schema string `toml:"db-name" json:"db-name"` 294 Table string `toml:"tbl-name" json:"tbl-name"` 295 Type string `toml:"type" json:"type"` 296 } 297 298 func loadMetaFile(metaFile string) (*config.Meta, error) { 299 file, err := os.Open(metaFile) 300 if err != nil { 301 return nil, errors.Trace(err) 302 } 303 304 defer file.Close() 305 306 meta := &config.Meta{} 307 _, err = toml.DecodeReader(file, meta) 308 if err != nil { 309 return nil, errors.Trace(err) 310 } 311 return meta, nil 312 } 313 314 func (oc *syncerConfig) convertToNewFormat() (*config.SubTaskConfig, error) { 315 meta, err := loadMetaFile(oc.Meta) 316 if err != nil { 317 return nil, errors.Trace(err) 318 } 319 newTask := &config.SubTaskConfig{ 320 Name: oc.Name, 321 SourceID: fmt.Sprintf("%s_source", oc.Name), 322 Mode: config.ModeIncrement, 323 Meta: meta, 324 325 LogLevel: oc.LogLevel, 326 LogFile: oc.LogFile, 327 LogFormat: oc.LogFormat, 328 LogRotate: oc.LogRotate, 329 330 StatusAddr: oc.StatusAddr, 331 ServerID: uint32(oc.ServerID), 332 Flavor: oc.Flavor, 333 334 SyncerConfig: config.SyncerConfig{ 335 WorkerCount: oc.WorkerCount, 336 Batch: oc.Batch, 337 EnableGTID: oc.EnableGTID, 338 SafeMode: oc.SafeMode, 339 }, 340 341 BAList: &filter.Rules{ 342 DoTables: oc.DoTables, 343 DoDBs: oc.DoDBs, 344 IgnoreTables: oc.IgnoreTables, 345 IgnoreDBs: oc.IgnoreDBs, 346 }, 347 348 ConfigFile: oc.ConfigFile, 349 From: oc.From, 350 To: oc.To, 351 } 352 353 for _, rule := range oc.RouteRules { 354 newTask.RouteRules = append(newTask.RouteRules, &router.TableRule{ 355 SchemaPattern: rule.PatternSchema, 356 TablePattern: rule.PatternTable, 357 TargetSchema: rule.TargetSchema, 358 TargetTable: rule.TargetTable, 359 }) 360 } 361 362 newTask.FilterRules, err = generateBinlogEventRule(oc.SkipDDLs, oc.SkipDMLs) 363 if err != nil { 364 return nil, errors.Trace(err) 365 } 366 err = newTask.Adjust(false) 367 return newTask, err 368 } 369 370 func generateBinlogEventRule(skipDDLs []string, skipDMLs []*SkipDML) ([]*bf.BinlogEventRule, error) { 371 result := make([]*bf.BinlogEventRule, 0, 1+len(skipDMLs)) 372 ddlEvents := &bf.BinlogEventRule{} 373 for _, skipDDL := range skipDDLs { 374 if tp, _ := bf.ClassifyEvent(bf.EventType(skipDDL)); tp != "ddl" { 375 return nil, errors.NotValidf("event type %s", skipDDL) 376 } 377 ddlEvents.SQLPattern = append(ddlEvents.SQLPattern, skipDDL) 378 } 379 for _, skipDML := range skipDMLs { 380 if tp, _ := bf.ClassifyEvent(bf.EventType(skipDML.Type)); tp != "dml" { 381 return nil, errors.NotValidf("event type %s", skipDML.Type) 382 } 383 found := false 384 for _, evt := range result { 385 if evt.SchemaPattern == skipDML.Schema && evt.TablePattern == skipDML.Table { 386 found = true 387 evt.Events = append(evt.Events, bf.EventType(skipDML.Type)) 388 break 389 } 390 } 391 if !found { 392 result = append(result, &bf.BinlogEventRule{ 393 SchemaPattern: skipDML.Schema, 394 TablePattern: skipDML.Table, 395 Events: []bf.EventType{bf.EventType(skipDML.Type)}, 396 }) 397 } 398 } 399 return result, nil 400 }