github.com/aavshr/aws-sdk-go@v1.41.3/awstesting/integration/performance/s3UploadManager/main.go (about)

     1  //go:build integration && perftest
     2  // +build integration,perftest
     3  
     4  package main
     5  
     6  import (
     7  	"flag"
     8  	"log"
     9  	"os"
    10  	"path/filepath"
    11  	"strconv"
    12  	"time"
    13  
    14  	"github.com/aavshr/aws-sdk-go/aws"
    15  	"github.com/aavshr/aws-sdk-go/aws/request"
    16  	"github.com/aavshr/aws-sdk-go/aws/session"
    17  	"github.com/aavshr/aws-sdk-go/awstesting/integration"
    18  	"github.com/aavshr/aws-sdk-go/service/s3"
    19  	"github.com/aavshr/aws-sdk-go/service/s3/s3manager"
    20  )
    21  
    22  var config Config
    23  
    24  func main() {
    25  	parseCommandLine()
    26  
    27  	log.SetOutput(os.Stderr)
    28  
    29  	var (
    30  		file *os.File
    31  		err  error
    32  	)
    33  
    34  	if config.Filename != "" {
    35  		file, err = os.Open(config.Filename)
    36  		if err != nil {
    37  			log.Fatalf("failed to open file: %v", err)
    38  		}
    39  	} else {
    40  		file, err = integration.CreateFileOfSize(config.TempDir, config.Size)
    41  		if err != nil {
    42  			log.Fatalf("failed to create file: %v", err)
    43  		}
    44  		defer os.Remove(file.Name())
    45  	}
    46  
    47  	defer file.Close()
    48  
    49  	traces := make(chan *RequestTrace, config.SDK.Concurrency)
    50  	requestTracer := uploadRequestTracer(traces)
    51  	uploader := newUploader(config.Client, config.SDK, requestTracer)
    52  
    53  	metricReportDone := make(chan struct{})
    54  	go func() {
    55  		defer close(metricReportDone)
    56  		metrics := map[string]*RequestTrace{}
    57  		for trace := range traces {
    58  			curTrace, ok := metrics[trace.Operation]
    59  			if !ok {
    60  				curTrace = trace
    61  			} else {
    62  				curTrace.attempts = append(curTrace.attempts, trace.attempts...)
    63  				if len(trace.errs) != 0 {
    64  					curTrace.errs = append(curTrace.errs, trace.errs...)
    65  				}
    66  				curTrace.finish = trace.finish
    67  			}
    68  
    69  			metrics[trace.Operation] = curTrace
    70  		}
    71  
    72  		for _, name := range []string{
    73  			"CreateMultipartUpload",
    74  			"CompleteMultipartUpload",
    75  			"UploadPart",
    76  			"PutObject",
    77  		} {
    78  			if trace, ok := metrics[name]; ok {
    79  				printAttempts(name, trace, config.LogVerbose)
    80  			}
    81  		}
    82  	}()
    83  
    84  	log.Println("starting upload...")
    85  	start := time.Now()
    86  	_, err = uploader.Upload(&s3manager.UploadInput{
    87  		Bucket: &config.Bucket,
    88  		Key:    aws.String(filepath.Base(file.Name())),
    89  		Body:   file,
    90  	})
    91  	if err != nil {
    92  		log.Fatalf("failed to upload object, %v", err)
    93  	}
    94  	close(traces)
    95  
    96  	fileInfo, _ := file.Stat()
    97  	size := fileInfo.Size()
    98  	dur := time.Since(start)
    99  	log.Printf("Upload finished, Size: %d, Dur: %s, Throughput: %.5f GB/s",
   100  		size, dur, (float64(size)/(float64(dur)/float64(time.Second)))/float64(1e9),
   101  	)
   102  
   103  	<-metricReportDone
   104  }
   105  
   106  func parseCommandLine() {
   107  	config.SetupFlags("", flag.CommandLine)
   108  
   109  	if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
   110  		flag.CommandLine.PrintDefaults()
   111  		log.Fatalf("failed to parse CLI commands")
   112  	}
   113  	if err := config.Validate(); err != nil {
   114  		flag.CommandLine.PrintDefaults()
   115  		log.Fatalf("invalid arguments: %v", err)
   116  	}
   117  }
   118  
   119  func printAttempts(op string, trace *RequestTrace, verbose bool) {
   120  	if !verbose {
   121  		return
   122  	}
   123  
   124  	log.Printf("%s: latency:%s requests:%d errors:%d",
   125  		op,
   126  		trace.finish.Sub(trace.start),
   127  		len(trace.attempts),
   128  		len(trace.errs),
   129  	)
   130  
   131  	for _, a := range trace.attempts {
   132  		log.Printf("  * %s", a)
   133  	}
   134  	if err := trace.Err(); err != nil {
   135  		log.Printf("Operation Errors: %v", err)
   136  	}
   137  	log.Println()
   138  }
   139  
   140  func uploadRequestTracer(traces chan<- *RequestTrace) request.Option {
   141  	tracerOption := func(r *request.Request) {
   142  		id := "op"
   143  		if v, ok := r.Params.(*s3.UploadPartInput); ok {
   144  			id = strconv.FormatInt(*v.PartNumber, 10)
   145  		}
   146  		tracer := NewRequestTrace(r.Context(), r.Operation.Name, id)
   147  		r.SetContext(tracer)
   148  
   149  		r.Handlers.Send.PushFront(tracer.OnSendAttempt)
   150  		r.Handlers.CompleteAttempt.PushBack(tracer.OnCompleteAttempt)
   151  		r.Handlers.Complete.PushBack(tracer.OnComplete)
   152  		r.Handlers.Complete.PushBack(func(rr *request.Request) {
   153  			traces <- tracer
   154  		})
   155  	}
   156  
   157  	return tracerOption
   158  }
   159  
   160  func SetUnsignedPayload(r *request.Request) {
   161  	if r.Operation.Name != "UploadPart" && r.Operation.Name != "PutObject" {
   162  		return
   163  	}
   164  	r.HTTPRequest.Header.Set("X-Amz-Content-Sha256", "UNSIGNED-PAYLOAD")
   165  }
   166  
   167  func newUploader(clientConfig ClientConfig, sdkConfig SDKConfig, options ...request.Option) *s3manager.Uploader {
   168  	client := NewClient(clientConfig)
   169  
   170  	if sdkConfig.WithUnsignedPayload {
   171  		options = append(options, SetUnsignedPayload)
   172  	}
   173  
   174  	sess, err := session.NewSessionWithOptions(session.Options{
   175  		Config: aws.Config{
   176  			HTTPClient:                    client,
   177  			S3Disable100Continue:          aws.Bool(!sdkConfig.ExpectContinue),
   178  			S3DisableContentMD5Validation: aws.Bool(!sdkConfig.WithContentMD5),
   179  		},
   180  		SharedConfigState: session.SharedConfigEnable,
   181  	})
   182  	if err != nil {
   183  		log.Fatalf("failed to load session, %v", err)
   184  	}
   185  
   186  	uploader := s3manager.NewUploader(sess, func(u *s3manager.Uploader) {
   187  		u.PartSize = sdkConfig.PartSize
   188  		u.Concurrency = sdkConfig.Concurrency
   189  		u.BufferProvider = sdkConfig.BufferProvider
   190  
   191  		u.RequestOptions = append(u.RequestOptions, options...)
   192  	})
   193  
   194  	return uploader
   195  }
   196  
   197  func getUploadPartSize(fileSize, uploadPartSize int64) int64 {
   198  	partSize := uploadPartSize
   199  
   200  	if fileSize/partSize > s3manager.MaxUploadParts {
   201  		partSize = (fileSize / s3manager.MaxUploadParts) + 1
   202  	}
   203  
   204  	return partSize
   205  }