From 27a8aa66d9eb0f6403da982d6d750b32a0ebe5a6 Mon Sep 17 00:00:00 2001 From: jonaustin09 Date: Mon, 20 Nov 2023 14:28:02 -0500 Subject: [PATCH] feat: Added s3 proxy comparison to upload, download and throughput benchmark tests --- cmd/versitygw/test.go | 97 ++++++++++++++++++++++++++++++++++++++----- integration/bench.go | 26 ++++++------ 2 files changed, 99 insertions(+), 24 deletions(-) diff --git a/cmd/versitygw/test.go b/cmd/versitygw/test.go index 51a86b25..2bbabe51 100644 --- a/cmd/versitygw/test.go +++ b/cmd/versitygw/test.go @@ -2,6 +2,9 @@ package main import ( "fmt" + "math" + "os" + "text/tabwriter" "github.com/urfave/cli/v2" "github.com/versity/versitygw/integration" @@ -13,6 +16,7 @@ var ( endpoint string prefix string dstBucket string + proxyURL string partSize int64 objSize int64 concurrency int @@ -118,6 +122,7 @@ func initTestCommands() []*cli.Command { Name: "bucket", Usage: "Destination bucket name to read/write data", Destination: &dstBucket, + Required: true, }, &cli.Int64Flag{ Name: "partSize", @@ -143,6 +148,11 @@ func initTestCommands() []*cli.Command { Value: false, Destination: &checksumDisable, }, + &cli.StringFlag{ + Name: "proxy-url", + Usage: "S3 proxy server url to compare", + Destination: &proxyURL, + }, }, Action: func(ctx *cli.Context) error { if upload && download { @@ -152,10 +162,6 @@ func initTestCommands() []*cli.Command { return fmt.Errorf("must specify one of upload or download") } - if dstBucket == "" { - return fmt.Errorf("must specify bucket") - } - opts := []integration.Option{ integration.WithAccess(awsID), integration.WithSecret(awsSecret), @@ -177,9 +183,47 @@ func initTestCommands() []*cli.Command { s3conf := integration.NewS3Conf(opts...) if upload { - return integration.TestUpload(s3conf, files, objSize, dstBucket, prefix) + if proxyURL == "" { + integration.TestUpload(s3conf, files, objSize, dstBucket, prefix) + return nil + } else { + size, elapsed, err := integration.TestUpload(s3conf, files, objSize, dstBucket, prefix) + opts = append(opts, integration.WithEndpoint(proxyURL)) + proxyS3Conf := integration.NewS3Conf(opts...) + proxySize, proxyElapsed, proxyErr := integration.TestUpload(proxyS3Conf, files, objSize, dstBucket, prefix) + if err != nil || proxyErr != nil { + return nil + } + + printProxyResultsTable([][4]string{ + {" # ", "Total Size", "Time Taken", "Speed(MB/S)"}, + {"---------", "----------", "----------", "-----------"}, + {"S3 Server", fmt.Sprint(size), fmt.Sprintf("%v", elapsed), fmt.Sprint(int(math.Ceil(float64(size)/elapsed.Seconds()) / 1048576))}, + {"S3 Proxy", fmt.Sprint(proxySize), fmt.Sprintf("%v", proxyElapsed), fmt.Sprint(int(math.Ceil(float64(proxySize)/proxyElapsed.Seconds()) / 1048576))}, + }) + return nil + } } else { - return integration.TestDownload(s3conf, files, objSize, dstBucket, prefix) + if proxyURL == "" { + integration.TestDownload(s3conf, files, objSize, dstBucket, prefix) + return nil + } else { + size, elapsed, err := integration.TestDownload(s3conf, files, objSize, dstBucket, prefix) + opts = append(opts, integration.WithEndpoint(proxyURL)) + proxyS3Conf := integration.NewS3Conf(opts...) + proxySize, proxyElapsed, proxyErr := integration.TestDownload(proxyS3Conf, files, objSize, dstBucket, prefix) + if err != nil || proxyErr != nil { + return nil + } + + printProxyResultsTable([][4]string{ + {" # ", "Total Size", "Time Taken", "Speed(MB/S)"}, + {"---------", "----------", "----------", "-----------"}, + {"S3 server", fmt.Sprint(size), fmt.Sprintf("%v", elapsed), fmt.Sprint(int(math.Ceil(float64(size)/elapsed.Seconds()) / 1048576))}, + {"S3 proxy", fmt.Sprint(proxySize), fmt.Sprintf("%v", proxyElapsed), fmt.Sprint(int(math.Ceil(float64(proxySize)/proxyElapsed.Seconds()) / 1048576))}, + }) + return nil + } } }, }, @@ -211,12 +255,13 @@ func initTestCommands() []*cli.Command { Value: false, Destination: &checksumDisable, }, + &cli.StringFlag{ + Name: "proxy-url", + Usage: "S3 proxy server url to compare", + Destination: &proxyURL, + }, }, Action: func(ctx *cli.Context) error { - if dstBucket == "" { - return fmt.Errorf("must specify the destination bucket") - } - opts := []integration.Option{ integration.WithAccess(awsID), integration.WithSecret(awsSecret), @@ -233,7 +278,27 @@ func initTestCommands() []*cli.Command { s3conf := integration.NewS3Conf(opts...) - return integration.TestReqPerSec(s3conf, totalReqs, dstBucket) + if proxyURL == "" { + _, _, err := integration.TestReqPerSec(s3conf, totalReqs, dstBucket) + return err + } else { + elapsed, rps, err := integration.TestReqPerSec(s3conf, totalReqs, dstBucket) + opts = append(opts, integration.WithEndpoint(proxyURL)) + s3proxy := integration.NewS3Conf(opts...) + proxyElapsed, proxyRPS, proxyErr := integration.TestReqPerSec(s3proxy, totalReqs, dstBucket) + if err != nil || proxyErr != nil { + return nil + } + + printProxyResultsTable([][4]string{ + {" # ", "Total Requests", "Time Taken", "Requests Per Second(Req/Sec)"}, + {"---------", "--------------", "----------", "----------------------------"}, + {"S3 Server", fmt.Sprint(totalReqs), fmt.Sprintf("%v", elapsed), fmt.Sprint(rps)}, + {"S3 Proxy", fmt.Sprint(totalReqs), fmt.Sprintf("%v", proxyElapsed), fmt.Sprint(proxyRPS)}, + }) + + return nil + } }, }, } @@ -264,3 +329,13 @@ func getAction(tf testFunc) func(*cli.Context) error { return nil } } + +func printProxyResultsTable(stats [][4]string) { + w := new(tabwriter.Writer) + w.Init(os.Stdout, minwidth, tabwidth, padding, padchar, flags) + for _, elem := range stats { + fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", elem[0], elem[1], elem[2], elem[3]) + } + fmt.Fprintln(w) + w.Flush() +} diff --git a/integration/bench.go b/integration/bench.go index f9f86378..19b16cb8 100644 --- a/integration/bench.go +++ b/integration/bench.go @@ -17,16 +17,16 @@ type prefResult struct { err error } -func TestUpload(s *S3Conf, files int, objSize int64, bucket, prefix string) error { +func TestUpload(s *S3Conf, files int, objSize int64, bucket, prefix string) (size int64, elapsed time.Duration, err error) { var sg sync.WaitGroup results := make([]prefResult, files) start := time.Now() if objSize == 0 { - return fmt.Errorf("must specify object size for upload") + return 0, time.Since(start), fmt.Errorf("must specify object size for upload") } if objSize > (int64(10000) * s.PartSize) { - return fmt.Errorf("object size can not exceed 10000 * chunksize") + return 0, time.Since(start), fmt.Errorf("object size can not exceed 10000 * chunksize") } runF("performance test: upload objects") @@ -45,13 +45,13 @@ func TestUpload(s *S3Conf, files int, objSize int64, bucket, prefix string) erro }(i) } sg.Wait() - elapsed := time.Since(start) + elapsed = time.Since(start) var tot int64 for i, res := range results { if res.err != nil { failF("%v: %v\n", i, res.err) - break + return 0, time.Since(start), res.err } tot += res.size fmt.Printf("%v: %v in %v (%v MB/s)\n", @@ -63,10 +63,10 @@ func TestUpload(s *S3Conf, files int, objSize int64, bucket, prefix string) erro passF("run upload: %v in %v (%v MB/s)\n", tot, elapsed, int(math.Ceil(float64(tot)/elapsed.Seconds())/1048576)) - return nil + return tot, time.Since(start), nil } -func TestDownload(s *S3Conf, files int, objSize int64, bucket, prefix string) error { +func TestDownload(s *S3Conf, files int, objSize int64, bucket, prefix string) (size int64, elapsed time.Duration, err error) { var sg sync.WaitGroup results := make([]prefResult, files) start := time.Now() @@ -86,13 +86,13 @@ func TestDownload(s *S3Conf, files int, objSize int64, bucket, prefix string) er }(i) } sg.Wait() - elapsed := time.Since(start) + elapsed = time.Since(start) var tot int64 for i, res := range results { if res.err != nil { failF("%v: %v\n", i, res.err) - break + return 0, elapsed, err } tot += res.size fmt.Printf("%v: %v in %v (%v MB/s)\n", @@ -104,10 +104,10 @@ func TestDownload(s *S3Conf, files int, objSize int64, bucket, prefix string) er passF("run download: %v in %v (%v MB/s)\n", tot, elapsed, int(math.Ceil(float64(tot)/elapsed.Seconds())/1048576)) - return nil + return tot, elapsed, nil } -func TestReqPerSec(s *S3Conf, totalReqs int, bucket string) error { +func TestReqPerSec(s *S3Conf, totalReqs int, bucket string) (time.Duration, int, error) { client := s3.NewFromConfig(s.Config()) var wg sync.WaitGroup var resErr error @@ -132,11 +132,11 @@ func TestReqPerSec(s *S3Conf, totalReqs int, bucket string) error { wg.Wait() if resErr != nil { failF("performance test failed with error: %w", resErr) - return nil + return time.Since(startTime), 0, resErr } elapsedTime := time.Since(startTime) rps := int(float64(totalReqs) / elapsedTime.Seconds()) passF("Success\nTotal Requests: %d,\nConcurrency Level: %d,\nTime Taken: %s,\nRequests Per Second: %dreq/sec", totalReqs, s.Concurrency, elapsedTime, rps) - return nil + return elapsedTime, rps, nil }