func (s *ManagerTestSuite) TestGetConfig(t *C) { mockConnFactory := &mock.ConnectionFactory{Conn: s.nullmysql} m := qan.NewManager(s.logger, mockConnFactory, s.clock, s.iterFactory, s.workerFactory, s.spool, s.im) t.Assert(m, NotNil) config := &qan.Config{ ServiceInstance: s.mysqlInstance, Interval: 300, MaxSlowLogSize: 1000, MaxWorkers: 3, WorkerRunTime: 300, Start: []mysql.Query{ mysql.Query{Set: "SET GLOBAL slow_query_log=OFF"}, mysql.Query{Set: "SET GLOBAL long_query_time=0.456"}, mysql.Query{Set: "SET GLOBAL slow_query_log=ON"}, }, Stop: []mysql.Query{ mysql.Query{Set: "SET GLOBAL slow_query_log=OFF"}, mysql.Query{Set: "SET GLOBAL long_query_time=10"}, }, } qanConfig, _ := json.Marshal(config) cmd := &proto.Cmd{ Ts: time.Now(), Cmd: "StartService", Data: qanConfig, } reply := m.Handle(cmd) t.Assert(reply.Error, Equals, "") test.WaitStatusPrefix(1, m, "qan-log-parser", "Idle") s.nullmysql.Reset() cmd = &proto.Cmd{ Cmd: "GetConfig", Service: "qan", } reply = m.Handle(cmd) t.Assert(reply.Error, Equals, "") t.Assert(reply.Data, NotNil) gotConfig := []proto.AgentConfig{} if err := json.Unmarshal(reply.Data, &gotConfig); err != nil { t.Fatal(err) } expectConfig := []proto.AgentConfig{ { InternalService: "qan", Config: string(qanConfig), Running: true, }, } if same, diff := test.IsDeeply(gotConfig, expectConfig); !same { test.Dump(gotConfig) t.Error(diff) } // Stop manager reply = m.Handle(&proto.Cmd{Cmd: "StopService"}) t.Assert(reply.Error, Equals, "") }
func (s *WorkerTestSuite) TestWorkerSlow001NoExamples(t *C) { job := &qan.Job{ Id: "99", SlowLogFile: testlog.Sample + "slow001.log", StartOffset: 0, EndOffset: 524, RunTime: time.Duration(3 * time.Second), ZeroRunTime: true, ExampleQueries: false, } w := qan.NewSlowLogWorker(s.logger, "qan-worker-1") got, _ := w.Run(job) expect := &qan.Result{} if err := test.LoadMmReport(sample+"slow001-no-examples.json", expect); err != nil { t.Fatal(err) } if same, diff := test.IsDeeply(got, expect); !same { test.Dump(got) t.Error(diff) } // Worker should be able to report its name and status. t.Check(w.Name(), Equals, "qan-worker-1") t.Check(w.Status(), Equals, "Done job "+job.Id) }
func (s *WorkerTestSuite) TestWorkerSlow011(t *C) { // Percona Server rate limit job := &qan.Job{ SlowLogFile: testlog.Sample + "slow011.log", StartOffset: 0, EndOffset: 3000, RunTime: time.Duration(3 * time.Second), ZeroRunTime: true, ExampleQueries: true, } w := qan.NewSlowLogWorker(s.logger, "qan-worker-1") got, _ := w.Run(job) expect := &qan.Result{} if err := test.LoadMmReport(sample+"slow011.json", expect); err != nil { t.Fatal(err) } if same, diff := test.IsDeeply(got, expect); !same { test.Dump(got) t.Error(diff) } }
func (s *ManagerTestSuite) TestRotateSlowLog(t *C) { // Same as TestRotateAndRemoveSlowLog, but with qan.Config.RemoveOldSlowLogs=false // and testing that Start and Stop queries were executed. slowlog := "slow006.log" files, _ := filepath.Glob("/tmp/" + slowlog + "-[0-9]*") for _, file := range files { os.Remove(file) } mockConnFactory := &mock.ConnectionFactory{Conn: s.nullmysql} m := qan.NewManager(s.logger, mockConnFactory, s.clock, s.iterFactory, s.workerFactory, s.spool, s.im) if m == nil { t.Fatal("Create qan.Manager") } config := &qan.Config{ ServiceInstance: s.mysqlInstance, Interval: 300, MaxSlowLogSize: 1000, RemoveOldSlowLogs: false, // <-- HERE ExampleQueries: false, MaxWorkers: 2, WorkerRunTime: 600, Start: []mysql.Query{ mysql.Query{Set: "SET GLOBAL slow_query_log=OFF"}, mysql.Query{Set: "SET GLOBAL long_query_time=0.456"}, mysql.Query{Set: "SET GLOBAL slow_query_log=ON"}, }, Stop: []mysql.Query{ mysql.Query{Set: "SET GLOBAL slow_query_log=OFF"}, mysql.Query{Set: "SET GLOBAL long_query_time=10"}, }, } qanConfig, _ := json.Marshal(config) cmd := &proto.Cmd{ Ts: time.Now(), Cmd: "StartService", Data: qanConfig, } reply := m.Handle(cmd) t.Assert(reply.Error, Equals, "") test.WaitStatusPrefix(1, m, "qan-log-parser", "Idle") s.nullmysql.Reset() cp := exec.Command("cp", testlog.Sample+slowlog, "/tmp/"+slowlog) cp.Run() // First interval: 0 - 736 now := time.Now() i1 := &qan.Interval{ Filename: "/tmp/" + slowlog, StartOffset: 0, EndOffset: 736, StartTime: now, StopTime: now, } s.intervalChan <- i1 resultData := <-s.dataChan report := *resultData.(*qan.Report) if report.Global.TotalQueries != 2 { t.Error("First interval has 2 queries, got ", report.Global.TotalQueries) } if report.Global.UniqueQueries != 1 { t.Error("First interval has 1 unique query, got ", report.Global.UniqueQueries) } // Second interval: 736 - 1833, but will actually go to end: 2200, if not // the next two test will fail. i2 := &qan.Interval{ Filename: "/tmp/" + slowlog, StartOffset: 736, EndOffset: 1833, StartTime: now, StopTime: now, } s.intervalChan <- i2 resultData = <-s.dataChan report = *resultData.(*qan.Report) if report.Global.TotalQueries != 4 { t.Error("Second interval has 2 queries, got ", report.Global.TotalQueries) } if report.Global.UniqueQueries != 2 { t.Error("Second interval has 2 unique queries, got ", report.Global.UniqueQueries) } test.WaitStatus(1, m, "qan-log-parser", "Idle (0 of 2 running)") // Original slow log should no longer exist; it was rotated away. if _, err := os.Stat("/tmp/" + slowlog); !os.IsNotExist(err) { t.Error("/tmp/" + slowlog + " no longer exists") } // The original slow log should NOT have been removed. files, _ = filepath.Glob("/tmp/" + slowlog + "-[0-9]*") if len(files) != 1 { t.Errorf("Old slow log not removed, got %+v", files) } defer func() { for _, file := range files { os.Remove(file) } }() expect := []mysql.Query{} for _, q := range config.Stop { expect = append(expect, q) } for _, q := range config.Start { expect = append(expect, q) } if same, diff := test.IsDeeply(s.nullmysql.GetSet(), expect); !same { t.Logf("%+v", s.nullmysql.GetSet()) t.Logf("%+v", expect) t.Error(diff) } // Stop manager reply = m.Handle(&proto.Cmd{Cmd: "StopService"}) t.Assert(reply.Error, Equals, "") }
func (s *ManagerTestSuite) TestStartService(t *C) { /** * Create and start manager. */ m := qan.NewManager(s.logger, &mysql.RealConnectionFactory{}, s.clock, s.iterFactory, s.workerFactory, s.spool, s.im) t.Assert(m, NotNil) // Create the qan config. tmpFile := fmt.Sprintf("/tmp/qan_test.TestStartService.%d", os.Getpid()) defer func() { os.Remove(tmpFile) }() config := &qan.Config{ ServiceInstance: s.mysqlInstance, Start: []mysql.Query{ mysql.Query{Set: "SET GLOBAL slow_query_log=OFF"}, mysql.Query{Set: "SET GLOBAL long_query_time=0.123"}, mysql.Query{Set: "SET GLOBAL slow_query_log=ON"}, }, Stop: []mysql.Query{ mysql.Query{Set: "SET GLOBAL slow_query_log=OFF"}, mysql.Query{Set: "SET GLOBAL long_query_time=10"}, }, Interval: 300, // 5 min MaxSlowLogSize: 1073741824, // 1 GiB RemoveOldSlowLogs: true, ExampleQueries: true, MaxWorkers: 2, WorkerRunTime: 600, // 10 min } // Create the StartService cmd which contains the qan config. now := time.Now() qanConfig, _ := json.Marshal(config) cmd := &proto.Cmd{ User: "******", Ts: now, AgentUuid: "123", Service: "agent", Cmd: "StartService", Data: qanConfig, } // Have the service manager start the qa service reply := m.Handle(cmd) // It should start without error. t.Assert(reply.Error, Equals, "") // It should write the config to disk. data, err := ioutil.ReadFile(pct.Basedir.ConfigFile("qan")) t.Check(err, IsNil) gotConfig := &qan.Config{} err = json.Unmarshal(data, gotConfig) t.Check(err, IsNil) if same, diff := test.IsDeeply(gotConfig, config); !same { test.Dump(gotConfig) t.Error(diff) } // And status should be "Running" and "Idle". test.WaitStatus(1, m, "qan-log-parser", "Idle (0 of 2 running)") status := m.Status() t.Check(status["qan"], Equals, "Running") t.Check(status["qan-log-parser"], Equals, "Idle (0 of 2 running)") // It should have enabled the slow log. slowLog := s.realmysql.GetGlobalVarNumber("slow_query_log") t.Assert(slowLog, Equals, float64(1)) longQueryTime := s.realmysql.GetGlobalVarNumber("long_query_time") t.Assert(longQueryTime, Equals, 0.123) // Starting an already started service should result in a ServiceIsRunningError. reply = m.Handle(cmd) t.Check(reply.Error, Not(Equals), "") // It should add a tickChan for the interval iter. t.Check(s.clock.Added, HasLen, 1) t.Check(s.clock.Removed, HasLen, 0) /** * Have manager run a worker, parse, and send data. */ interv := &qan.Interval{ Filename: testlog.Sample + "slow001.log", StartOffset: 0, EndOffset: 524, StartTime: now, StopTime: now, } s.intervalChan <- interv v := test.WaitData(s.dataChan) t.Assert(v, HasLen, 1) report := v[0].(*qan.Report) result := &qan.Result{ StopOffset: report.StopOffset, Global: report.Global, Classes: report.Class, } test.WriteData(result, tmpFile) t.Check(tmpFile, testlog.FileEquals, sample+"slow001.json") /** * Send StopService cmd to stop qan/qan-log-parser. */ now = time.Now() cmd = &proto.Cmd{ User: "******", Ts: now, AgentUuid: "123", Service: "agent", Cmd: "StopService", } // Have the service manager start the qa service reply = m.Handle(cmd) // It should start without error. t.Assert(reply.Error, Equals, "") // It should disable the slow log. slowLog = s.realmysql.GetGlobalVarNumber("slow_query_log") t.Assert(slowLog, Equals, float64(0)) longQueryTime = s.realmysql.GetGlobalVarNumber("long_query_time") t.Assert(longQueryTime, Equals, 10.0) // It should remove the tickChan (and not have added others). t.Check(s.clock.Added, HasLen, 1) t.Check(s.clock.Removed, HasLen, 1) // qan still running, but qan-log-parser stopped. test.WaitStatus(1, m, "qan-log-parser", "Stopped") status = m.Status() t.Check(status["qan"], Equals, "Running") t.Check(status["qan-log-parser"], Equals, "Stopped") }