Commit 111d9661 authored by Quentin Smith's avatar Quentin Smith

storage, analysis: report better errors

Show a meaningful error to the user if their query is invalid or
matches zero results.

Change-Id: I8894686b598e008bac418f85e5d6ab229b64ec09
Reviewed-on: https://go-review.googlesource.com/35504Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 5a7725d4
...@@ -79,11 +79,7 @@ func (a *App) compare(w http.ResponseWriter, r *http.Request) { ...@@ -79,11 +79,7 @@ func (a *App) compare(w http.ResponseWriter, r *http.Request) {
return return
} }
data, err := a.compareQuery(q) data := a.compareQuery(q)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := t.Execute(w, data); err != nil { if err := t.Execute(w, data); err != nil {
...@@ -94,32 +90,47 @@ func (a *App) compare(w http.ResponseWriter, r *http.Request) { ...@@ -94,32 +90,47 @@ func (a *App) compare(w http.ResponseWriter, r *http.Request) {
type compareData struct { type compareData struct {
Q string Q string
Error string
Benchstat template.HTML Benchstat template.HTML
Groups []*resultGroup Groups []*resultGroup
Labels map[string]bool Labels map[string]bool
} }
func (a *App) compareQuery(q string) (*compareData, error) { func (a *App) compareQuery(q string) *compareData {
// Parse query // Parse query
queries := parseQueryString(q) queries := parseQueryString(q)
// Send requests // Send requests
// TODO(quentin): Issue requests in parallel? // TODO(quentin): Issue requests in parallel?
var groups []*resultGroup var groups []*resultGroup
for _, q := range queries { var found int
for _, qPart := range queries {
group := &resultGroup{} group := &resultGroup{}
res := a.StorageClient.Query(q) res := a.StorageClient.Query(qPart)
defer res.Close() // TODO: Should happen each time through the loop defer res.Close() // TODO: Should happen each time through the loop
for res.Next() { for res.Next() {
group.add(res.Result()) group.add(res.Result())
found++
} }
if err := res.Err(); err != nil { err := res.Err()
res.Close()
if err != nil {
// TODO: If the query is invalid, surface that to the user. // TODO: If the query is invalid, surface that to the user.
return nil, err return &compareData{
Q: q,
Error: err.Error(),
}
} }
groups = append(groups, group) groups = append(groups, group)
} }
if found == 0 {
return &compareData{
Q: q,
Error: "No results matched the query string.",
}, nil
}
// Attempt to automatically split results. // Attempt to automatically split results.
if len(groups) == 1 { if len(groups) == 1 {
group := groups[0] group := groups[0]
...@@ -152,5 +163,5 @@ func (a *App) compareQuery(q string) (*compareData, error) { ...@@ -152,5 +163,5 @@ func (a *App) compareQuery(q string) (*compareData, error) {
Groups: groups, Groups: groups,
Labels: labels, Labels: labels,
} }
return data, nil return data
} }
...@@ -10,34 +10,38 @@ ...@@ -10,34 +10,38 @@
<input type="submit" value="Search"> <input type="submit" value="Search">
</form> </form>
</div> </div>
<div> {{with .Error}}
{{.Benchstat}} <p>{{.}}</p>
</div> {{else}}
<table> <div>
<tr> {{.Benchstat}}
<th>label</th> </div>
{{range $index, $group := .Groups}} <table>
<th> <tr>
#{{$index}} <th>label</th>
</th> {{range $index, $group := .Groups}}
{{end}} <th>
</tr> #{{$index}}
{{range $label, $exists := .Labels}} </th>
<tr> {{end}}
<th>{{$label}}</th> </tr>
{{range $.Groups}} {{range $label, $exists := .Labels}}
<td> <tr>
{{with index .LabelValues $label}} <th>{{$label}}</th>
[ {{range $.Groups}}
{{range $value, $exists := .}} <td>
{{printf "%q" $value}} {{with index .LabelValues $label}}
[
{{range $value, $exists := .}}
{{printf "%q" $value}}
{{end}}
]
{{end}} {{end}}
] </td>
{{end}} {{end}}
</td> </tr>
{{end}} {{end}}
</tr> </table>
{{end}} {{end}}
</table>
</body> </body>
</html> </html>
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
package storage package storage
import ( import (
"fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
...@@ -45,6 +47,15 @@ func (c *Client) Query(q string) *Query { ...@@ -45,6 +47,15 @@ func (c *Client) Query(q string) *Query {
return &Query{err: err} return &Query{err: err}
} }
if resp.StatusCode != 200 {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return &Query{err: err}
}
return &Query{err: fmt.Errorf("%s", body)}
}
br := benchfmt.NewReader(resp.Body) br := benchfmt.NewReader(resp.Body)
return &Query{br: br, body: resp.Body} return &Query{br: br, body: resp.Body}
...@@ -93,7 +104,10 @@ func (q *Query) Err() error { ...@@ -93,7 +104,10 @@ func (q *Query) Err() error {
// Close frees resources associated with the query. // Close frees resources associated with the query.
func (q *Query) Close() error { func (q *Query) Close() error {
q.body.Close() if q.body != nil {
q.body.Close()
q.body = nil
}
return q.err return q.err
} }
......
...@@ -17,6 +17,25 @@ import ( ...@@ -17,6 +17,25 @@ import (
"golang.org/x/perf/storage/benchfmt" "golang.org/x/perf/storage/benchfmt"
) )
func TestQueryError(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "invalid query", 500)
}))
defer ts.Close()
c := &Client{BaseURL: ts.URL}
q := c.Query("invalid query")
defer q.Close()
if q.Next() {
t.Error("Next = true, want false")
}
if q.Err() == nil {
t.Error("Err = nil, want error")
}
}
func TestQuery(t *testing.T) { func TestQuery(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if have, want := r.URL.RequestURI(), "/search?q=key1%3Avalue+key2%3Avalue"; have != want { if have, want := r.URL.RequestURI(), "/search?q=key1%3Avalue+key2%3Avalue"; have != want {
......
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