Skip to content

Commit 3c6cebb

Browse files
authored
Add TTFB to all APIs and enable for responses without body (minio#20479)
Add TTFB for all requests in metrics-v3 in addition to the existing GetObject. Also for the requests that do not return a body in the response, calculate TTFB as the HTTP status code and the headers are sent.
1 parent f6f0807 commit 3c6cebb

File tree

5 files changed

+22
-10
lines changed

5 files changed

+22
-10
lines changed

cmd/http-stats.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func (bh *bucketHTTPStats) updateHTTPStats(bucket, api string, w *xhttp.Response
133133
bucketHTTPRequestsDuration.With(prometheus.Labels{
134134
"api": api,
135135
"bucket": bucket,
136-
}).Observe(w.TimeToFirstByte.Seconds())
136+
}).Observe(w.TTFB().Seconds())
137137
}
138138

139139
bh.Lock()
@@ -433,7 +433,7 @@ func (st *HTTPStats) updateStats(api string, w *xhttp.ResponseRecorder) {
433433
st.totalS3Requests.Inc(api)
434434

435435
// Increment the prometheus http request response histogram with appropriate label
436-
httpRequestsDuration.With(prometheus.Labels{"api": api}).Observe(w.TimeToFirstByte.Seconds())
436+
httpRequestsDuration.With(prometheus.Labels{"api": api}).Observe(w.TTFB().Seconds())
437437

438438
code := w.StatusCode
439439

cmd/http-tracer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func httpTracerMiddleware(h http.Handler) http.Handler {
164164
Latency: reqEndTime.Sub(respRecorder.StartTime),
165165
InputBytes: inputBytes,
166166
OutputBytes: respRecorder.Size(),
167-
TimeToFirstByte: respRecorder.TimeToFirstByte,
167+
TimeToFirstByte: respRecorder.TTFB(),
168168
},
169169
},
170170
}

cmd/metrics-v3-api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func loadAPIRequestsHTTPMetrics(ctx context.Context, m MetricValues, _ *metricsC
129129
// This is a `MetricsLoaderFn`.
130130
func loadAPIRequestsTTFBMetrics(ctx context.Context, m MetricValues, _ *metricsCache) error {
131131
renameLabels := map[string]string{"api": "name"}
132-
labelsFilter := map[string]set.StringSet{"api": set.CreateStringSet("GetObject")}
132+
labelsFilter := map[string]set.StringSet{}
133133
m.SetHistogram(apiRequestsTTFBSecondsDistribution, httpRequestsDuration, labelsFilter, renameLabels, nil,
134134
"type", "s3")
135135
return nil
@@ -217,7 +217,7 @@ func loadBucketAPIHTTPMetrics(ctx context.Context, m MetricValues, _ *metricsCac
217217
// This is a `MetricsLoaderFn`.
218218
func loadBucketAPITTFBMetrics(ctx context.Context, m MetricValues, _ *metricsCache, buckets []string) error {
219219
renameLabels := map[string]string{"api": "name"}
220-
labelsFilter := map[string]set.StringSet{"api": set.CreateStringSet("GetObject")}
220+
labelsFilter := map[string]set.StringSet{}
221221
m.SetHistogram(apiRequestsTTFBSecondsDistribution, bucketHTTPRequestsDuration, labelsFilter, renameLabels,
222222
buckets, "type", "s3")
223223
return nil

internal/http/response-recorder.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ type ResponseRecorder struct {
4141
// Log body of all responses
4242
LogAllBody bool
4343

44-
TimeToFirstByte time.Duration
45-
StartTime time.Time
44+
ttfbHeader time.Duration
45+
ttfbBody time.Duration
46+
47+
StartTime time.Time
4648
// number of bytes written
4749
bytesWritten int
4850
// number of bytes of response headers written
@@ -63,6 +65,15 @@ func (lrw *ResponseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) {
6365
return hj.Hijack()
6466
}
6567

68+
// TTFB of the request - this function needs to be called
69+
// when the request is finished to provide accurate data
70+
func (lrw *ResponseRecorder) TTFB() time.Duration {
71+
if lrw.ttfbBody != 0 {
72+
return lrw.ttfbBody
73+
}
74+
return lrw.ttfbHeader
75+
}
76+
6677
// NewResponseRecorder - returns a wrapped response writer to trap
6778
// http status codes for auditing purposes.
6879
func NewResponseRecorder(w http.ResponseWriter) *ResponseRecorder {
@@ -97,8 +108,8 @@ func (lrw *ResponseRecorder) Write(p []byte) (int, error) {
97108
}
98109
n, err := lrw.ResponseWriter.Write(p)
99110
lrw.bytesWritten += n
100-
if lrw.TimeToFirstByte == 0 {
101-
lrw.TimeToFirstByte = time.Now().UTC().Sub(lrw.StartTime)
111+
if lrw.ttfbBody == 0 {
112+
lrw.ttfbBody = time.Now().UTC().Sub(lrw.StartTime)
102113
}
103114

104115
if (lrw.LogErrBody && lrw.StatusCode >= http.StatusBadRequest) || lrw.LogAllBody {
@@ -159,6 +170,7 @@ func (lrw *ResponseRecorder) Body() []byte {
159170
// WriteHeader - writes http status code
160171
func (lrw *ResponseRecorder) WriteHeader(code int) {
161172
if !lrw.headersLogged {
173+
lrw.ttfbHeader = time.Now().UTC().Sub(lrw.StartTime)
162174
lrw.StatusCode = code
163175
lrw.writeHeaders(&lrw.headers, code, lrw.ResponseWriter.Header())
164176
lrw.headersLogged = true

internal/logger/audit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func AuditLog(ctx context.Context, w http.ResponseWriter, r *http.Request, reqCl
100100
outputBytes = int64(tc.ResponseRecorder.Size())
101101
headerBytes = int64(tc.ResponseRecorder.HeaderSize())
102102
timeToResponse = time.Now().UTC().Sub(tc.ResponseRecorder.StartTime)
103-
timeToFirstByte = tc.ResponseRecorder.TimeToFirstByte
103+
timeToFirstByte = tc.ResponseRecorder.TTFB()
104104
}
105105

106106
entry.AccessKey = reqInfo.Cred.AccessKey

0 commit comments

Comments
 (0)