mirror of https://github.com/k3s-io/k3s
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
parent
4f559b3af7
commit
443cafab90
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue