Skip to content
This repository has been archived by the owner on Nov 24, 2023. It is now read-only.

config/task: add check for duplicate config items (#380) #385

Merged
merged 6 commits into from
Nov 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions _utils/terror_gen/errors_release.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ ErrConfigMydumperPathNotValid,[code=20028:class=config:scope=internal:level=medi
ErrConfigLoaderCfgNotFound,[code=20029:class=config:scope=internal:level=medium],"mysql-instance(%d)'s loader config %s not exist in loaders"
ErrConfigSyncerCfgNotFound,[code=20030:class=config:scope=internal:level=medium],"mysql-instance(%d)'s syncer config %s not exist in syncer"
ErrConfigSourceIDNotFound,[code=20031:class=config:scope=internal:level=medium],"source %s in deployment configuration not found"
ErrConfigDuplicateCfgItem,[code=20032:class=config:scope=internal:level=medium],"the following mysql configs have duplicate items, please remove the duplicates:\n%s"
ErrBinlogExtractPosition,[code=22001:class=binlog-op:scope=internal:level=high],""
ErrBinlogInvalidFilename,[code=22002:class=binlog-op:scope=internal:level=high],"invalid binlog filename"
ErrBinlogParsePosFromStr,[code=22003:class=binlog-op:scope=internal:level=high],""
Expand Down
31 changes: 31 additions & 0 deletions dm/config/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ package config

import (
"flag"
"fmt"
"io/ioutil"
"strings"
"time"

"github.com/pingcap/dm/pkg/log"
Expand Down Expand Up @@ -348,6 +350,7 @@ func (c *TaskConfig) adjust() error {
}

iids := make(map[string]int) // source-id -> instance-index
duplicateErrorStrings := make([]string, 0)
for i, inst := range c.MySQLInstances {
if err := inst.Verify(); err != nil {
return terror.Annotatef(err, "mysql-instance: %s", humanize.Ordinal(i))
Expand Down Expand Up @@ -443,6 +446,16 @@ func (c *TaskConfig) adjust() error {
if inst.SyncerThread != 0 {
inst.Syncer.WorkerCount = inst.SyncerThread
}

if dupeRules := checkDuplicateString(inst.RouteRules); len(dupeRules) > 0 {
duplicateErrorStrings = append(duplicateErrorStrings, fmt.Sprintf("mysql-instance(%d)'s route-rules: %s", i, strings.Join(dupeRules, ", ")))
}
if dupeRules := checkDuplicateString(inst.FilterRules); len(dupeRules) > 0 {
duplicateErrorStrings = append(duplicateErrorStrings, fmt.Sprintf("mysql-instance(%d)'s filter-rules: %s", i, strings.Join(dupeRules, ", ")))
}
}
if len(duplicateErrorStrings) > 0 {
return terror.ErrConfigDuplicateCfgItem.Generate(strings.Join(duplicateErrorStrings, "\n"))
}

if c.Timezone != "" {
Expand Down Expand Up @@ -516,3 +529,21 @@ func (c *TaskConfig) SubTaskConfigs(sources map[string]DBConfig) ([]*SubTaskConf
}
return cfgs, nil
}

// checkDuplicateString checks whether the given string array has duplicate string item
// if there is duplicate, it will return **all** the duplicate strings
func checkDuplicateString(ruleNames []string) []string {
mp := make(map[string]bool, len(ruleNames))
dupeArray := make([]string, 0)
for _, name := range ruleNames {
if added, ok := mp[name]; ok {
if !added {
dupeArray = append(dupeArray, name)
mp[name] = true
}
} else {
mp[name] = false
}
}
return dupeArray
}
75 changes: 74 additions & 1 deletion dm/config/task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
package config

import (
. "github.com/pingcap/check"
"io/ioutil"
"path"
"sort"

"github.com/pingcap/dm/pkg/terror"

. "github.com/pingcap/check"
)

func (t *testConfig) TestInvalidTaskConfig(c *C) {
Expand Down Expand Up @@ -96,6 +100,8 @@ timezone: "Asia/Shanghai"
ignore-checking-items: ["all"]
`)
err = ioutil.WriteFile(filepath, configContent, 0644)
c.Assert(err, IsNil)
taskConfig = NewTaskConfig()
err = taskConfig.DecodeFile(filepath)
c.Assert(err, NotNil)
c.Assert(err, ErrorMatches, "*line 2: field aaa not found in type config.TaskConfig*")
Expand All @@ -113,6 +119,8 @@ timezone: "Asia/Shanghai"
ignore-checking-items: ["all"]
`)
err = ioutil.WriteFile(filepath, configContent, 0644)
c.Assert(err, IsNil)
taskConfig = NewTaskConfig()
err = taskConfig.DecodeFile(filepath)
c.Assert(err, NotNil)
c.Assert(err, ErrorMatches, "*line 4: field task-mode already set in type config.TaskConfig*")
Expand Down Expand Up @@ -181,6 +189,8 @@ syncers:
`)

err = ioutil.WriteFile(filepath, configContent, 0644)
c.Assert(err, IsNil)
taskConfig = NewTaskConfig()
err = taskConfig.DecodeFile(filepath)
c.Assert(err, IsNil)
c.Assert(taskConfig.MySQLInstances[0].Mydumper.Threads, Equals, 11)
Expand All @@ -192,4 +202,67 @@ syncers:
c.Assert(taskConfig.MySQLInstances[2].Mydumper.Threads, Equals, 44)
c.Assert(taskConfig.MySQLInstances[2].Loader.PoolSize, Equals, 55)
c.Assert(taskConfig.MySQLInstances[2].Syncer.WorkerCount, Equals, 66)

configContent = []byte(`---
name: test
task-mode: all
is-sharding: false
meta-schema: "dm_meta"
remove-meta: false
enable-heartbeat: true
heartbeat-update-interval: 1
heartbeat-report-interval: 1
timezone: "Asia/Shanghai"

target-database:
host: "127.0.0.1"
port: 4000
user: "root"
password: ""

mysql-instances:
- source-id: "mysql-replica-01"
- source-id: "mysql-replica-02"
- source-id: "mysql-replica-03"

black-white-list:
instance:
do-dbs: ["test"]

routes:
route-rule-1:
route-rule-2:
route-rule-3:
route-rule-4:

filters:
filter-rule-1:
filter-rule-2:
filter-rule-3:
filter-rule-4:
`)

err = ioutil.WriteFile(filepath, configContent, 0644)
c.Assert(err, IsNil)
taskConfig = NewTaskConfig()
err = taskConfig.DecodeFile(filepath)
c.Assert(err, IsNil)
taskConfig.MySQLInstances[0].RouteRules = []string{"route-rule-1", "route-rule-2", "route-rule-1", "route-rule-2"}
taskConfig.MySQLInstances[1].FilterRules = []string{"filter-rule-1", "filter-rule-2", "filter-rule-3", "filter-rule-2"}
err = taskConfig.adjust()
c.Assert(terror.ErrConfigDuplicateCfgItem.Equal(err), IsTrue)
c.Assert(err, ErrorMatches, `[\s\S]*mysql-instance\(0\)'s route-rules: route-rule-1, route-rule-2[\s\S]*`)
c.Assert(err, ErrorMatches, `[\s\S]*mysql-instance\(1\)'s filter-rules: filter-rule-2[\s\S]*`)

}

func (t *testConfig) TestCheckDuplicateString(c *C) {
a := []string{"a", "b", "c", "d"}
dupeStrings := checkDuplicateString(a)
c.Assert(dupeStrings, HasLen, 0)
a = []string{"a", "a", "b", "b", "c", "c"}
dupeStrings = checkDuplicateString(a)
c.Assert(dupeStrings, HasLen, 3)
sort.Strings(dupeStrings)
c.Assert(dupeStrings, DeepEquals, []string{"a", "b", "c"})
}
2 changes: 2 additions & 0 deletions pkg/terror/error_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ const (
codeConfigLoaderCfgNotFound
codeConfigSyncerCfgNotFound
codeConfigSourceIDNotFound
codeConfigDuplicateCfgItem
)

// Binlog operation error code list
Expand Down Expand Up @@ -636,6 +637,7 @@ var (
ErrConfigLoaderCfgNotFound = New(codeConfigLoaderCfgNotFound, ClassConfig, ScopeInternal, LevelMedium, "mysql-instance(%d)'s loader config %s not exist in loaders")
ErrConfigSyncerCfgNotFound = New(codeConfigSyncerCfgNotFound, ClassConfig, ScopeInternal, LevelMedium, "mysql-instance(%d)'s syncer config %s not exist in syncer")
ErrConfigSourceIDNotFound = New(codeConfigSourceIDNotFound, ClassConfig, ScopeInternal, LevelMedium, "source %s in deployment configuration not found")
ErrConfigDuplicateCfgItem = New(codeConfigDuplicateCfgItem, ClassConfig, ScopeInternal, LevelMedium, "the following mysql configs have duplicate items, please remove the duplicates:\n%s")

// Binlog operation error
ErrBinlogExtractPosition = New(codeBinlogExtractPosition, ClassBinlogOp, ScopeInternal, LevelHigh, "")
Expand Down