Add benchmarks for watch over websocket and http

... and a quick doc on how to run them

```
$ godep go test ./pkg/apiserver -benchmem -run=XXX -bench=BenchmarkWatch
PASS
BenchmarkWatchHTTP-8        20000     95669 ns/op   15053 B/op    196 allocs/op
BenchmarkWatchWebsocket-8   10000    102871 ns/op   18430 B/op    204 allocs/op
```
pull/6/head
Clayton Coleman 2016-01-12 21:10:38 -05:00
parent 4f559b3af7
commit 443cafab90
3 changed files with 113 additions and 0 deletions

View File

@ -374,6 +374,15 @@ See [conformance-test.sh](http://releases.k8s.io/HEAD/hack/conformance-test.sh).
[Instructions here](flaky-tests.md)
## Benchmarking
To run benchmark tests, you'll typically use something like:
$ godep go test ./pkg/apiserver -benchmem -run=XXX -bench=BenchmarkWatch
The `-run=XXX` prevents normal unit tests for running, while `-bench` is a regexp for selecting which benchmarks to run.
See `go test -h` for more instructions on generating profiles from benchmarks.
## Regenerating the CLI documentation
```sh

View File

@ -38,6 +38,7 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
apiservertesting "k8s.io/kubernetes/pkg/apiserver/testing"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
@ -158,6 +159,7 @@ func addNewTestTypes() {
api.Scheme.AddKnownTypes(newGroupVersion,
&apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{},
&api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
api.Scheme.AddKnownTypes(newGroupVersion, &v1.Pod{})
}
func init() {

View File

@ -19,16 +19,20 @@ package apiserver
import (
"encoding/json"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"sync"
"testing"
"time"
"golang.org/x/net/websocket"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/api/unversioned"
apiservertesting "k8s.io/kubernetes/pkg/apiserver/testing"
"k8s.io/kubernetes/pkg/fields"
@ -424,3 +428,101 @@ func TestWatchHTTPTimeout(t *testing.T) {
t.Errorf("Unexpected non-error")
}
}
const benchmarkSeed = 100
func benchmarkItems() []api.Pod {
apiObjectFuzzer := apitesting.FuzzerFor(nil, api.SchemeGroupVersion, rand.NewSource(benchmarkSeed))
items := make([]api.Pod, 3)
for i := range items {
apiObjectFuzzer.Fuzz(&items[i])
}
return items
}
// BenchmarkWatchHTTP measures the cost of serving a watch.
func BenchmarkWatchHTTP(b *testing.B) {
items := benchmarkItems()
simpleStorage := &SimpleRESTStorage{}
handler := handle(map[string]rest.Storage{"simples": simpleStorage})
server := httptest.NewServer(handler)
defer server.Close()
client := http.Client{}
dest, _ := url.Parse(server.URL)
dest.Path = "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/watch/simples"
dest.RawQuery = ""
request, err := http.NewRequest("GET", dest.String(), nil)
if err != nil {
b.Fatalf("unexpected error: %v", err)
}
response, err := client.Do(request)
if err != nil {
b.Fatalf("unexpected error: %v", err)
}
if response.StatusCode != http.StatusOK {
b.Fatalf("Unexpected response %#v", response)
}
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer response.Body.Close()
if _, err := io.Copy(ioutil.Discard, response.Body); err != nil {
b.Fatal(err)
}
wg.Done()
}()
actions := []watch.EventType{watch.Added, watch.Modified, watch.Deleted}
b.ResetTimer()
for i := 0; i < b.N; i++ {
simpleStorage.fakeWatch.Action(actions[i%len(actions)], &items[i%len(items)])
}
simpleStorage.fakeWatch.Stop()
wg.Wait()
b.StopTimer()
}
// BenchmarkWatchWebsocket measures the cost of serving a watch.
func BenchmarkWatchWebsocket(b *testing.B) {
items := benchmarkItems()
simpleStorage := &SimpleRESTStorage{}
handler := handle(map[string]rest.Storage{"simples": simpleStorage})
server := httptest.NewServer(handler)
defer server.Close()
dest, _ := url.Parse(server.URL)
dest.Scheme = "ws" // Required by websocket, though the server never sees it.
dest.Path = "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/watch/simples"
dest.RawQuery = ""
ws, err := websocket.Dial(dest.String(), "", "http://localhost")
if err != nil {
b.Fatalf("unexpected error: %v", err)
}
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer ws.Close()
if _, err := io.Copy(ioutil.Discard, ws); err != nil {
b.Fatal(err)
}
wg.Done()
}()
actions := []watch.EventType{watch.Added, watch.Modified, watch.Deleted}
b.ResetTimer()
for i := 0; i < b.N; i++ {
simpleStorage.fakeWatch.Action(actions[i%len(actions)], &items[i%len(items)])
}
simpleStorage.fakeWatch.Stop()
wg.Wait()
b.StopTimer()
}