Skip to content

Commit

Permalink
OTT-1431: Added publisher level stats for vast-wrapper count and resp…
Browse files Browse the repository at this point in the history
…onse time (#647)
  • Loading branch information
pm-jaydeep-mohite authored Nov 21, 2023
1 parent f275b8f commit c6c9682
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 72 deletions.
4 changes: 4 additions & 0 deletions modules/pubmatic/vastunwrap/entryhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func handleEntrypointHook(
vastRequestContext := models.RequestCtx{
VastUnwrapEnabled: getVastUnwrapperEnable(payload.Request.Context(), VastUnwrapEnabled) && getRandomNumber() < config.TrafficPercentage,
}

if !vastRequestContext.VastUnwrapEnabled {
vastRequestContext.VastUnwrapStatsEnabled = getRandomNumber() < config.StatTrafficPercentage
}
result.ModuleContext = make(hookstage.ModuleContext)
result.ModuleContext[RequestContext] = vastRequestContext
return result, nil
Expand Down
38 changes: 25 additions & 13 deletions modules/pubmatic/vastunwrap/hook_raw_bidder_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,38 @@ func (m VastUnwrapModule) handleRawBidderResponseHook(
result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleRawBidderResponseHook()")
return result, nil
}
if !vastRequestContext.VastUnwrapEnabled {
if !vastRequestContext.VastUnwrapEnabled && !vastRequestContext.VastUnwrapStatsEnabled {
result.DebugMessages = append(result.DebugMessages, "error: vast unwrap flag is not enabled in handleRawBidderResponseHook()")
return result, nil
}
defer func() {
miCtx.ModuleContext[RequestContext] = vastRequestContext
}()
wg := new(sync.WaitGroup)
for _, bid := range payload.Bids {
if string(bid.BidType) == MediaTypeVideo {
wg.Add(1)
go func(bid *adapters.TypedBid) {
defer wg.Done()
m.doUnwrapandUpdateBid(bid, vastRequestContext.UA, unwrapURL, miCtx.AccountID, payload.Bidder)
}(bid)

// Below code collects stats only
if vastRequestContext.VastUnwrapStatsEnabled {
for _, bid := range payload.Bids {
if string(bid.BidType) == MediaTypeVideo {
go func(bid *adapters.TypedBid) {
m.doUnwrapandUpdateBid(vastRequestContext.VastUnwrapStatsEnabled, bid, vastRequestContext.UA, unwrapURL, miCtx.AccountID, payload.Bidder)
}(bid)
}
}
} else {
wg := new(sync.WaitGroup)
for _, bid := range payload.Bids {
if string(bid.BidType) == MediaTypeVideo {
wg.Add(1)
go func(bid *adapters.TypedBid) {
defer wg.Done()
m.doUnwrapandUpdateBid(vastRequestContext.VastUnwrapStatsEnabled, bid, vastRequestContext.UA, unwrapURL, miCtx.AccountID, payload.Bidder)
}(bid)
}
}
wg.Wait()
changeSet := hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{}
changeSet.RawBidderResponse().Bids().Update(payload.Bids)
result.ChangeSet = changeSet
}
wg.Wait()
changeSet := hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{}
changeSet.RawBidderResponse().Bids().Update(payload.Bids)
result.ChangeSet = changeSet
return result, nil
}
58 changes: 47 additions & 11 deletions modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,42 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false},
wantErr: false,
},
{
name: "Set Vast Unwrapper to false in request context with type video, stats enabled true",
args: args{
module: VastUnWrapModule,
payload: hookstage.RawBidderResponsePayload{
Bids: []*adapters.TypedBid{
{
Bid: &openrtb2.Bid{
ID: "Bid-123",
ImpID: fmt.Sprintf("div-adunit-%d", 123),
Price: 2.1,
AdM: vastXMLAdM,
CrID: "Cr-234",
W: 100,
H: 50,
},
BidType: "video",
}},
Bidder: "pubmatic",
},
moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false, VastUnwrapStatsEnabled: true}}},
},
wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false},
setup: func() {
mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes()
mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes()
},
unwrapRequest: func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("unwrap-status", "0")
w.Header().Add("unwrap-count", "1")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(inlineXMLAdM))
},
wantErr: false,
},
{
name: "Set Vast Unwrapper to true in request context with invalid vast xml",
args: args{
Expand All @@ -96,8 +132,8 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
},
wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false},
setup: func() {
mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "1").AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "1").AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes()
},
unwrapRequest: func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("unwrap-status", "1")
Expand Down Expand Up @@ -132,9 +168,9 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
},
wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false},
setup: func() {
mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0").AnyTimes()
mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1").AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes()
mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes()
},
unwrapRequest: func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("unwrap-status", "0")
Expand Down Expand Up @@ -182,9 +218,9 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
},
wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false},
setup: func() {
mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0").AnyTimes()
mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1").AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes()
mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes()
},
unwrapRequest: func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("unwrap-status", "0")
Expand Down Expand Up @@ -256,9 +292,9 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
},
},
setup: func() {
mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0").AnyTimes()
mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "0").AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any()).AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes()
mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "0").AnyTimes()
mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes()
},
unwrapRequest: func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("unwrap-status", "0")
Expand Down
5 changes: 3 additions & 2 deletions modules/pubmatic/vastunwrap/models/request.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package models

type RequestCtx struct {
UA string
VastUnwrapEnabled bool
UA string
VastUnwrapEnabled bool
VastUnwrapStatsEnabled bool
}
11 changes: 6 additions & 5 deletions modules/pubmatic/vastunwrap/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ import (
)

type VastUnwrapModule struct {
Cfg unWrapCfg.VastUnWrapCfg `mapstructure:"vastunwrap_cfg" json:"vastunwrap_cfg"`
TrafficPercentage int `mapstructure:"traffic_percentage" json:"traffic_percentage"`
Enabled bool `mapstructure:"enabled" json:"enabled"`
MetricsEngine metrics.MetricsEngine
unwrapRequest func(w http.ResponseWriter, r *http.Request)
Cfg unWrapCfg.VastUnWrapCfg `mapstructure:"vastunwrap_cfg" json:"vastunwrap_cfg"`
TrafficPercentage int `mapstructure:"traffic_percentage" json:"traffic_percentage"`
StatTrafficPercentage int `mapstructure:"stat_traffic_percentage" json:"stat_traffic_percentage"`
Enabled bool `mapstructure:"enabled" json:"enabled"`
MetricsEngine metrics.MetricsEngine
unwrapRequest func(w http.ResponseWriter, r *http.Request)
}

func Builder(rawCfg json.RawMessage, deps moduledeps.ModuleDeps) (interface{}, error) {
Expand Down
6 changes: 3 additions & 3 deletions modules/pubmatic/vastunwrap/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,9 @@ func TestVastUnwrapModuleHandleRawBidderResponseHook(t *testing.T) {
wantAdM: true,
},
setup: func() {
mockMetricsEngine.EXPECT().RecordRequestStatus("pubmatic", "0")
mockMetricsEngine.EXPECT().RecordWrapperCount("pubmatic", "1")
mockMetricsEngine.EXPECT().RecordRequestTime("pubmatic", gomock.Any())
mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0")
mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1")
mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any())
},
unwrapRequest: func(w http.ResponseWriter, req *http.Request) {
w.Header().Add("unwrap-status", "0")
Expand Down
21 changes: 12 additions & 9 deletions modules/pubmatic/vastunwrap/stats/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ const (

// MetricsEngine is a generic interface to record metrics into the desired backend
type MetricsEngine interface {
RecordRequestStatus(bidder, status string)
RecordWrapperCount(bidder string, wrapper_count string)
RecordRequestTime(bidder string, readTime time.Duration)
RecordRequestStatus(accountId, bidder, status string)
RecordWrapperCount(accountId, bidder string, wrapper_count string)
RecordRequestTime(accountId, bidder string, readTime time.Duration)
}

// Metrics defines the datatype which will implement MetricsEngine
Expand All @@ -48,14 +48,14 @@ func NewMetricsEngine(cfg moduledeps.ModuleDeps) (*Metrics, error) {
metrics.requests = newCounter(cfg.MetricsCfg.Prometheus, metrics.Registry,
"vastunwrap_status",
"Count of vast unwrap requests labeled by status",
[]string{bidderLabel, statusLabel})
[]string{pubIdLabel, bidderLabel, statusLabel})
metrics.wrapperCount = newCounter(cfg.MetricsCfg.Prometheus, metrics.Registry,
"vastunwrap_wrapper_count",
"Count of vast unwrap levels labeled by bidder",
[]string{bidderLabel, wrapperCountLabel})
[]string{pubIdLabel, bidderLabel, wrapperCountLabel})
metrics.requestTime = newHistogramVec(cfg.MetricsCfg.Prometheus, metrics.Registry,
"vastunwrap_request_time",
"Time taken to serve the vast unwrap request in Milliseconds", []string{bidderLabel},
"Time taken to serve the vast unwrap request in Milliseconds", []string{pubIdLabel, bidderLabel},
[]float64{50, 100, 200, 300, 500})
return &metrics, nil
}
Expand Down Expand Up @@ -86,24 +86,27 @@ func newHistogramVec(cfg config.PrometheusMetrics, registry *prometheus.Registry
}

// RecordRequest record counter with vast unwrap status
func (m *Metrics) RecordRequestStatus(bidder, status string) {
func (m *Metrics) RecordRequestStatus(accountId, bidder, status string) {
m.requests.With(prometheus.Labels{
pubIdLabel: accountId,
bidderLabel: bidder,
statusLabel: status,
}).Inc()
}

// RecordWrapperCount record counter of wrapper levels
func (m *Metrics) RecordWrapperCount(bidder, wrapper_count string) {
func (m *Metrics) RecordWrapperCount(accountId, bidder, wrapper_count string) {
m.wrapperCount.With(prometheus.Labels{
pubIdLabel: accountId,
bidderLabel: bidder,
wrapperCountLabel: wrapper_count,
}).Inc()
}

// RecordRequestReadTime records time takent to complete vast unwrap
func (m *Metrics) RecordRequestTime(bidder string, requestTime time.Duration) {
func (m *Metrics) RecordRequestTime(accountId, bidder string, requestTime time.Duration) {
m.requestTime.With(prometheus.Labels{
pubIdLabel: accountId,
bidderLabel: bidder,
}).Observe(float64(requestTime.Milliseconds()))
}
8 changes: 5 additions & 3 deletions modules/pubmatic/vastunwrap/stats/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,18 @@ func createMetricsForTesting() *Metrics {
func TestRecordRequestTime(t *testing.T) {
m := createMetricsForTesting()

m.RecordRequestTime("pubmatic", time.Millisecond*250)
m.RecordRequestTime("1234", "pubmatic", time.Millisecond*250)

result := getHistogramFromHistogramVec(m.requestTime, "bidder", "pubmatic")
assertHistogram(t, result, 1, 250)
}
func TestRecordRequestStatus(t *testing.T) {
m := createMetricsForTesting()

m.RecordRequestStatus("pubmatic", "0")
m.RecordRequestStatus("1234", "pubmatic", "0")

assertCounterVecValue(t, "Record_Request_Status", "Record_Request_Status_Success", m.requests, float64(1), prometheus.Labels{
"pub_id": "1234",
"bidder": "pubmatic",
"status": "0",
})
Expand All @@ -55,9 +56,10 @@ func TestRecordRequestStatus(t *testing.T) {
func TestRecordWrapperCount(t *testing.T) {
m := createMetricsForTesting()

m.RecordWrapperCount("pubmatic", "1")
m.RecordWrapperCount("1234", "pubmatic", "1")

assertCounterVecValue(t, "Record_Wrapper_Count", "Record_Wrapper_Count", m.wrapperCount, float64(1), prometheus.Labels{
"pub_id": "1234",
"bidder": "pubmatic",
"wrapper_count": "1",
})
Expand Down
24 changes: 12 additions & 12 deletions modules/pubmatic/vastunwrap/stats/mock/mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions modules/pubmatic/vastunwrap/unwrap_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/prebid/prebid-server/adapters"
)

func (m VastUnwrapModule) doUnwrapandUpdateBid(bid *adapters.TypedBid, userAgent string, unwrapURL string, accountID string, bidder string) {
func (m VastUnwrapModule) doUnwrapandUpdateBid(isStatsEnabled bool, bid *adapters.TypedBid, userAgent string, unwrapURL string, accountID string, bidder string) {
startTime := time.Now()
var wrapperCnt int64
var respStatus string
Expand All @@ -23,10 +23,10 @@ func (m VastUnwrapModule) doUnwrapandUpdateBid(bid *adapters.TypedBid, userAgent
glog.Errorf("AdM:[%s] Error:[%v] stacktrace:[%s]", bid.Bid.AdM, r, string(debug.Stack()))
}
respTime := time.Since(startTime)
m.MetricsEngine.RecordRequestTime(bidder, respTime)
m.MetricsEngine.RecordRequestStatus(bidder, respStatus)
m.MetricsEngine.RecordRequestTime(accountID, bidder, respTime)
m.MetricsEngine.RecordRequestStatus(accountID, bidder, respStatus)
if respStatus == "0" {
m.MetricsEngine.RecordWrapperCount(bidder, strconv.Itoa(int(wrapperCnt)))
m.MetricsEngine.RecordWrapperCount(accountID, bidder, strconv.Itoa(int(wrapperCnt)))
}
}()
headers := http.Header{}
Expand All @@ -42,8 +42,8 @@ func (m VastUnwrapModule) doUnwrapandUpdateBid(bid *adapters.TypedBid, userAgent
m.unwrapRequest(httpResp, httpReq)
respStatus = httpResp.Header().Get(UnwrapStatus)
wrapperCnt, _ = strconv.ParseInt(httpResp.Header().Get(UnwrapCount), 10, 0)
respBody := httpResp.Body.Bytes()
if httpResp.Code == http.StatusOK {
if !isStatsEnabled && httpResp.Code == http.StatusOK {
respBody := httpResp.Body.Bytes()
bid.Bid.AdM = string(respBody)
return
}
Expand Down
Loading

0 comments on commit c6c9682

Please sign in to comment.