blob: 9b7e5c64332b8e865f36fc750427c84316f188c4 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"encoding/json"
"fmt"
"io"
"math/rand"
"runtime"
"time"
"v.io/v23/context"
"v.io/x/lib/cmdline"
"v.io/x/ref/lib/v23cmd"
"v.io/x/ref/runtime/internal/rpc/stress"
"v.io/x/ref/runtime/internal/rpc/stress/internal"
)
var (
duration time.Duration
workers int
maxChunkCnt int
maxPayloadSize int
outFormat string
)
func init() {
cmdStressTest.Flags.DurationVar(&duration, "duration", 1*time.Minute, "duration of the test to run")
cmdStressTest.Flags.IntVar(&workers, "workers", 1, "number of test workers to run")
cmdStressTest.Flags.IntVar(&maxChunkCnt, "max-chunk-count", 1000, "maximum number of chunks to send per streaming RPC")
cmdStressTest.Flags.IntVar(&maxPayloadSize, "max-payload-size", 10000, "maximum size of payload in bytes")
cmdStressTest.Flags.StringVar(&outFormat, "format", "text", "Stats output format; either text or json")
cmdStressStats.Flags.StringVar(&outFormat, "format", "text", "Stats output format; either text or json")
}
var cmdStressTest = &cmdline.Command{
Runner: v23cmd.RunnerFunc(runStressTest),
Name: "stress",
Short: "Run stress test",
Long: "Run stress test",
ArgsName: "<server> ...",
ArgsLong: "<server> ... A list of servers to connect to.",
}
func runStressTest(ctx *context.T, env *cmdline.Env, args []string) error {
if len(args) == 0 {
return env.UsageErrorf("no server specified")
}
if outFormat != "text" && outFormat != "json" {
return env.UsageErrorf("invalid output format: %s\n", outFormat)
}
rand.Seed(time.Now().UnixNano())
fmt.Fprintf(env.Stdout, "starting stress test against %d server(s) using %d core(s)...\n", len(args), runtime.NumCPU())
fmt.Fprintf(env.Stdout, "workers: %d, maxChunkCnt: %d, maxPayloadSize: %d, duration: %v\n", workers, maxChunkCnt, maxPayloadSize, duration)
start := time.Now()
done := make(chan stress.SumStats)
for i := 0; i < workers; i++ {
go func() {
var stats stress.SumStats
timeout := time.After(duration)
done:
for {
server := args[rand.Intn(len(args))]
if rand.Intn(2) == 0 {
internal.CallSum(ctx, server, maxPayloadSize, &stats)
} else {
internal.CallSumStream(ctx, server, maxChunkCnt, maxPayloadSize, &stats)
}
select {
case <-timeout:
break done
default:
}
}
done <- stats
}()
}
var merged stress.SumStats
for i := 0; i < workers; i++ {
stats := <-done
merged.SumCount += stats.SumCount
merged.SumStreamCount += stats.SumStreamCount
merged.BytesRecv += stats.BytesRecv
merged.BytesSent += stats.BytesSent
}
elapsed := time.Since(start)
fmt.Printf("done after %v\n", elapsed)
return outSumStats(env.Stdout, outFormat, "client stats:", &merged)
}
var cmdStressStats = &cmdline.Command{
Runner: v23cmd.RunnerFunc(runStressStats),
Name: "stats",
Short: "Print out stress stats of servers",
Long: "Print out stress stats of servers",
ArgsName: "<server> ...",
ArgsLong: "<server> ... A list of servers to connect to.",
}
func runStressStats(ctx *context.T, env *cmdline.Env, args []string) error {
if len(args) == 0 {
return env.UsageErrorf("no server specified")
}
if outFormat != "text" && outFormat != "json" {
return env.UsageErrorf("invalid output format: %s\n", outFormat)
}
for _, server := range args {
stats, err := stress.StressClient(server).GetSumStats(ctx)
if err != nil {
return err
}
title := fmt.Sprintf("server stats(%s):", server)
if err := outSumStats(env.Stdout, outFormat, title, &stats); err != nil {
return err
}
}
return nil
}
func outSumStats(w io.Writer, format, title string, stats *stress.SumStats) error {
switch format {
case "text":
fmt.Fprintf(w, "%s\n", title)
fmt.Fprintf(w, "\tnumber of non-streaming RPCs:\t%d\n", stats.SumCount)
fmt.Fprintf(w, "\tnumber of streaming RPCs:\t%d\n", stats.SumStreamCount)
fmt.Fprintf(w, "\tnumber of bytes received:\t%d\n", stats.BytesRecv)
fmt.Fprintf(w, "\tnumber of bytes sent:\t\t%d\n", stats.BytesSent)
case "json":
b, err := json.Marshal(stats)
if err != nil {
return err
}
fmt.Fprintf(w, "%s%s\n", title, b)
default:
return fmt.Errorf("invalid output format: %s\n", format)
}
return nil
}