| // 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 |
| } |