-
Notifications
You must be signed in to change notification settings - Fork 31
/
rows.go
121 lines (102 loc) · 3.2 KB
/
rows.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Package tableio contains the routines for managing
// performance_schema.tableio_waits_by_table.
package tableio
import (
"database/sql"
"github.com/sjmudd/ps-top/log"
"github.com/sjmudd/ps-top/model/filter"
"github.com/sjmudd/ps-top/utils"
)
// Rows contains a set of rows
type Rows []Row
func totals(rows Rows) Row {
total := Row{Name: "Totals"}
for _, row := range rows {
total.SumTimerWait += row.SumTimerWait
total.SumTimerFetch += row.SumTimerFetch
total.SumTimerInsert += row.SumTimerInsert
total.SumTimerUpdate += row.SumTimerUpdate
total.SumTimerDelete += row.SumTimerDelete
total.SumTimerRead += row.SumTimerRead
total.SumTimerWrite += row.SumTimerWrite
total.CountStar += row.CountStar
total.CountFetch += row.CountFetch
total.CountInsert += row.CountInsert
total.CountUpdate += row.CountUpdate
total.CountDelete += row.CountDelete
total.CountRead += row.CountRead
total.CountWrite += row.CountWrite
}
return total
}
func collect(db *sql.DB, databaseFilter *filter.DatabaseFilter) Rows {
var t Rows
log.Printf("collect(?,%q)\n", databaseFilter)
// we collect all information even if it's mainly empty as we may reference it later
sql := `SELECT OBJECT_SCHEMA, OBJECT_NAME, COUNT_STAR, SUM_TIMER_WAIT, COUNT_READ, SUM_TIMER_READ, COUNT_WRITE, SUM_TIMER_WRITE, COUNT_FETCH, SUM_TIMER_FETCH, COUNT_INSERT, SUM_TIMER_INSERT, COUNT_UPDATE, SUM_TIMER_UPDATE, COUNT_DELETE, SUM_TIMER_DELETE FROM table_io_waits_summary_by_table WHERE SUM_TIMER_WAIT > 0`
args := []interface{}{}
// Apply the filter if provided and seems good.
if len(databaseFilter.Args()) > 0 {
sql = sql + databaseFilter.ExtraSQL()
for _, v := range databaseFilter.Args() {
args = append(args, v)
}
log.Printf("apply databaseFilter: sql: %q, args: %+v\n", sql, args)
}
rows, err := db.Query(sql, args...)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var schema, table string
var r Row
if err := rows.Scan(
&schema,
&table,
&r.CountStar,
&r.SumTimerWait,
&r.CountRead,
&r.SumTimerRead,
&r.CountWrite,
&r.SumTimerWrite,
&r.CountFetch,
&r.SumTimerFetch,
&r.CountInsert,
&r.SumTimerInsert,
&r.CountUpdate,
&r.SumTimerUpdate,
&r.CountDelete,
&r.SumTimerDelete); err != nil {
log.Fatal(err)
}
r.Name = utils.QualifiedTableName(schema, table)
// we collect all information even if it's mainly empty as we may reference it later
t = append(t, r)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
return t
}
// remove the initial values from those rows where there's a match
// - if we find a row we can't match ignore it
func (rows *Rows) subtract(initial Rows) {
initialByName := make(map[string]int)
// iterate over rows by name
for i := range initial {
initialByName[initial[i].Name] = i
}
for i := range *rows {
rowName := (*rows)[i].Name
if _, ok := initialByName[rowName]; ok {
initialIndex := initialByName[rowName]
(*rows)[i].subtract(initial[initialIndex])
}
}
}
// if the data in t2 is "newer", "has more values" than t then it needs refreshing.
// check this by comparing totals.
func (rows Rows) needsRefresh(otherRows Rows) bool {
return totals(rows).SumTimerWait > totals(otherRows).SumTimerWait
}