Commit 80185218 authored by Quentin Smith's avatar Quentin Smith

storage/db: optimize /uploads queries without query string

This uses a different query structure for ListUploads when the query
string is empty. On our production database, this reduces the query
time from ~5s to ~0.1s, which drastically improves the performance of
perf.golang.org.

Change-Id: I1f2bf97449858d1b06791dd352cc437dcbda1fac
Reviewed-on: https://go-review.googlesource.com/37168
Run-TryBot: Quentin Smith <quentin@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 5a6afbd2
...@@ -590,33 +590,37 @@ func (db *DB) ListUploads(q string, extraLabels []string, limit int) *UploadList ...@@ -590,33 +590,37 @@ func (db *DB) ListUploads(q string, extraLabels []string, limit int) *UploadList
query += fmt.Sprintf(", (SELECT l%d.Value FROM RecordLabels l%d WHERE l%d.UploadID = j.UploadID AND Name = ? LIMIT 1)", i, i, i) query += fmt.Sprintf(", (SELECT l%d.Value FROM RecordLabels l%d WHERE l%d.UploadID = j.UploadID AND Name = ? LIMIT 1)", i, i, i)
args = append(args, label) args = append(args, label)
} }
query += " FROM (SELECT UploadID, COUNT(*) as rCount FROM "
sql, qArgs, err := parseQuery(q) sql, qArgs, err := parseQuery(q)
if err != nil { if err != nil {
ret.err = err ret.err = err
return ret return ret
} }
args = append(args, qArgs...) if len(sql) == 0 {
for i, part := range sql { // Optimize empty query.
if i > 0 { query += " FROM (SELECT UploadID, (SELECT COUNT(*) FROM Records r WHERE r.UploadID = u.UploadID) AS rCount FROM Uploads u ORDER BY u.Day DESC, u.Seq DESC, u.UploadID DESC"
query += " INNER JOIN " if limit != 0 {
query += fmt.Sprintf(" LIMIT %d", limit)
} }
query += fmt.Sprintf("(%s) t%d", part, i) query += ") j"
if i > 0 { } else {
query += " USING (UploadID, RecordID)" // Join individual queries.
query += " FROM (SELECT UploadID, COUNT(*) as rCount FROM "
args = append(args, qArgs...)
for i, part := range sql {
if i > 0 {
query += " INNER JOIN "
}
query += fmt.Sprintf("(%s) t%d", part, i)
if i > 0 {
query += " USING (UploadID, RecordID)"
}
} }
}
if len(sql) > 0 { query += " LEFT JOIN Records r USING (UploadID, RecordID)"
query += " LEFT JOIN" query += " GROUP BY UploadID) j LEFT JOIN Uploads u USING (UploadID) ORDER BY u.Day DESC, u.Seq DESC, u.UploadID DESC"
} if limit != 0 {
query += " Records r" query += fmt.Sprintf(" LIMIT %d", limit)
if len(sql) > 0 { }
query += " USING (UploadID, RecordID)"
}
query += " GROUP BY UploadID) j LEFT JOIN Uploads u USING (UploadID) ORDER BY u.Day DESC, u.Seq DESC, u.UploadID DESC"
if limit != 0 {
query += fmt.Sprintf(" LIMIT %d", limit)
} }
ret.sqlQuery, ret.sqlArgs = query, args ret.sqlQuery, ret.sqlArgs = query, args
......
...@@ -392,7 +392,10 @@ func TestListUploads(t *testing.T) { ...@@ -392,7 +392,10 @@ func TestListUploads(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(fmt.Sprintf("query=%s/limit=%d", test.query, test.limit), func(t *testing.T) { t.Run(fmt.Sprintf("query=%s/limit=%d", test.query, test.limit), func(t *testing.T) {
r := db.ListUploads(test.query, test.extraLabels, test.limit) r := db.ListUploads(test.query, test.extraLabels, test.limit)
defer r.Close() defer func() {
t.Logf("r.Debug: %s", r.Debug())
r.Close()
}()
var have []result var have []result
for r.Next() { for r.Next() {
ui := r.Info() ui := r.Info()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment