Skip to content

Commit f63f77a

Browse files
authored
Metrics: Record incoming request sizes (#4495)
1 parent 01daa2c commit f63f77a

File tree

10 files changed

+135
-4
lines changed

10 files changed

+135
-4
lines changed

endpoints/openrtb2/amp_auction.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
160160

161161
// There is no body for AMP requests, so we pass a nil body and ignore the return value.
162162
_, rejectErr := hookExecutor.ExecuteEntrypointStage(r, nilBody)
163-
reqWrapper, storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, errL := deps.parseAmpRequest(r)
163+
reqWrapper, storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, errL := deps.parseAmpRequest(r, labels)
164164
ao.Errors = append(ao.Errors, errL...)
165165
// Process reject after parsing amp request, so we can use reqWrapper.
166166
// There is no body for AMP requests, so we pass a nil body and ignore the return value.
@@ -503,9 +503,9 @@ func getExtBidResponse(
503503
// possible, it will return errors with messages that suggest improvements.
504504
//
505505
// If the errors list has at least one element, then no guarantees are made about the returned request.
506-
func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openrtb_ext.RequestWrapper, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, bidderImpReplaceImp stored_responses.BidderImpReplaceImpID, errs []error) {
506+
func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request, labels metrics.Labels) (req *openrtb_ext.RequestWrapper, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, bidderImpReplaceImp stored_responses.BidderImpReplaceImpID, errs []error) {
507507
// Load the stored request for the AMP ID.
508-
reqNormal, storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, e := deps.loadRequestJSONForAmp(httpRequest)
508+
reqNormal, storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, e := deps.loadRequestJSONForAmp(httpRequest, labels)
509509
if errs = append(errs, e...); errortypes.ContainsFatalError(errs) {
510510
return
511511
}
@@ -536,7 +536,7 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr
536536
}
537537

538538
// Load the stored OpenRTB request for an incoming AMP request, or return the errors found.
539-
func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req *openrtb2.BidRequest, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, bidderImpReplaceImp stored_responses.BidderImpReplaceImpID, errs []error) {
539+
func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request, labels metrics.Labels) (req *openrtb2.BidRequest, storedAuctionResponses stored_responses.ImpsWithBidResponses, storedBidResponses stored_responses.ImpBidderStoredResp, bidderImpReplaceImp stored_responses.BidderImpReplaceImpID, errs []error) {
540540
req = &openrtb2.BidRequest{}
541541
errs = nil
542542

@@ -559,6 +559,7 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req
559559

560560
// The fetched config becomes the entire OpenRTB request
561561
requestJSON := storedRequests[ampParams.StoredRequestID]
562+
labels.RequestSize = len(requestJSON)
562563
if err := jsonutil.UnmarshalValid(requestJSON, req); err != nil {
563564
errs = []error{err}
564565
return

endpoints/openrtb2/auction.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric
442442
errs = []error{err}
443443
return
444444
}
445+
labels.RequestSize = len(requestJson)
445446

446447
if limitedReqReader.N <= 0 {
447448
// Limited Reader returns 0 if the request was exactly at the max size or over the limit.

endpoints/openrtb2/video_auction.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
176176
handleError(&labels, w, []error{err}, &vo, &debugLog)
177177
return
178178
}
179+
labels.RequestSize = len(requestJson)
179180

180181
resolvedRequest := requestJson
181182
if debugLog.DebugEnabledOrOverridden {

metrics/config/metrics_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func TestMultiMetricsEngine(t *testing.T) {
5353
PubID: "test1",
5454
CookieFlag: metrics.CookieFlagYes,
5555
RequestStatus: metrics.RequestStatusOK,
56+
RequestSize: 1024,
5657
}
5758
apnLabels := metrics.AdapterLabels{
5859
Source: metrics.DemandWeb,
@@ -157,6 +158,9 @@ func TestMultiMetricsEngine(t *testing.T) {
157158
VerifyMetrics(t, "RequestStatuses.OpenRTB2.BadInput", goEngine.RequestStatuses[metrics.ReqTypeORTB2Web][metrics.RequestStatusBadInput].Count(), 0)
158159
VerifyMetrics(t, "RequestStatuses.OpenRTB2.BlockedApp", goEngine.RequestStatuses[metrics.ReqTypeORTB2Web][metrics.RequestStatusBlockedApp].Count(), 0)
159160

161+
VerifyMetrics(t, "RequestSizeByEndpoint.Auction", goEngine.RequestSizeByEndpoint[metrics.EndpointAuction].Count(), 5)
162+
VerifyMetrics(t, "RequestSizeByEndpoint.Auction", goEngine.RequestSizeByEndpoint[metrics.EndpointAuction].Max(), 1024)
163+
160164
VerifyMetrics(t, "ImpsTypeBanner", goEngine.ImpsTypeBanner.Count(), 5)
161165
VerifyMetrics(t, "ImpsTypeVideo", goEngine.ImpsTypeVideo.Count(), 3)
162166
VerifyMetrics(t, "ImpsTypeAudio", goEngine.ImpsTypeAudio.Count(), 5)

metrics/go_metrics.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type Metrics struct {
4141

4242
// Metrics for OpenRTB requests specifically
4343
RequestStatuses map[RequestType]map[RequestStatus]metrics.Meter
44+
RequestSizeByEndpoint map[EndpointType]metrics.Histogram
4445
AmpNoCookieMeter metrics.Meter
4546
CookieSyncMeter metrics.Meter
4647
CookieSyncStatusMeter map[CookieSyncStatus]metrics.Meter
@@ -159,6 +160,7 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []string, disabledMetr
159160
newMetrics := &Metrics{
160161
MetricsRegistry: registry,
161162
RequestStatuses: make(map[RequestType]map[RequestStatus]metrics.Meter),
163+
RequestSizeByEndpoint: make(map[EndpointType]metrics.Histogram),
162164
ConnectionCounter: metrics.NilCounter{},
163165
ConnectionAcceptErrorMeter: blankMeter,
164166
ConnectionCloseErrorMeter: blankMeter,
@@ -235,6 +237,12 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []string, disabledMetr
235237
}
236238
}
237239

240+
for _, t := range EndpointTypes() {
241+
if t != EndpointAmp {
242+
newMetrics.RequestSizeByEndpoint[t] = &metrics.NilHistogram{}
243+
}
244+
}
245+
238246
for _, c := range CacheResults() {
239247
newMetrics.StoredReqCacheMeter[c] = blankMeter
240248
newMetrics.StoredImpCacheMeter[c] = blankMeter
@@ -358,6 +366,10 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d
358366
}
359367
}
360368

369+
for _, endpoint := range EndpointTypes() {
370+
newMetrics.RequestSizeByEndpoint[endpoint] = metrics.GetOrRegisterHistogram("requests.size."+string(endpoint), registry, metrics.NewUniformSample(1024))
371+
}
372+
361373
for _, cacheRes := range CacheResults() {
362374
newMetrics.StoredReqCacheMeter[cacheRes] = metrics.GetOrRegisterMeter(fmt.Sprintf("stored_request_cache_%s", string(cacheRes)), registry)
363375
newMetrics.StoredImpCacheMeter[cacheRes] = metrics.GetOrRegisterMeter(fmt.Sprintf("stored_imp_cache_%s", string(cacheRes)), registry)
@@ -612,6 +624,11 @@ func (me *Metrics) RecordRequest(labels Labels) {
612624
}
613625
}
614626

627+
// Request size by endpoint
628+
if labels.RequestSize > 0 && labels.RType != ReqTypeAMP {
629+
me.RequestSizeByEndpoint[GetEndpointFromRequestType(labels.RType)].Update(int64(labels.RequestSize))
630+
}
631+
615632
// Handle the account metrics now.
616633
am := me.getAccountMetrics(labels.PubID)
617634
am.requestMeter.Mark(1)

metrics/go_metrics_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ func TestNewMetrics(t *testing.T) {
5757
ensureContains(t, registry, "requests.badinput.video", m.RequestStatuses[ReqTypeVideo][RequestStatusBadInput])
5858
ensureContains(t, registry, "requests.err.video", m.RequestStatuses[ReqTypeVideo][RequestStatusErr])
5959
ensureContains(t, registry, "requests.networkerr.video", m.RequestStatuses[ReqTypeVideo][RequestStatusNetworkErr])
60+
ensureContains(t, registry, "requests.size.auction", m.RequestSizeByEndpoint[EndpointAuction])
61+
ensureContains(t, registry, "requests.size.video", m.RequestSizeByEndpoint[EndpointVideo])
6062

6163
ensureContains(t, registry, "queued_requests.video.rejected", m.RequestsQueueTimer[ReqTypeVideo][false])
6264
ensureContains(t, registry, "queued_requests.video.accepted", m.RequestsQueueTimer[ReqTypeVideo][true])

metrics/metrics.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Labels struct {
1313
PubID string // exchange specific ID, so we cannot compile in values
1414
CookieFlag CookieFlag
1515
RequestStatus RequestStatus
16+
RequestSize int
1617
}
1718

1819
// AdapterLabels defines the labels that can be attached to the adapter metrics.
@@ -139,6 +140,8 @@ type DemandSource string
139140
// ImpMediaType : Media type described in the "imp" JSON object TODO is this still needed?
140141
type ImpMediaType string
141142

143+
type EndpointType string
144+
142145
// RequestType : Request type enumeration
143146
type RequestType string
144147

@@ -196,6 +199,40 @@ func RequestTypes() []RequestType {
196199
}
197200
}
198201

202+
// Endpoint type constants
203+
const (
204+
EndpointAuction EndpointType = "auction"
205+
EndpointVideo EndpointType = "video"
206+
EndpointAmp EndpointType = "amp"
207+
)
208+
209+
func EndpointTypes() []EndpointType {
210+
return []EndpointType{
211+
EndpointAuction,
212+
EndpointVideo,
213+
EndpointAmp,
214+
}
215+
}
216+
217+
func GetEndpointFromRequestType(requestType RequestType) EndpointType {
218+
var requestEndpoint EndpointType
219+
220+
switch requestType {
221+
case ReqTypeORTB2Web:
222+
fallthrough
223+
case ReqTypeORTB2App:
224+
fallthrough
225+
case ReqTypeORTB2DOOH:
226+
requestEndpoint = EndpointAuction
227+
case ReqTypeAMP:
228+
requestEndpoint = EndpointAmp
229+
case ReqTypeVideo:
230+
requestEndpoint = EndpointVideo
231+
}
232+
233+
return requestEndpoint
234+
}
235+
199236
// The media types described in the "imp" json objects
200237
const (
201238
ImpTypeBanner ImpMediaType = "banner"

metrics/metrics_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package metrics
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestGetEndpointFromRequestType(t *testing.T) {
10+
testCases := []struct {
11+
name string
12+
inRequestType RequestType
13+
expected EndpointType
14+
}{
15+
{
16+
name: "request-type-openrtb2-web",
17+
inRequestType: ReqTypeORTB2Web,
18+
expected: EndpointAuction,
19+
},
20+
{
21+
name: "request-type-openrtb2-app",
22+
inRequestType: ReqTypeORTB2App,
23+
expected: EndpointAuction,
24+
},
25+
{
26+
name: "request-type-openrtb2-dooh",
27+
inRequestType: ReqTypeORTB2DOOH,
28+
expected: EndpointAuction,
29+
},
30+
{
31+
name: "request-type-amp",
32+
inRequestType: ReqTypeAMP,
33+
expected: EndpointAmp,
34+
},
35+
{
36+
name: "request-type-video",
37+
inRequestType: ReqTypeVideo,
38+
expected: EndpointVideo,
39+
},
40+
}
41+
42+
for _, tc := range testCases {
43+
t.Run(tc.name, func(t *testing.T) {
44+
out := GetEndpointFromRequestType(tc.inRequestType)
45+
46+
assert.Equal(t, tc.expected, out)
47+
})
48+
}
49+
}

metrics/prometheus/prometheus.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type Metrics struct {
3030
impressions *prometheus.CounterVec
3131
prebidCacheWriteTimer *prometheus.HistogramVec
3232
requests *prometheus.CounterVec
33+
requestsSize *prometheus.HistogramVec
3334
debugRequests prometheus.Counter
3435
requestsTimer *prometheus.HistogramVec
3536
requestsQueueTimer *prometheus.HistogramVec
@@ -127,6 +128,7 @@ const (
127128
privacyBlockedLabel = "privacy_blocked"
128129
requestStatusLabel = "request_status"
129130
requestTypeLabel = "request_type"
131+
requestEndpointLabel = "request_size"
130132
stageLabel = "stage"
131133
statusLabel = "status"
132134
successLabel = "success"
@@ -171,6 +173,7 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
171173
priceBuckets := []float64{250, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000}
172174
queuedRequestTimeBuckets := []float64{0, 1, 5, 30, 60, 120, 180, 240, 300}
173175
overheadTimeBuckets := []float64{0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1}
176+
requestSizeBuckets := []float64{100, 500, 750, 1000, 2000, 4000, 7000, 10000, 15000, 20000, 50000, 75000}
174177

175178
metrics := Metrics{}
176179
reg := prometheus.NewRegistry()
@@ -227,6 +230,11 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
227230
"Count of total requests to Prebid Server labeled by type and status.",
228231
[]string{requestTypeLabel, requestStatusLabel})
229232

233+
metrics.requestsSize = newHistogramVec(cfg, reg,
234+
"request_size_bytes",
235+
"Count that keeps track of incoming request size in bytes labeled by endpoint.",
236+
[]string{requestEndpointLabel}, requestSizeBuckets)
237+
230238
metrics.debugRequests = newCounterWithoutLabels(cfg, reg,
231239
"debug_requests",
232240
"Count of total requests to Prebid Server that have debug enabled")
@@ -682,6 +690,13 @@ func (m *Metrics) RecordRequest(labels metrics.Labels) {
682690
requestStatusLabel: string(labels.RequestStatus),
683691
}).Inc()
684692

693+
if labels.RequestSize > 0 && labels.RType != metrics.ReqTypeAMP {
694+
endpoint := metrics.GetEndpointFromRequestType(labels.RType)
695+
m.requestsSize.With(prometheus.Labels{
696+
requestEndpointLabel: string(endpoint),
697+
}).Observe(float64(labels.RequestSize))
698+
}
699+
685700
if labels.CookieFlag == metrics.CookieFlagNo {
686701
m.requestsWithoutCookie.With(prometheus.Labels{
687702
requestTypeLabel: string(labels.RType),

metrics/prometheus/prometheus_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func TestRequestMetric(t *testing.T) {
166166
m.RecordRequest(metrics.Labels{
167167
RType: requestType,
168168
RequestStatus: requestStatus,
169+
RequestSize: 1024,
169170
})
170171

171172
expectedCount := float64(1)
@@ -175,6 +176,9 @@ func TestRequestMetric(t *testing.T) {
175176
requestTypeLabel: string(requestType),
176177
requestStatusLabel: string(requestStatus),
177178
})
179+
180+
histogram := getHistogramFromHistogramVec(m.requestsSize, requestEndpointLabel, string(metrics.EndpointAuction))
181+
assertHistogram(t, "requests_size_auction", histogram, 1, 1024)
178182
}
179183

180184
func TestDebugRequestMetric(t *testing.T) {

0 commit comments

Comments
 (0)