mirror of
https://github.com/minio/mc.git
synced 2025-07-30 07:23:03 +03:00
support perf to execute all speed tests (#4158)
This commit will make `mc support perf <alias>` to execute all performance tests, networking, drive and object speed tests. perf will only have --duration and --verbose flags. The user can still test with 'drive', 'object', and 'net' but they will be hidden for now along with their flags.
This commit is contained in:
@ -146,12 +146,13 @@ func (m *speedTestUI) View() string {
|
|||||||
table.Render()
|
table.Render()
|
||||||
|
|
||||||
if m.quitting {
|
if m.quitting {
|
||||||
s.WriteString(fmt.Sprintf("\nSpeedtest: %s", m.result.String()))
|
s.WriteString("\n" + m.result.String())
|
||||||
if vstr := m.result.StringVerbose(); vstr != "" {
|
if vstr := m.result.StringVerbose(); vstr != "" {
|
||||||
s.WriteString(vstr)
|
s.WriteString(vstr + "\n")
|
||||||
} else {
|
} else {
|
||||||
s.WriteString("\n")
|
s.WriteString("\n")
|
||||||
}
|
}
|
||||||
|
s.WriteString("Objectperf: ✔\n")
|
||||||
}
|
}
|
||||||
} else if nres != nil {
|
} else if nres != nil {
|
||||||
table.SetHeader([]string{"Node", "RX", "TX", ""})
|
table.SetHeader([]string{"Node", "RX", "TX", ""})
|
||||||
|
@ -18,7 +18,15 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
humanize "github.com/dustin/go-humanize"
|
||||||
"github.com/minio/cli"
|
"github.com/minio/cli"
|
||||||
|
"github.com/minio/madmin-go"
|
||||||
|
"github.com/minio/mc/pkg/probe"
|
||||||
"github.com/minio/pkg/console"
|
"github.com/minio/pkg/console"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,3 +45,91 @@ func mainAdminSpeedtest(ctx *cli.Context) error {
|
|||||||
console.Infoln("Please use 'mc support perf'")
|
console.Infoln("Please use 'mc support perf'")
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
@ -18,16 +18,11 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
humanize "github.com/dustin/go-humanize"
|
humanize "github.com/dustin/go-humanize"
|
||||||
"github.com/minio/cli"
|
"github.com/minio/cli"
|
||||||
json "github.com/minio/colorjson"
|
json "github.com/minio/colorjson"
|
||||||
"github.com/minio/madmin-go"
|
|
||||||
"github.com/minio/mc/pkg/probe"
|
"github.com/minio/mc/pkg/probe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,39 +32,44 @@ var supportPerfFlags = []cli.Flag{
|
|||||||
Usage: "duration the entire perf tests are run",
|
Usage: "duration the entire perf tests are run",
|
||||||
Value: "10s",
|
Value: "10s",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "verbose, v",
|
||||||
|
Usage: "display per-server stats",
|
||||||
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "size",
|
Name: "size",
|
||||||
Usage: "size of the object used for uploads/downloads",
|
Usage: "size of the object used for uploads/downloads",
|
||||||
Value: "64MiB",
|
Value: "64MiB",
|
||||||
|
Hidden: true,
|
||||||
},
|
},
|
||||||
cli.IntFlag{
|
cli.IntFlag{
|
||||||
Name: "concurrent",
|
Name: "concurrent",
|
||||||
Usage: "number of concurrent requests per server",
|
Usage: "number of concurrent requests per server",
|
||||||
Value: 32,
|
Value: 32,
|
||||||
|
Hidden: true,
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "bucket",
|
Name: "bucket",
|
||||||
Usage: "provide a custom bucket name to use (NOTE: bucket must be created prior)",
|
Usage: "provide a custom bucket name to use (NOTE: bucket must be created prior)",
|
||||||
Hidden: true, // Hidden for now.
|
Hidden: true, // Hidden for now.
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "verbose, v",
|
|
||||||
Usage: "display per-server stats",
|
|
||||||
},
|
|
||||||
// Drive test specific flags.
|
// Drive test specific flags.
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "filesize",
|
Name: "filesize",
|
||||||
Usage: "total amount of data read/written to each drive",
|
Usage: "total amount of data read/written to each drive",
|
||||||
Value: "1GiB",
|
Value: "1GiB",
|
||||||
|
Hidden: true,
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "blocksize",
|
Name: "blocksize",
|
||||||
Usage: "read/write block size",
|
Usage: "read/write block size",
|
||||||
Value: "4MiB",
|
Value: "4MiB",
|
||||||
|
Hidden: true,
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "serial",
|
Name: "serial",
|
||||||
Usage: "run tests on drive(s) one-by-one",
|
Usage: "run tests on drive(s) one-by-one",
|
||||||
|
Hidden: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,30 +87,14 @@ var supportPerfCmd = cli.Command{
|
|||||||
USAGE:
|
USAGE:
|
||||||
{{.HelpName}} [COMMAND] [FLAGS] TARGET
|
{{.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:
|
FLAGS:
|
||||||
{{range .VisibleFlags}}{{.}}
|
{{range .VisibleFlags}}{{.}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
EXAMPLES:
|
EXAMPLES:
|
||||||
1. Run object speed measurement with autotuning the concurrency to obtain maximum throughput and IOPs:
|
1. Run performance tests on 'myminio' cluster: networking, drive speed and:
|
||||||
{{.Prompt}} {{.HelpName}} object myminio/
|
{{.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) {
|
switch len(args) {
|
||||||
case 1:
|
case 1:
|
||||||
// cannot use alias by the name 'drive' or 'net'
|
// 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)
|
cli.ShowCommandHelpAndExit(ctx, "perf", 1)
|
||||||
}
|
}
|
||||||
aliasedURL = args[0]
|
aliasedURL = args[0]
|
||||||
|
|
||||||
|
mainAdminSpeedTestNetperf(ctx, aliasedURL)
|
||||||
|
mainAdminSpeedTestDrive(ctx, aliasedURL)
|
||||||
|
mainAdminSpeedTestObject(ctx, aliasedURL)
|
||||||
case 2:
|
case 2:
|
||||||
|
aliasedURL := args[1]
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "drive":
|
case "drive":
|
||||||
aliasedURL = args[1]
|
|
||||||
return mainAdminSpeedTestDrive(ctx, aliasedURL)
|
return mainAdminSpeedTestDrive(ctx, aliasedURL)
|
||||||
case "object":
|
case "object":
|
||||||
aliasedURL = args[1]
|
return mainAdminSpeedTestObject(ctx, aliasedURL)
|
||||||
case "net":
|
case "net":
|
||||||
aliasedURL = args[1]
|
|
||||||
return mainAdminSpeedTestNetperf(ctx, aliasedURL)
|
return mainAdminSpeedTestNetperf(ctx, aliasedURL)
|
||||||
default:
|
default:
|
||||||
cli.ShowCommandHelpAndExit(ctx, "perf", 1) // last argument is exit code
|
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
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user