Skip to content

Commit

Permalink
add features
Browse files Browse the repository at this point in the history
complete refacto of how results are displayed
add some stats
  • Loading branch information
eze-kiel committed Mar 25, 2021
1 parent 629d908 commit c59f318
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 48 deletions.
165 changes: 134 additions & 31 deletions cmd/slowql-replayer/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bufio"
"database/sql"
"errors"
"flag"
Expand All @@ -17,7 +18,7 @@ import (
"github.com/devops-works/slowql"
"github.com/devops-works/slowql/cmd/slowql-replayer/pprof"
"github.com/devops-works/slowql/query"
"github.com/olekukonko/tablewriter"
. "github.com/logrusorgru/aurora"
"github.com/sirupsen/logrus"
"golang.org/x/term"

Expand Down Expand Up @@ -48,11 +49,12 @@ type database struct {
}

type results struct {
kind string
dryRun string
queries int
errors int
duration time.Duration
kind string
dryRun bool
queries int
errors int
duration time.Duration
realDuration time.Duration
}

func main() {
Expand Down Expand Up @@ -99,6 +101,12 @@ func main() {

var r results

db.logger.Info("getting real execution time")
realExec, err := getRealTime(opt.kind, opt.file)
if err != nil {
db.logger.Fatalf("cannot get real duration from log file: %s", err)
}

db.logger.Infof("%d workers will be created", opt.workers)
if opt.noDryRun {
db.logger.Warn("no-dry-run flag found, replaying for real")
Expand All @@ -111,14 +119,10 @@ func main() {
db.logger.Fatalf("cannot replay %s: %s", opt.kind, err)
}

if opt.noDryRun {
r.dryRun = "no"
} else {
r.dryRun = "yes"
}
r.dryRun = !opt.noDryRun
r.kind = opt.kind

r.show()
r.realDuration = realExec
r.show(opt)
}

// parse ensures that no options has been omitted. It also asks for a password
Expand Down Expand Up @@ -344,37 +348,65 @@ func (db *database) replay(f io.Reader) (results, error) {
return r, nil
}

func (r results) show() {
data := []string{
r.kind,
r.dryRun,
strconv.Itoa(r.queries),
strconv.Itoa(r.errors),
r.duration.String(),
func (r results) show(o options) {
prcSuccess := (float64(r.queries) - float64(r.errors)) * 100.0 / float64(r.queries)
durationDelta := fmt.Sprint(r.duration - r.realDuration)
if durationDelta == r.duration.String() {
durationDelta = "n/a"
} else if r.duration > r.realDuration {
durationDelta = "replayer took " + durationDelta + " more"
} else if r.duration < r.realDuration {
durationDelta = "replayer took " + durationDelta + " less"
}
t := newTable()
t.Append(data)
t.Render()

fmt.Printf(`
=-= Results =-=
Replay duration: %s
Log file: %s
Dry run: %v
Workers: %d
Database
├─ kind: %s
├─ user: %s
├─ use pass: %v
└─ address: %s
Statistics
├─ Queries: %d
├─ Errors: %d
├─ Queries success rate: %.4f%%
└─ Duration difference: %s
`,
Bold(r.duration),
Bold(o.file),
Bold(r.dryRun),
Bold(o.workers),
// database
Bold(r.kind),
Bold(o.user),
Bold(o.usePass),
Bold(o.host),
// statistics
Bold(r.queries),
Bold(r.errors),
Bold(prcSuccess),
Bold(durationDelta),
)
}

func newSpinner(t int) *spinner.Spinner {
return spinner.New(spinner.CharSets[t], 100*time.Millisecond)
}

func newTable() *tablewriter.Table {
table := tablewriter.NewWriter(os.Stdout)
table.SetAlignment(tablewriter.ALIGN_CENTER)
table.SetHeader([]string{"DB", "dry run", "Queries", "Errors", "Duration"})
return table
}

func (db database) worker(queries chan string, errors chan error, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case q, ok := <-queries:
if !ok {
db.logger.Trace("channel closed, worker exiting")
wg.Done()
return
}
rows, err := db.drv.Query(q)
Expand All @@ -400,3 +432,74 @@ func (r *results) errorsCollector(errors chan error) {
}
}
}

// getRealTime returns the real duration of the slow query log based on the Time
// tags in the headers
func getRealTime(k, file string) (time.Duration, error) {
f, err := os.Open(file)
if err != nil {
return time.Duration(0), err
}
defer f.Close()

s := bufio.NewScanner(f)
var start, end string

switch k {
case "mysql", "pxc":
// Get the first Time
for s.Scan() {
if strings.Contains(s.Text(), "Time:") {
parts := strings.Split(s.Text(), " ")
start = parts[2]
break
}
}

// Get the last time
for s.Scan() {
if strings.Contains(s.Text(), "Time:") {
parts := strings.Split(s.Text(), " ")
end = parts[2]
}
}

timeStart, err := time.Parse("2006-01-02T15:04:05.999999Z", start)
if err != nil {
return time.Duration(0), err
}
timeEnd, err := time.Parse("2006-01-02T15:04:05.999999Z", end)
if err != nil {
return time.Duration(0), err
}
return timeEnd.Sub(timeStart), nil
case "mariadb":
// Get the first Time
for s.Scan() {
if strings.Contains(s.Text(), "Time:") {
parts := strings.Split(s.Text(), " ")
start = parts[2] + " " + parts[3]
break
}
}
// Get the last time
for s.Scan() {
if strings.Contains(s.Text(), "Time:") {
parts := strings.Split(s.Text(), " ")
end = parts[2] + " " + parts[3]
}
}

timeStart, err := time.Parse("060102 15:04:05", start)
if err != nil {
return time.Duration(0), err
}
timeEnd, err := time.Parse("060102 15:04:05", end)
if err != nil {
return time.Duration(0), err
}
return timeEnd.Sub(timeStart), nil
default:
return time.Duration(0), nil
}
}
13 changes: 7 additions & 6 deletions database/mariadb/mariadb.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,15 @@ func (p *Database) ParseServerMeta(lines chan []string) {
p.srv.VersionDescription = p.srv.Binary
p.srv.Port = 0
p.srv.Socket = p.srv.Binary
} else {
p.srv.Binary = matches[1]
p.srv.VersionShort = matches[2]
p.srv.Version = p.srv.VersionShort + matches[3]
p.srv.VersionDescription = matches[4]
p.srv.Port, _ = strconv.Atoi(strings.Split(net, " ")[2])
p.srv.Socket = strings.TrimLeft(strings.Split(net, ":")[2], " ")
}

p.srv.Binary = matches[1]
p.srv.VersionShort = matches[2]
p.srv.Version = p.srv.VersionShort + matches[3]
p.srv.VersionDescription = matches[4]
p.srv.Port, _ = strconv.Atoi(strings.Split(net, " ")[2])
p.srv.Socket = strings.TrimLeft(strings.Split(net, ":")[2], " ")
return
}
}
Expand Down
13 changes: 7 additions & 6 deletions database/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,15 @@ func (p *Database) ParseServerMeta(lines chan []string) {
p.srv.VersionDescription = p.srv.Binary
p.srv.Port = 0
p.srv.Socket = p.srv.Binary
} else {
p.srv.Binary = matches[1]
p.srv.VersionShort = matches[2]
p.srv.Version = p.srv.VersionShort + matches[3]
p.srv.VersionDescription = matches[4]
p.srv.Port, _ = strconv.Atoi(strings.Split(net, " ")[2])
p.srv.Socket = strings.TrimLeft(strings.Split(net, ":")[2], " ")
}

p.srv.Binary = matches[1]
p.srv.VersionShort = matches[2]
p.srv.Version = p.srv.VersionShort + matches[3]
p.srv.VersionDescription = matches[4]
p.srv.Port, _ = strconv.Atoi(strings.Split(net, " ")[2])
p.srv.Socket = strings.TrimLeft(strings.Split(net, ":")[2], " ")
return
}
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/briandowns/spinner v1.12.0
github.com/go-sql-driver/mysql v1.5.0
github.com/kr/pretty v0.1.0 // indirect
github.com/olekukonko/tablewriter v0.0.5
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/sirupsen/logrus v1.8.0
github.com/stretchr/testify v1.4.0 // indirect
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g=
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU=
Expand Down

0 comments on commit c59f318

Please sign in to comment.