diff --git a/cmd/soar/soar.go b/cmd/soar/soar.go index 313a6a325383ca7eb1f039c542229b81617cd334..7941a4302c6826247e58b4a686fbdc1a26981c3f 100644 --- a/cmd/soar/soar.go +++ b/cmd/soar/soar.go @@ -80,16 +80,17 @@ func main() { // 环境初始化,连接检查线上环境+构建测试环境 vEnv, rEnv := env.BuildEnv() - //如果使用CleanTestDataBase命令,则清除前1小时的数据库 - if common.Config.CleanTestDataBase { - vEnv.CleanTestDataBase() - } - // 如果使用到测试环境,在这里环境清理 if common.Config.DropTestTemporary { defer vEnv.CleanUp() } + // 使用CleanupTestDatabase命令手动清理残余的`optimizer_xxx`数据库 + // 为了保护当前正在评审的SQL,只清除前1小时前的残余 + if common.Config.CleanupTestDatabase { + vEnv.CleanupTestDatabase() + } + // 对指定的库表进行索引重复检查 if common.Config.ReportType == "duplicate-key-checker" { dupKeySuggest := advisor.DuplicateKeyChecker(rEnv) diff --git a/common/config.go b/common/config.go index 541138efe45f6bf355a651fef5d1a221abea7508..145da62370b98a2aaf2b48a7f715cbdd7adfff7b 100644 --- a/common/config.go +++ b/common/config.go @@ -43,6 +43,7 @@ type Configration struct { TestDSN *dsn `yaml:"test-dsn"` // 测试环境数据库配置 AllowOnlineAsTest bool `yaml:"allow-online-as-test"` // 允许Online环境也可以当作Test环境 DropTestTemporary bool `yaml:"drop-test-temporary"` // 是否清理Test环境产生的临时库表 + CleanupTestDatabase bool `yaml:"cleanup-test-database"` // 清理残余的测试数据库(程序异常退出或未开启drop-test-temporary) issue #48 OnlySyntaxCheck bool `yaml:"only-syntax-check"` // 只做语法检查不输出优化建议 SamplingStatisticTarget int `yaml:"sampling-statistic-target"` // 数据采样因子,对应postgres的default_statistics_target Sampling bool `yaml:"sampling"` // 数据采样开关 @@ -119,7 +120,6 @@ type Configration struct { Verbose bool `yaml:"verbose"` // verbose模式,会多输出一些信息 DryRun bool `yaml:"dry-run"` // 是否在预演环境执行 MaxPrettySQLLength int `yaml:"max-pretty-sql-length"` // 超出该长度的SQL会转换成指纹输出 - CleanTestDataBase bool `yaml:"clean-test-database"` // 清理数据库 issue #48 } // getDefaultLogOutput get default log-output by runtime.GOOS @@ -146,6 +146,7 @@ var Config = &Configration{ }, AllowOnlineAsTest: false, DropTestTemporary: true, + CleanupTestDatabase: false, DryRun: true, OnlySyntaxCheck: false, SamplingStatisticTarget: 100, @@ -220,7 +221,6 @@ var Config = &Configration{ ListTestSqls: false, ListReportTypes: false, MaxPrettySQLLength: 1024, - CleanTestDataBase: false, } type dsn struct { @@ -480,6 +480,7 @@ func readCmdFlags() error { testDSN := flag.String("test-dsn", FormatDSN(Config.TestDSN), "TestDSN, 测试环境数据库配置, username:password@ip:port/schema") allowOnlineAsTest := flag.Bool("allow-online-as-test", Config.AllowOnlineAsTest, "AllowOnlineAsTest, 允许线上环境也可以当作测试环境") dropTestTemporary := flag.Bool("drop-test-temporary", Config.DropTestTemporary, "DropTestTemporary, 是否清理测试环境产生的临时库表") + cleanupTestDatabase := flag.Bool("cleanup-test-database", Config.CleanupTestDatabase, "单次运行清理历史1小时前残余的测试库。") onlySyntaxCheck := flag.Bool("only-syntax-check", Config.OnlySyntaxCheck, "OnlySyntaxCheck, 只做语法检查不输出优化建议") profiling := flag.Bool("profiling", Config.Profiling, "Profiling, 开启数据采样的情况下在测试环境执行Profile") trace := flag.Bool("trace", Config.Trace, "Trace, 开启数据采样的情况下在测试环境执行Trace") @@ -547,7 +548,6 @@ func readCmdFlags() error { verbose := flag.Bool("verbose", Config.Verbose, "Verbose") dryrun := flag.Bool("dry-run", Config.DryRun, "是否在预演环境执行") maxPrettySQLLength := flag.Int("max-pretty-sql-length", Config.MaxPrettySQLLength, "MaxPrettySQLLength, 超出该长度的SQL会转换成指纹输出") - cleanTestDataBase := flag.Bool("clean-test-database", Config.CleanTestDataBase, "由于程序异常退出,导致临时数据库的存在,可用此命令删除。") // 一个不存在log-level,用于更新usage。 // 因为vitess里面也用了flag,这些vitess的参数我们不需要关注 if !Config.Verbose && runtime.GOOS != "windows" { @@ -566,6 +566,7 @@ func readCmdFlags() error { Config.TestDSN = parseDSN(*testDSN, Config.TestDSN) Config.AllowOnlineAsTest = *allowOnlineAsTest Config.DropTestTemporary = *dropTestTemporary + Config.CleanupTestDatabase = *cleanupTestDatabase Config.OnlySyntaxCheck = *onlySyntaxCheck Config.Profiling = *profiling Config.Trace = *trace @@ -651,7 +652,6 @@ func readCmdFlags() error { Config.DryRun = *dryrun Config.MaxPrettySQLLength = *maxPrettySQLLength Config.MaxVarcharLength = *maxVarcharLength - Config.CleanTestDataBase = *cleanTestDataBase if *ver { version() diff --git a/env/env.go b/env/env.go index 3513bb82b7999db99789fdb506ae0c0e6b610dd1..a148c56df58f04000bf7f404efa0dca9f15d5398 100644 --- a/env/env.go +++ b/env/env.go @@ -150,35 +150,42 @@ func (ve VirtualEnv) CleanUp() bool { return true } -// CleanTestDataBase 清除一小时前的环境 -func (ve *VirtualEnv) CleanTestDataBase() { - common.Log.Debug("CleanTestDataBase ...") +// CleanupTestDatabase 清除一小时前的环境 +func (ve *VirtualEnv) CleanupTestDatabase() { + common.Log.Debug("CleanupTestDatabase ...") dbs, err := ve.Query("show databases like 'optimizer%'") if err == nil { - for index, row := range dbs.Rows { - s := strings.Split(row.Str(index), "_") + for _, row := range dbs.Rows { + testDatabase := row.Str(0) + // test temporary database format `opimtizer_YYMMDDHHmmss_randomString(16)` + if len(testDatabase) != 39 { + common.Log.Debug("CleanupTestDatabase by pass %s", testDatabase) + continue + } + s := strings.Split(testDatabase, "_") pastTime, err := time.Parse("060102150405", s[1]) if err != nil { - common.Log.Error("CleanTestDataBase compute pastTime Error: %s", err) + common.Log.Error("CleanupTestDatabase compute pastTime Error: %s", err) } nowTime, err := time.Parse("060102150405", time.Now().Format("060102150405")) if err != nil { - common.Log.Error("CleanTestDataBase compute nowTime Error: %s", err) + common.Log.Error("CleanupTestDatabase compute nowTime Error: %s", err) } + // TODO: 1 hour should be configable subHour := nowTime.Sub(pastTime).Hours() if subHour > 1 { - _, err := ve.Query("drop database %s", row.Str(index)) + _, err := ve.Query("drop database %s", testDatabase) if err != nil { - common.Log.Error("CleanTestDataBase failed Error: %s", err) + common.Log.Error("CleanupTestDatabase failed Error: %s", err) } - common.Log.Debug("CleanTestDataBase, done") + common.Log.Debug("CleanupTestDatabase drop database %s", testDatabase) + } else { + common.Log.Debug("CleanupTestDatabase by pass database %s, less than %d hours", testDatabase, subHour) } - } } else { - common.Log.Error("CleanTestDataBase failed Error:%s", err) + common.Log.Error("CleanupTestDatabase failed Error:%s", err) } - } // BuildVirtualEnv rEnv为SQL源环境,DB使用的信息从接口获取 diff --git a/env/env_test.go b/env/env_test.go index 6bec0bfd0b8b85e88c61d9643d6f775bdd3c9a76..759a77b37776aedc6c657da1eb7f4ad4473cdca7 100644 --- a/env/env_test.go +++ b/env/env_test.go @@ -113,9 +113,34 @@ func TestNewVirtualEnv(t *testing.T) { }, t.Name(), update) } +func TestCleanupTestDatabase(t *testing.T) { + vEnv, _ := BuildEnv() + vEnv.Query("drop database if exists optimizer_100000000000_xxxxxxxxxxxxxxxx") + _, err := vEnv.Query("create database optimizer_100000000000_xxxxxxxxxxxxxxxx") + if err != nil { + t.Error(err) + } + vEnv.CleanupTestDatabase() + _, err = vEnv.Query("drop database optimizer_100000000000_xxxxxxxxxxxxxxxx") + if err == nil { + t.Error("optimizer_100000000000_xxxxxxxxxxxxxxxx exist, should be droped") + } + + vEnv.Query("drop database if exists optimizer_100000000000") + _, err = vEnv.Query("create database optimizer_100000000000") + if err != nil { + t.Error(err) + } + vEnv.CleanupTestDatabase() + _, err = vEnv.Query("drop database optimizer_100000000000") + if err != nil { + t.Error("optimizer_100000000000 not exist, should not be droped") + } +} + func TestGenTableColumns(t *testing.T) { vEnv, rEnv := BuildEnv() - vEnv.CleanTestDataBase() + vEnv.CleanupTestDatabase() defer vEnv.CleanUp() pretty.Println(common.Config.TestDSN.Disable)