Skip to content

Commit

Permalink
feat: add integration tests for statistics and fix inconsistencies in…
Browse files Browse the repository at this point in the history
… CLI tools
  • Loading branch information
buraksezer committed Jun 8, 2021
1 parent 1ea2b4c commit 78f077b
Show file tree
Hide file tree
Showing 21 changed files with 793 additions and 541 deletions.
37 changes: 21 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ With Olric, you can instantly create a fast, scalable, shared pool of RAM across

See [Docker](#docker) and [Sample Code](#sample-code) sections to get started!

Current production version is [v0.3.7](https://github.com/buraksezer/olric/tree/7e13bd0c669b83f2b1ef1716ec8564b2e0098977)

## At a glance

* Designed to share some transient, approximate, fast-changing data between servers,
Expand Down Expand Up @@ -45,7 +47,7 @@ failure detection and simple anti-entropy services. So it can be used as an ordi
* [olricd](#olricd)
* [olric-cli](#olric-cli)
* [olric-stats](#olric-stats)
* [olric-load](#olric-load)
* [olric-benchmark](#olric-benchmark)
* [Usage](#usage)
* [Distributed Map](#distributed-map)
* [Put](#put)
Expand Down Expand Up @@ -167,7 +169,7 @@ Then, install olricd and its siblings:
go install -v ./cmd/*
```

Now you should access **olricd**, **olric-stats**, **olric-cli** and **olric-load** on your path. You can just run olricd
Now you should access **olricd**, **olric-stats**, **olric-cli** and **olric-benchmark** on your path. You can just run olricd
to start experimenting:

```
Expand Down Expand Up @@ -249,7 +251,7 @@ Get a shell to the running container:
kubectl exec -it olric-debug -- /bin/sh
```

Now you have a running Alpine Linux setup on Kubernetes. It includes `olric-cli`, `olric-load` and `olric-stats` commands.
Now you have a running Alpine Linux setup on Kubernetes. It includes `olric-cli`, `olric-benchmark` and `olric-stats` commands.

```bash
/go/src/github.com/buraksezer/olric # olric-cli -a olricd.default.svc.cluster.local:3320
Expand Down Expand Up @@ -366,8 +368,11 @@ In order to get more details about the options, call `olric-cli -h` in your shel

### olric-stats

olric-stats calls `Stats` command on a cluster member and prints the result. The returned data from the member includes the Go runtime
metrics and statistics from hosted primary and backup partitions.
olric-stats calls `Stats` command on a given cluster member and prints the result.
The results from the member also includes the Go runtime metrics and statistics from
hosted primary and backup partitions.

You should know that all the statistics are belonged to the current member.

In order to install `olric-stats`:

Expand All @@ -378,39 +383,39 @@ go get -u github.com/buraksezer/olric/cmd/olric-stats
Statistics about a partition:

```
olric-stats -p 69
olric-stats --partitions --id 69
PartID: 69
Owner: olric.node:3320
Previous Owners: not found
Backups: not found
DMap count: 1
DMaps:
Name: olric-load-test
Name: olric-benchmark-test
Length: 1374
Allocated: 1048576
Inuse: 47946
Garbage: 0
```

In order to get detailed statistics about the Go runtime, you should call `olric-stats -a <ADDRESS> -r`.

Without giving a partition number, it will print everything about the cluster and hosted primary/backup partitions.
In order to get detailed statistics about the Go runtime, you should call `olric-stats -a <ADDRESS> -r`.
In order to get more details about the command, call `olric-stats -h`.

### olric-load
See [stats/stats.go](stats/stats.go) file to get detailed information about the statistics.

### olric-benchmark

olric-load simulates running commands done by N clients at the same time sending M total queries. It measures response time.
olric-benchmark simulates running commands done by N clients at the same time sending M total queries. It measures response time.

In order to install `olric-load`:
In order to install `olric-benchmark`:

```bash
go get -u github.com/buraksezer/olric/cmd/olric-load
go get -u github.com/buraksezer/olric/cmd/olric-benchmark
```

The following command calls `Put` command for 1M keys on `127.0.0.1:3320` (it's default) and uses `msgpack` for serialization.

```
olric-load -a 192.168.1.3:3320 -s msgpack -k 1000000 -c put
olric-benchmark -a 192.168.1.3:3320 -s msgpack -r 1000000 -T put
### STATS FOR COMMAND: PUT ###
Serializer is msgpack
1000000 requests completed in 6.943316278s
Expand All @@ -431,7 +436,7 @@ Serializer is msgpack
144023.397460 requests per second
```

In order to get more details about the command, call `olric-load -h`.
In order to get more details about the command, call `olric-benchmark -h`.

## Usage

Expand Down
6 changes: 3 additions & 3 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (c *Client) request(req protocol.EncodeDecoder) (protocol.EncodeDecoder, er
}

// Stats exposes some useful metrics to monitor an Olric node.
func (c *Client) Stats(addr string, options ...statsOption) (stats.Stats, error) {
func (c *Client) Stats(addr string, options ...StatsOption) (stats.Stats, error) {
var extra protocol.StatsExtra
for _, opt := range options {
opt(&extra)
Expand All @@ -154,9 +154,9 @@ func (c *Client) Stats(addr string, options ...statsOption) (stats.Stats, error)
return s, nil
}

type statsOption func(*protocol.StatsExtra)
type StatsOption func(*protocol.StatsExtra)

func CollectRuntime() statsOption {
func CollectRuntime() StatsOption {
return func(extra *protocol.StatsExtra) {
extra.CollectRuntime = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package loader
package benchmark

import (
"fmt"
Expand All @@ -28,29 +28,32 @@ import (
_serializer "github.com/buraksezer/olric/serializer"
)

type Loader struct {
type Benchmark struct {
mu sync.RWMutex
responses []time.Duration
commands []string
numRequests int
numClients int
requests int
connections int
serializer string
client *client.Client
log *log.Logger
wg sync.WaitGroup
}

func New(addrs, timeout, serializer string,
numClients, keyCount int, logger *log.Logger) (*Loader, error) {
func New(address,
timeout,
serializer string,
conns, requests int,
logger *log.Logger) (*Benchmark, error) {
// Default serializer is Gob serializer, just set nil or use gob keyword to use it.
var s _serializer.Serializer
if serializer == "json" {
switch {
case serializer == "json":
s = _serializer.NewJSONSerializer()
} else if serializer == "msgpack" {
case serializer == "msgpack":
s = _serializer.NewMsgpackSerializer()
} else if serializer == "gob" {
case serializer == "gob":
s = _serializer.NewGobSerializer()
} else {
default:
return nil, fmt.Errorf("invalid serializer: %s", serializer)
}

Expand All @@ -59,44 +62,45 @@ func New(addrs, timeout, serializer string,
return nil, err
}
cc := &client.Config{
Servers: strings.Split(addrs, ","),
Servers: strings.Split(address, ","),
Serializer: s,
Client: &config.Client{
DialTimeout: dt,
MaxConn: numClients,
MaxConn: conns,
},
}

c, err := client.New(cc)
if err != nil {
return nil, err
}
l := &Loader{

return &Benchmark{
responses: []time.Duration{},
numRequests: keyCount,
numClients: numClients,
requests: requests,
connections: conns,
client: c,
serializer: serializer,
log: logger,
}
return l, nil
}, nil
}

func (l *Loader) stats(cmd string, elapsed time.Duration) {
l.mu.RLock()
defer l.mu.RUnlock()
func (b *Benchmark) stats(cmd string, elapsed time.Duration) {
b.mu.RLock()
defer b.mu.RUnlock()

l.log.Printf("### STATS FOR COMMAND: %s ###", strings.ToUpper(cmd))
l.log.Printf("Serializer is %s", l.serializer)
l.log.Printf("%d requests completed in %v", l.numRequests, elapsed)
l.log.Printf("%d parallel clients", l.numClients)
l.log.Printf("\n")
b.log.Printf("### STATS FOR COMMAND: %s ###", strings.ToUpper(cmd))
b.log.Printf("Serializer is %s", b.serializer)
b.log.Printf("%d requests completed in %v", b.requests, elapsed)
b.log.Printf("%d parallel clients", b.connections)
b.log.Printf("\n")

var limit time.Duration
var lastper float64
for {
limit += time.Millisecond
var hits, count int
for _, rtime := range l.responses {
for _, rtime := range b.responses {
if rtime < limit {
hits++
}
Expand All @@ -112,51 +116,51 @@ func (l *Loader) stats(cmd string, elapsed time.Duration) {
break
}
}
rps := float64(l.numRequests) / (float64(elapsed) / float64(time.Second))
l.log.Printf("\n%f requests per second\n", rps)
rps := float64(b.requests) / (float64(elapsed) / float64(time.Second))
b.log.Printf("\n%f requests per second\n", rps)
}

func (l *Loader) worker(cmd string, ch chan int) {
defer l.wg.Done()
func (b *Benchmark) worker(cmd string, ch chan int) {
defer b.wg.Done()

dm := l.client.NewDMap("olric-load-test")
dm := b.client.NewDMap("olric-benchmark-test")
for i := range ch {
now := time.Now()
switch {
case strings.ToLower(cmd) == "put":
if err := dm.Put(strconv.Itoa(i), i); err != nil {
l.log.Printf("[ERROR] Failed to call Put command for %d: %v", i, err)
b.log.Printf("[ERROR] Failed to call Put command for %d: %v", i, err)
}
case strings.ToLower(cmd) == "get":
_, err := dm.Get(strconv.Itoa(i))
if err != nil {
l.log.Printf("[ERROR] Failed to call Get command for %d: %v", i, err)
b.log.Printf("[ERROR] Failed to call Get command for %d: %v", i, err)
}
case strings.ToLower(cmd) == "delete":
err := dm.Delete(strconv.Itoa(i))
if err != nil {
l.log.Printf("[ERROR] Failed to call Delete command for %d: %v", i, err)
b.log.Printf("[ERROR] Failed to call Delete command for %d: %v", i, err)
}
case strings.ToLower(cmd) == "incr":
_, err := dm.Incr(strconv.Itoa(i), 1)
if err != nil {
l.log.Printf("[ERROR] Failed to call Incr command for %d: %v", i, err)
b.log.Printf("[ERROR] Failed to call Incr command for %d: %v", i, err)
}
case strings.ToLower(cmd) == "decr":
_, err := dm.Decr(strconv.Itoa(i), 1)
if err != nil {
l.log.Printf("[ERROR] Failed to call Decr command for %d: %v", i, err)
b.log.Printf("[ERROR] Failed to call Decr command for %d: %v", i, err)
}
}

response := time.Since(now)
l.mu.Lock()
l.responses = append(l.responses, response)
l.mu.Unlock()
b.mu.Lock()
b.responses = append(b.responses, response)
b.mu.Unlock()
}
}

func (l *Loader) Run(cmd string) error {
func (b *Benchmark) Run(cmd string) error {
if cmd == "" {
return fmt.Errorf("no command given")
}
Expand All @@ -172,19 +176,19 @@ func (l *Loader) Run(cmd string) error {

var elapsed time.Duration
ch := make(chan int)
for i := 0; i < l.numClients; i++ {
l.wg.Add(1)
go l.worker(cmd, ch)
for i := 0; i < b.connections; i++ {
b.wg.Add(1)
go b.worker(cmd, ch)
}

now := time.Now()
for i := 0; i < l.numRequests; i++ {
for i := 0; i < b.requests; i++ {
ch <- i
}
close(ch)
l.wg.Wait()
b.wg.Wait()

elapsed = time.Since(now)
l.stats(cmd, elapsed)
b.stats(cmd, elapsed)
return nil
}
Loading

0 comments on commit 78f077b

Please sign in to comment.