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

     1  //go:build integration && perftest
     2  // +build integration,perftest
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"crypto/tls"
     9  	"fmt"
    10  	"net/http/httptrace"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/aavshr/aws-sdk-go/aws/request"
    16  )
    17  
    18  type RequestTrace struct {
    19  	Operation string
    20  	ID        string
    21  
    22  	context.Context
    23  
    24  	start, finish time.Time
    25  
    26  	errs       Errors
    27  	attempts   []RequestAttempt
    28  	curAttempt RequestAttempt
    29  }
    30  
    31  func NewRequestTrace(ctx context.Context, op, id string) *RequestTrace {
    32  	rt := &RequestTrace{
    33  		Operation: op,
    34  		ID:        id,
    35  		start:     time.Now(),
    36  		attempts:  []RequestAttempt{},
    37  		curAttempt: RequestAttempt{
    38  			ID: id,
    39  		},
    40  	}
    41  
    42  	trace := &httptrace.ClientTrace{
    43  		GetConn:              rt.getConn,
    44  		GotConn:              rt.gotConn,
    45  		PutIdleConn:          rt.putIdleConn,
    46  		GotFirstResponseByte: rt.gotFirstResponseByte,
    47  		Got100Continue:       rt.got100Continue,
    48  		DNSStart:             rt.dnsStart,
    49  		DNSDone:              rt.dnsDone,
    50  		ConnectStart:         rt.connectStart,
    51  		ConnectDone:          rt.connectDone,
    52  		TLSHandshakeStart:    rt.tlsHandshakeStart,
    53  		TLSHandshakeDone:     rt.tlsHandshakeDone,
    54  		WroteHeaders:         rt.wroteHeaders,
    55  		Wait100Continue:      rt.wait100Continue,
    56  		WroteRequest:         rt.wroteRequest,
    57  	}
    58  
    59  	rt.Context = httptrace.WithClientTrace(ctx, trace)
    60  
    61  	return rt
    62  }
    63  
    64  func (rt *RequestTrace) AppendError(err error) {
    65  	rt.errs = append(rt.errs, err)
    66  }
    67  func (rt *RequestTrace) OnSendAttempt(r *request.Request) {
    68  	rt.curAttempt.SendStart = time.Now()
    69  }
    70  func (rt *RequestTrace) OnCompleteAttempt(r *request.Request) {
    71  	rt.curAttempt.Start = r.AttemptTime
    72  	rt.curAttempt.Finish = time.Now()
    73  	rt.curAttempt.Err = r.Error
    74  
    75  	if r.Error != nil {
    76  		rt.AppendError(r.Error)
    77  	}
    78  
    79  	rt.attempts = append(rt.attempts, rt.curAttempt)
    80  	rt.curAttempt = RequestAttempt{
    81  		ID:         rt.curAttempt.ID,
    82  		AttemptNum: rt.curAttempt.AttemptNum + 1,
    83  	}
    84  }
    85  func (rt *RequestTrace) OnComplete(r *request.Request) {
    86  	rt.finish = time.Now()
    87  	// Last attempt includes reading the response body
    88  	if len(rt.attempts) > 0 {
    89  		rt.attempts[len(rt.attempts)-1].Finish = rt.finish
    90  	}
    91  }
    92  
    93  func (rt *RequestTrace) Err() error {
    94  	if len(rt.errs) != 0 {
    95  		return rt.errs
    96  	}
    97  	return nil
    98  }
    99  func (rt *RequestTrace) TotalLatency() time.Duration {
   100  	return rt.finish.Sub(rt.start)
   101  }
   102  func (rt *RequestTrace) Attempts() []RequestAttempt {
   103  	return rt.attempts
   104  }
   105  func (rt *RequestTrace) Retries() int {
   106  	return len(rt.attempts) - 1
   107  }
   108  
   109  func (rt *RequestTrace) getConn(hostPort string) {}
   110  func (rt *RequestTrace) gotConn(info httptrace.GotConnInfo) {
   111  	rt.curAttempt.Reused = info.Reused
   112  }
   113  func (rt *RequestTrace) putIdleConn(err error) {}
   114  func (rt *RequestTrace) gotFirstResponseByte() {
   115  	rt.curAttempt.FirstResponseByte = time.Now()
   116  }
   117  func (rt *RequestTrace) got100Continue() {}
   118  func (rt *RequestTrace) dnsStart(info httptrace.DNSStartInfo) {
   119  	rt.curAttempt.DNSStart = time.Now()
   120  }
   121  func (rt *RequestTrace) dnsDone(info httptrace.DNSDoneInfo) {
   122  	rt.curAttempt.DNSDone = time.Now()
   123  }
   124  func (rt *RequestTrace) connectStart(network, addr string) {
   125  	rt.curAttempt.ConnectStart = time.Now()
   126  }
   127  func (rt *RequestTrace) connectDone(network, addr string, err error) {
   128  	rt.curAttempt.ConnectDone = time.Now()
   129  }
   130  func (rt *RequestTrace) tlsHandshakeStart() {
   131  	rt.curAttempt.TLSHandshakeStart = time.Now()
   132  }
   133  func (rt *RequestTrace) tlsHandshakeDone(state tls.ConnectionState, err error) {
   134  	rt.curAttempt.TLSHandshakeDone = time.Now()
   135  }
   136  func (rt *RequestTrace) wroteHeaders() {
   137  	rt.curAttempt.WroteHeaders = time.Now()
   138  }
   139  func (rt *RequestTrace) wait100Continue() {
   140  	rt.curAttempt.Read100Continue = time.Now()
   141  }
   142  func (rt *RequestTrace) wroteRequest(info httptrace.WroteRequestInfo) {
   143  	rt.curAttempt.RequestWritten = time.Now()
   144  }
   145  
   146  type RequestAttempt struct {
   147  	Start, Finish time.Time
   148  	SendStart     time.Time
   149  	Err           error
   150  
   151  	Reused     bool
   152  	ID         string
   153  	AttemptNum int
   154  
   155  	DNSStart, DNSDone                   time.Time
   156  	ConnectStart, ConnectDone           time.Time
   157  	TLSHandshakeStart, TLSHandshakeDone time.Time
   158  	WroteHeaders                        time.Time
   159  	RequestWritten                      time.Time
   160  	Read100Continue                     time.Time
   161  	FirstResponseByte                   time.Time
   162  }
   163  
   164  func (a RequestAttempt) String() string {
   165  	const sep = ", "
   166  
   167  	var w strings.Builder
   168  	w.WriteString(a.ID + "-" + strconv.Itoa(a.AttemptNum) + sep)
   169  	w.WriteString("Latency:" + durToMSString(a.Finish.Sub(a.Start)) + sep)
   170  	w.WriteString("SDKPreSend:" + durToMSString(a.SendStart.Sub(a.Start)) + sep)
   171  
   172  	writeStart := a.SendStart
   173  	fmt.Fprintf(&w, "ConnReused:%t"+sep, a.Reused)
   174  	if !a.Reused {
   175  		w.WriteString("DNS:" + durToMSString(a.DNSDone.Sub(a.DNSStart)) + sep)
   176  		w.WriteString("Connect:" + durToMSString(a.ConnectDone.Sub(a.ConnectStart)) + sep)
   177  		w.WriteString("TLS:" + durToMSString(a.TLSHandshakeDone.Sub(a.TLSHandshakeStart)) + sep)
   178  		writeStart = a.TLSHandshakeDone
   179  	}
   180  
   181  	writeHeader := a.WroteHeaders.Sub(writeStart)
   182  	w.WriteString("WriteHeader:" + durToMSString(writeHeader) + sep)
   183  	if !a.Read100Continue.IsZero() {
   184  		// With 100-continue
   185  		w.WriteString("Read100Cont:" + durToMSString(a.Read100Continue.Sub(a.WroteHeaders)) + sep)
   186  		w.WriteString("WritePayload:" + durToMSString(a.FirstResponseByte.Sub(a.RequestWritten)) + sep)
   187  
   188  		w.WriteString("RespRead:" + durToMSString(a.Finish.Sub(a.RequestWritten)) + sep)
   189  	} else {
   190  		// No 100-continue
   191  		w.WriteString("WritePayload:" + durToMSString(a.RequestWritten.Sub(a.WroteHeaders)) + sep)
   192  
   193  		if !a.FirstResponseByte.IsZero() {
   194  			w.WriteString("RespFirstByte:" + durToMSString(a.FirstResponseByte.Sub(a.RequestWritten)) + sep)
   195  			w.WriteString("RespRead:" + durToMSString(a.Finish.Sub(a.FirstResponseByte)) + sep)
   196  		}
   197  	}
   198  
   199  	return w.String()
   200  }
   201  
   202  func durToMSString(v time.Duration) string {
   203  	ms := float64(v) / float64(time.Millisecond)
   204  	return fmt.Sprintf("%0.6f", ms)
   205  }