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/35504
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/analysis/app/compare.go b/analysis/app/compare.go
index 7773ed8..073ba5c 100644
--- a/analysis/app/compare.go
+++ b/analysis/app/compare.go
@@ -79,11 +79,7 @@
return
}
- data, err := a.compareQuery(q)
- if err != nil {
- http.Error(w, err.Error(), 500)
- return
- }
+ data := a.compareQuery(q)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := t.Execute(w, data); err != nil {
@@ -94,32 +90,47 @@
type compareData struct {
Q string
+ Error string
Benchstat template.HTML
Groups []*resultGroup
Labels map[string]bool
}
-func (a *App) compareQuery(q string) (*compareData, error) {
+func (a *App) compareQuery(q string) *compareData {
// Parse query
queries := parseQueryString(q)
// Send requests
// TODO(quentin): Issue requests in parallel?
var groups []*resultGroup
- for _, q := range queries {
+ var found int
+ for _, qPart := range queries {
group := &resultGroup{}
- res := a.StorageClient.Query(q)
+ res := a.StorageClient.Query(qPart)
defer res.Close() // TODO: Should happen each time through the loop
for res.Next() {
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.
- return nil, err
+ return &compareData{
+ Q: q,
+ Error: err.Error(),
+ }
}
groups = append(groups, group)
}
+ if found == 0 {
+ return &compareData{
+ Q: q,
+ Error: "No results matched the query string.",
+ }, nil
+ }
+
// Attempt to automatically split results.
if len(groups) == 1 {
group := groups[0]
@@ -152,5 +163,5 @@
Groups: groups,
Labels: labels,
}
- return data, nil
+ return data
}
diff --git a/analysis/appengine/template/compare.html b/analysis/appengine/template/compare.html
index 01a9755..741ca6e 100644
--- a/analysis/appengine/template/compare.html
+++ b/analysis/appengine/template/compare.html
@@ -10,34 +10,38 @@
<input type="submit" value="Search">
</form>
</div>
- <div>
- {{.Benchstat}}
- </div>
- <table>
- <tr>
- <th>label</th>
- {{range $index, $group := .Groups}}
- <th>
- #{{$index}}
- </th>
- {{end}}
- </tr>
- {{range $label, $exists := .Labels}}
- <tr>
- <th>{{$label}}</th>
- {{range $.Groups}}
- <td>
- {{with index .LabelValues $label}}
- [
- {{range $value, $exists := .}}
- {{printf "%q" $value}}
- {{end}}
- ]
+ {{with .Error}}
+ <p>{{.}}</p>
+ {{else}}
+ <div>
+ {{.Benchstat}}
+ </div>
+ <table>
+ <tr>
+ <th>label</th>
+ {{range $index, $group := .Groups}}
+ <th>
+ #{{$index}}
+ </th>
{{end}}
- </td>
+ </tr>
+ {{range $label, $exists := .Labels}}
+ <tr>
+ <th>{{$label}}</th>
+ {{range $.Groups}}
+ <td>
+ {{with index .LabelValues $label}}
+ [
+ {{range $value, $exists := .}}
+ {{printf "%q" $value}}
+ {{end}}
+ ]
+ {{end}}
+ </td>
+ {{end}}
+ </tr>
{{end}}
- </tr>
- {{end}}
- </table>
+ </table>
+ {{end}}
</body>
</html>
diff --git a/storage/client.go b/storage/client.go
index 09f5925..b648d81 100644
--- a/storage/client.go
+++ b/storage/client.go
@@ -6,7 +6,9 @@
package storage
import (
+ "fmt"
"io"
+ "io/ioutil"
"net/http"
"net/url"
@@ -45,6 +47,15 @@
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)
return &Query{br: br, body: resp.Body}
@@ -93,7 +104,10 @@
// Close frees resources associated with the query.
func (q *Query) Close() error {
- q.body.Close()
+ if q.body != nil {
+ q.body.Close()
+ q.body = nil
+ }
return q.err
}
diff --git a/storage/client_test.go b/storage/client_test.go
index c05ad20..2938430 100644
--- a/storage/client_test.go
+++ b/storage/client_test.go
@@ -17,6 +17,25 @@
"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) {
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 {