diff --git a/cmd/speedtest-spinner.go b/cmd/speedtest-spinner.go index 812922c2..4ce7bb0e 100644 --- a/cmd/speedtest-spinner.go +++ b/cmd/speedtest-spinner.go @@ -146,12 +146,13 @@ func (m *speedTestUI) View() string { table.Render() if m.quitting { - s.WriteString(fmt.Sprintf("\nSpeedtest: %s", m.result.String())) + s.WriteString("\n" + m.result.String()) if vstr := m.result.StringVerbose(); vstr != "" { - s.WriteString(vstr) + s.WriteString(vstr + "\n") } else { s.WriteString("\n") } + s.WriteString("Objectperf: ✔\n") } } else if nres != nil { table.SetHeader([]string{"Node", "RX", "TX", ""}) diff --git a/cmd/support-perf-object.go b/cmd/support-perf-object.go index 0af9cb53..f8a6b347 100644 --- a/cmd/support-perf-object.go +++ b/cmd/support-perf-object.go @@ -18,7 +18,15 @@ package cmd import ( + "context" + "os" + "time" + + tea "github.com/charmbracelet/bubbletea" + humanize "github.com/dustin/go-humanize" "github.com/minio/cli" + "github.com/minio/madmin-go" + "github.com/minio/mc/pkg/probe" "github.com/minio/pkg/console" ) @@ -37,3 +45,91 @@ func mainAdminSpeedtest(ctx *cli.Context) error { console.Infoln("Please use 'mc support perf'") return nil } + +func mainAdminSpeedTestObject(ctx *cli.Context, aliasedURL string) error { + client, perr := newAdminClient(aliasedURL) + if perr != nil { + fatalIf(perr.Trace(aliasedURL), "Unable to initialize admin client.") + return nil + } + + ctxt, cancel := context.WithCancel(globalContext) + defer cancel() + + duration, e := time.ParseDuration(ctx.String("duration")) + if e != nil { + fatalIf(probe.NewError(e), "Unable to parse duration") + return nil + } + if duration <= 0 { + fatalIf(errInvalidArgument(), "duration cannot be 0 or negative") + return nil + } + size, e := humanize.ParseBytes(ctx.String("size")) + if e != nil { + fatalIf(probe.NewError(e), "Unable to parse object size") + return nil + } + if size < 0 { + fatalIf(errInvalidArgument(), "size is expected to be atleast 0 bytes") + return nil + } + concurrent := ctx.Int("concurrent") + if concurrent <= 0 { + fatalIf(errInvalidArgument(), "concurrency cannot be '0' or negative") + return nil + } + globalPerfTestVerbose = ctx.Bool("verbose") + + // Turn-off autotuning only when "concurrent" is specified + // in all other scenarios keep auto-tuning on. + autotune := !ctx.IsSet("concurrent") + + resultCh, err := client.Speedtest(ctxt, madmin.SpeedtestOpts{ + Size: int(size), + Duration: duration, + Concurrency: concurrent, + Autotune: autotune, + Bucket: ctx.String("bucket"), // This is a hidden flag. + }) + fatalIf(probe.NewError(err), "Failed to execute performance test") + + if globalJSON { + for result := range resultCh { + if result.Version == "" { + continue + } + printMsg(speedTestResult{ + result: &result, + }) + } + return nil + } + + done := make(chan struct{}) + + p := tea.NewProgram(initSpeedTestUI()) + go func() { + if e := p.Start(); e != nil { + os.Exit(1) + } + close(done) + }() + + go func() { + var result madmin.SpeedTestResult + for result = range resultCh { + p.Send(speedTestResult{ + result: &result, + }) + } + p.Send(speedTestResult{ + result: &result, + final: true, + }) + }() + + <-done + + return nil +} diff --git a/cmd/support-perf.go b/cmd/support-perf.go index a780e26f..bcd92dd4 100644 --- a/cmd/support-perf.go +++ b/cmd/support-perf.go @@ -18,16 +18,11 @@ package cmd import ( - "context" "fmt" - "os" - "time" - tea "github.com/charmbracelet/bubbletea" humanize "github.com/dustin/go-humanize" "github.com/minio/cli" json "github.com/minio/colorjson" - "github.com/minio/madmin-go" "github.com/minio/mc/pkg/probe" ) @@ -37,39 +32,44 @@ var supportPerfFlags = []cli.Flag{ Usage: "duration the entire perf tests are run", Value: "10s", }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "display per-server stats", + }, cli.StringFlag{ - Name: "size", - Usage: "size of the object used for uploads/downloads", - Value: "64MiB", + Name: "size", + Usage: "size of the object used for uploads/downloads", + Value: "64MiB", + Hidden: true, }, cli.IntFlag{ - Name: "concurrent", - Usage: "number of concurrent requests per server", - Value: 32, + Name: "concurrent", + Usage: "number of concurrent requests per server", + Value: 32, + Hidden: true, }, cli.StringFlag{ Name: "bucket", Usage: "provide a custom bucket name to use (NOTE: bucket must be created prior)", Hidden: true, // Hidden for now. }, - cli.BoolFlag{ - Name: "verbose, v", - Usage: "display per-server stats", - }, // Drive test specific flags. cli.StringFlag{ - Name: "filesize", - Usage: "total amount of data read/written to each drive", - Value: "1GiB", + Name: "filesize", + Usage: "total amount of data read/written to each drive", + Value: "1GiB", + Hidden: true, }, cli.StringFlag{ - Name: "blocksize", - Usage: "read/write block size", - Value: "4MiB", + Name: "blocksize", + Usage: "read/write block size", + Value: "4MiB", + Hidden: true, }, cli.BoolFlag{ - Name: "serial", - Usage: "run tests on drive(s) one-by-one", + Name: "serial", + Usage: "run tests on drive(s) one-by-one", + Hidden: true, }, } @@ -87,30 +87,14 @@ var supportPerfCmd = cli.Command{ USAGE: {{.HelpName}} [COMMAND] [FLAGS] TARGET -COMMAND: - drive measure speed of drive in a cluster - object measure speed of reading and writing object in a cluster - net measure network throughput of all nodes - FLAGS: {{range .VisibleFlags}}{{.}} {{end}} EXAMPLES: - 1. Run object speed measurement with autotuning the concurrency to obtain maximum throughput and IOPs: - {{.Prompt}} {{.HelpName}} object myminio/ + 1. Run performance tests on 'myminio' cluster: networking, drive speed and: + {{.Prompt}} {{.HelpName}} myminio/ - 2. Run object speed measurement for 20 seconds with object size of 128MiB with autotuning the concurrency to obtain maximum throughput: - {{.Prompt}} {{.HelpName}} object myminio/ --duration 20s --size 128MiB - - 3. Run drive speed measurements on all drive on all nodes (with default blockSize of 4MiB): - {{.Prompt}} {{.HelpName}} drive myminio/ - - 4. Run drive speed measurements with blocksize of 64KiB, and 2GiB of data read/written from each drive: - {{.Prompt}} {{.HelpName}} drive myminio/ --blocksize 64KiB --filesize 2GiB - - 5. Run network throughput test: - {{.Prompt}} {{.HelpName}} net myminio `, } @@ -165,19 +149,22 @@ func mainSupportPerf(ctx *cli.Context) error { switch len(args) { case 1: // cannot use alias by the name 'drive' or 'net' - if args[0] == "drive" || args[0] == "net" { + if args[0] == "drive" || args[0] == "net" || args[0] == "object" { cli.ShowCommandHelpAndExit(ctx, "perf", 1) } aliasedURL = args[0] + + mainAdminSpeedTestNetperf(ctx, aliasedURL) + mainAdminSpeedTestDrive(ctx, aliasedURL) + mainAdminSpeedTestObject(ctx, aliasedURL) case 2: + aliasedURL := args[1] switch args[0] { case "drive": - aliasedURL = args[1] return mainAdminSpeedTestDrive(ctx, aliasedURL) case "object": - aliasedURL = args[1] + return mainAdminSpeedTestObject(ctx, aliasedURL) case "net": - aliasedURL = args[1] return mainAdminSpeedTestNetperf(ctx, aliasedURL) default: cli.ShowCommandHelpAndExit(ctx, "perf", 1) // last argument is exit code @@ -186,89 +173,5 @@ func mainSupportPerf(ctx *cli.Context) error { cli.ShowCommandHelpAndExit(ctx, "perf", 1) // last argument is exit code } - client, perr := newAdminClient(aliasedURL) - if perr != nil { - fatalIf(perr.Trace(aliasedURL), "Unable to initialize admin client.") - return nil - } - - ctxt, cancel := context.WithCancel(globalContext) - defer cancel() - - duration, e := time.ParseDuration(ctx.String("duration")) - if e != nil { - fatalIf(probe.NewError(e), "Unable to parse duration") - return nil - } - if duration <= 0 { - fatalIf(errInvalidArgument(), "duration cannot be 0 or negative") - return nil - } - size, e := humanize.ParseBytes(ctx.String("size")) - if e != nil { - fatalIf(probe.NewError(e), "Unable to parse object size") - return nil - } - if size < 0 { - fatalIf(errInvalidArgument(), "size is expected to be atleast 0 bytes") - return nil - } - concurrent := ctx.Int("concurrent") - if concurrent <= 0 { - fatalIf(errInvalidArgument(), "concurrency cannot be '0' or negative") - return nil - } - globalPerfTestVerbose = ctx.Bool("verbose") - - // Turn-off autotuning only when "concurrent" is specified - // in all other scenarios keep auto-tuning on. - autotune := !ctx.IsSet("concurrent") - - resultCh, err := client.Speedtest(ctxt, madmin.SpeedtestOpts{ - Size: int(size), - Duration: duration, - Concurrency: concurrent, - Autotune: autotune, - Bucket: ctx.String("bucket"), // This is a hidden flag. - }) - fatalIf(probe.NewError(err), "Failed to execute performance test") - - if globalJSON { - for result := range resultCh { - if result.Version == "" { - continue - } - printMsg(speedTestResult{ - result: &result, - }) - } - return nil - } - - done := make(chan struct{}) - - p := tea.NewProgram(initSpeedTestUI()) - go func() { - if e := p.Start(); e != nil { - os.Exit(1) - } - close(done) - }() - - go func() { - var result madmin.SpeedTestResult - for result = range resultCh { - p.Send(speedTestResult{ - result: &result, - }) - } - p.Send(speedTestResult{ - result: &result, - final: true, - }) - }() - - <-done - return nil }