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 }