| // 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. |
| |
| // The following enables go generate to generate the doc.go file. |
| //go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . |
| |
| package main |
| |
| import ( |
| "flag" |
| "net" |
| "regexp" |
| "time" |
| |
| "v.io/v23" |
| "v.io/v23/context" |
| "v.io/v23/naming" |
| "v.io/v23/options" |
| "v.io/v23/verror" |
| "v.io/x/lib/cmdline" |
| "v.io/x/ref/lib/v23cmd" |
| _ "v.io/x/ref/runtime/factories/generic" |
| ) |
| |
| func init() { |
| cmdline.HideGlobalFlagsExcept(regexp.MustCompile(`^((rate)|(duration)|(reauthenticate))$`)) |
| } |
| |
| var ( |
| rate = flag.Float64("rate", 1, "Rate, in RPCs per second, to send to the test server") |
| duration = flag.Duration("duration", 10*time.Second, "Duration for sending test traffic and measuring latency") |
| reauth = flag.Bool("reauthenticate", false, "If true, establish a new authenticated connection for each RPC, simulating load from a distinct process") |
| |
| cmdMount = &cmdline.Command{ |
| Name: "mount", |
| Short: "Measure latency of the Mount RPC at a fixed request rate", |
| Long: ` |
| Repeatedly issues a Mount request (at --rate) and measures latency |
| `, |
| ArgsName: "<mountpoint> <ttl>", |
| ArgsLong: ` |
| <mountpoint> defines the name to be mounted |
| |
| <ttl> specfies the time-to-live of the mount point. For example: 5s for 5 |
| seconds, 1m for 1 minute etc. |
| Valid time units are "ms", "s", "m", "h". |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| if got, want := len(args), 2; got != want { |
| return env.UsageErrorf("mount: got %d arguments, want %d", got, want) |
| } |
| mountpoint := args[0] |
| ttl, err := time.ParseDuration(args[1]) |
| if err != nil { |
| return env.UsageErrorf("invalid TTL: %v", err) |
| } |
| // Make up a random server to mount |
| ep := naming.FormatEndpoint("tcp", "127.0.0.1:14141") |
| mount := func(ctx *context.T) (time.Duration, error) { |
| // Currently this is a simple call, but at some |
| // point should generate random test data - |
| // mountpoints at different depths and the like |
| start := time.Now() |
| if err := v23.GetClient(ctx).Call(ctx, mountpoint, "Mount", []interface{}{ep, uint32(ttl / time.Second), 0}, nil, options.NoResolve{}); err != nil { |
| return 0, err |
| } |
| return time.Since(start), nil |
| } |
| p, err := paramsFromFlags(ctx, mountpoint) |
| if err != nil { |
| return err |
| } |
| return run(mount, p) |
| }), |
| } |
| |
| cmdResolve = &cmdline.Command{ |
| Name: "resolve", |
| Short: "Measure latency of the Resolve RPC at a fixed request rate", |
| Long: ` |
| Repeatedly issues a Resolve request (at --rate) to a name and measures latency |
| `, |
| ArgsName: "<name>", |
| ArgsLong: ` |
| <name> the object name to resolve |
| `, |
| Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error { |
| if got, want := len(args), 1; got != want { |
| return env.UsageErrorf("resolve: got %d arguments, want %d", got, want) |
| } |
| name := args[0] |
| resolve := func(ctx *context.T) (time.Duration, error) { |
| var entry naming.MountEntry |
| start := time.Now() |
| if err := v23.GetClient(ctx).Call(ctx, name, "ResolveStep", nil, []interface{}{&entry}, options.NoResolve{}); err != nil && verror.ErrorID(err) != naming.ErrNoSuchName.ID { |
| // ErrNoSuchName is fine, it just means |
| // that the mounttable server did not |
| // find an entry in its tables. |
| return 0, err |
| } |
| return time.Since(start), nil |
| } |
| p, err := paramsFromFlags(ctx, name) |
| if err != nil { |
| return err |
| } |
| return run(resolve, p) |
| }), |
| } |
| ) |
| |
| func paramsFromFlags(ctx *context.T, objectName string) (params, error) { |
| // Measure network distance to objectName |
| const iters = 5 |
| addr, _ := naming.SplitAddressName(objectName) |
| if len(addr) == 0 { |
| addr, _ = naming.SplitAddressName(v23.GetNamespace(ctx).Roots()[0]) |
| } |
| ep, err := v23.NewEndpoint(addr) |
| if err != nil { |
| return params{}, err |
| } |
| // Assume TCP |
| var total time.Duration |
| for i := 0; i < iters; i++ { |
| start := time.Now() |
| conn, err := net.Dial("tcp", ep.Addr().String()) |
| if err != nil { |
| return params{}, err |
| } |
| total += time.Since(start) |
| conn.Close() |
| } |
| return params{ |
| Rate: *rate, |
| Duration: *duration, |
| NetworkDistance: time.Duration(total.Nanoseconds() / iters), |
| Context: ctx, |
| Reauthenticate: *reauth, |
| }, nil |
| } |
| |
| func main() { |
| root := &cmdline.Command{ |
| Name: "mtstress", |
| Short: "Tool to stress test a mounttable service by issuing a fixed rate of requests per second and measuring latency", |
| Children: []*cmdline.Command{cmdMount, cmdResolve}, |
| } |
| cmdline.Main(root) |
| } |