Skip to content

Commit 905b3a5

Browse files
ShriprasadMpm-shriprasad-maratheashishshinde-pubmPubmatic-Dhruv-Sonone
authored
Log non bid reasons in bidder framework (#2891)
Co-authored-by: Shriprasad Marathe <shriprasad.marathe@pubmatic.com> Co-authored-by: ashish.shinde <ashish.shinde@pubmatic.com> Co-authored-by: dhruv.sonone <dhruv.sonone@pubmatic.com>
1 parent 3c4527e commit 905b3a5

File tree

11 files changed

+859
-158
lines changed

11 files changed

+859
-158
lines changed

analytics/pubstack/pubstack_module_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func TestNewModuleSuccess(t *testing.T) {
9999
{
100100
ImpId: "123",
101101
StatusCode: 34,
102-
Ext: openrtb_ext.NonBidExt{Prebid: openrtb_ext.ExtResponseNonBidPrebid{Bid: openrtb_ext.NonBidObject{}}},
102+
Ext: &openrtb_ext.NonBidExt{Prebid: openrtb_ext.ExtResponseNonBidPrebid{Bid: openrtb_ext.NonBidObject{}}},
103103
},
104104
},
105105
},

exchange/bidder.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,14 @@ type bidRequestOptions struct {
7676

7777
type extraBidderRespInfo struct {
7878
respProcessingStartTime time.Time
79+
seatNonBidBuilder SeatNonBidBuilder
7980
}
8081

8182
type extraAuctionResponseInfo struct {
8283
fledge *openrtb_ext.Fledge
8384
bidsFound bool
8485
bidderResponseStartTime time.Time
86+
seatNonBidBuilder SeatNonBidBuilder
8587
}
8688

8789
const ImpIdReqBody = "Stored bid response for impression id: "
@@ -135,6 +137,7 @@ type bidderAdapterConfig struct {
135137
func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest BidderRequest, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, adsCertSigner adscert.Signer, bidRequestOptions bidRequestOptions, alternateBidderCodes openrtb_ext.ExtAlternateBidderCodes, hookExecutor hookexecution.StageExecutor, ruleToAdjustments openrtb_ext.AdjustmentsByDealID) ([]*entities.PbsOrtbSeatBid, extraBidderRespInfo, []error) {
136138
request := openrtb_ext.RequestWrapper{BidRequest: bidderRequest.BidRequest}
137139
reject := hookExecutor.ExecuteBidderRequestStage(&request, string(bidderRequest.BidderName))
140+
seatNonBidBuilder := SeatNonBidBuilder{}
138141
if reject != nil {
139142
return nil, extraBidderRespInfo{}, []error{reject}
140143
}
@@ -398,13 +401,17 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde
398401
}
399402
} else {
400403
errs = append(errs, httpInfo.err)
404+
nonBidReason := httpInfoToNonBidReason(httpInfo)
405+
seatNonBidBuilder.rejectImps(httpInfo.request.ImpIDs, nonBidReason, string(bidderRequest.BidderName))
401406
}
402407
}
408+
403409
seatBids := make([]*entities.PbsOrtbSeatBid, 0, len(seatBidMap))
404410
for _, seatBid := range seatBidMap {
405411
seatBids = append(seatBids, seatBid)
406412
}
407413

414+
extraRespInfo.seatNonBidBuilder = seatNonBidBuilder
408415
return seatBids, extraRespInfo, errs
409416
}
410417

exchange/bidder_test.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@ import (
99
"errors"
1010
"fmt"
1111
"io"
12+
"net"
1213
"net/http"
1314
"net/http/httptest"
1415
"net/http/httptrace"
16+
"net/url"
17+
"os"
1518
"sort"
1619
"strings"
20+
"syscall"
1721
"testing"
1822
"time"
1923

@@ -3096,6 +3100,148 @@ func TestGetBidType(t *testing.T) {
30963100
}
30973101
}
30983102

3103+
func TestSeatNonBid(t *testing.T) {
3104+
type args struct {
3105+
BidRequest *openrtb2.BidRequest
3106+
Seat string
3107+
SeatRequests []*adapters.RequestData
3108+
BidderResponse func() (*http.Response, error)
3109+
client *http.Client
3110+
}
3111+
type expect struct {
3112+
seatBids []*entities.PbsOrtbSeatBid
3113+
seatNonBids SeatNonBidBuilder
3114+
errors []error
3115+
}
3116+
testCases := []struct {
3117+
name string
3118+
args args
3119+
expect expect
3120+
}{
3121+
{
3122+
name: "NBR_101_timeout_for_context_deadline_exceeded",
3123+
args: args{
3124+
Seat: "pubmatic",
3125+
BidRequest: &openrtb2.BidRequest{
3126+
Imp: []openrtb2.Imp{{ID: "1234"}},
3127+
},
3128+
SeatRequests: []*adapters.RequestData{{ImpIDs: []string{"1234"}}},
3129+
BidderResponse: func() (*http.Response, error) { return nil, context.DeadlineExceeded },
3130+
client: &http.Client{Timeout: time.Nanosecond}, // for timeout
3131+
},
3132+
expect: expect{
3133+
seatNonBids: SeatNonBidBuilder{
3134+
"pubmatic": {{
3135+
ImpId: "1234",
3136+
StatusCode: int(ErrorTimeout),
3137+
}},
3138+
},
3139+
errors: []error{&errortypes.Timeout{Message: context.DeadlineExceeded.Error()}},
3140+
seatBids: []*entities.PbsOrtbSeatBid{{Bids: []*entities.PbsOrtbBid{}, Currency: "USD", Seat: "pubmatic", HttpCalls: []*openrtb_ext.ExtHttpCall{}}},
3141+
},
3142+
}, {
3143+
name: "NBR_103_Bidder_Unreachable_Connection_Refused",
3144+
args: args{
3145+
Seat: "appnexus",
3146+
SeatRequests: []*adapters.RequestData{{ImpIDs: []string{"1234", "4567"}}},
3147+
BidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "1234"}, {ID: "4567"}}},
3148+
BidderResponse: func() (*http.Response, error) {
3149+
return nil, &net.OpError{Err: os.NewSyscallError(syscall.ECONNREFUSED.Error(), syscall.ECONNREFUSED)}
3150+
},
3151+
},
3152+
expect: expect{
3153+
seatNonBids: SeatNonBidBuilder{
3154+
"appnexus": {
3155+
{ImpId: "1234", StatusCode: int(ErrorBidderUnreachable)},
3156+
{ImpId: "4567", StatusCode: int(ErrorBidderUnreachable)},
3157+
},
3158+
},
3159+
seatBids: []*entities.PbsOrtbSeatBid{{Bids: []*entities.PbsOrtbBid{}, Currency: "USD", Seat: "appnexus", HttpCalls: []*openrtb_ext.ExtHttpCall{}}},
3160+
errors: []error{&url.Error{Op: "Get", URL: "", Err: &net.OpError{Err: os.NewSyscallError(syscall.ECONNREFUSED.Error(), syscall.ECONNREFUSED)}}},
3161+
},
3162+
}, {
3163+
name: "no_impids_populated_in_request_data",
3164+
args: args{
3165+
SeatRequests: []*adapters.RequestData{{
3166+
ImpIDs: nil, // no imp ids
3167+
}},
3168+
BidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "1234"}}},
3169+
BidderResponse: func() (*http.Response, error) {
3170+
return nil, errors.New("some_error")
3171+
},
3172+
},
3173+
expect: expect{
3174+
seatNonBids: SeatNonBidBuilder{},
3175+
seatBids: []*entities.PbsOrtbSeatBid{{Bids: []*entities.PbsOrtbBid{}, Currency: "USD", HttpCalls: []*openrtb_ext.ExtHttpCall{}}},
3176+
errors: []error{&url.Error{Op: "Get", URL: "", Err: errors.New("some_error")}},
3177+
},
3178+
},
3179+
}
3180+
for _, test := range testCases {
3181+
t.Run(test.name, func(t *testing.T) {
3182+
mockBidder := &mockBidder{}
3183+
mockBidder.On("MakeRequests", mock.Anything, mock.Anything).Return(test.args.SeatRequests, []error(nil))
3184+
mockMetricsEngine := &metrics.MetricsEngineMock{}
3185+
mockMetricsEngine.On("RecordOverheadTime", mock.Anything, mock.Anything).Return(nil)
3186+
mockMetricsEngine.On("RecordBidderServerResponseTime", mock.Anything).Return(nil)
3187+
roundTrip := &mockRoundTripper{}
3188+
roundTrip.On("RoundTrip", mock.Anything).Return(test.args.BidderResponse())
3189+
client := &http.Client{
3190+
Transport: roundTrip,
3191+
Timeout: 0,
3192+
}
3193+
if test.args.client != nil {
3194+
client.Timeout = test.args.client.Timeout
3195+
}
3196+
bidder := AdaptBidder(mockBidder, client, &config.Configuration{}, mockMetricsEngine, openrtb_ext.BidderAppnexus, &config.DebugInfo{}, test.args.Seat)
3197+
3198+
ctx := context.Background()
3199+
if client.Timeout > 0 {
3200+
ctxTimeout, cancel := context.WithTimeout(ctx, client.Timeout)
3201+
ctx = ctxTimeout
3202+
defer cancel()
3203+
}
3204+
seatBids, responseExtra, errors := bidder.requestBid(ctx, BidderRequest{
3205+
BidRequest: test.args.BidRequest,
3206+
BidderName: openrtb_ext.BidderName(test.args.Seat),
3207+
}, nil, &adapters.ExtraRequestInfo{}, &MockSigner{}, bidRequestOptions{}, openrtb_ext.ExtAlternateBidderCodes{}, hookexecution.EmptyHookExecutor{}, nil)
3208+
assert.Equal(t, test.expect.seatBids, seatBids)
3209+
assert.Equal(t, test.expect.seatNonBids, responseExtra.seatNonBidBuilder)
3210+
assert.Equal(t, test.expect.errors, errors)
3211+
for _, nonBids := range responseExtra.seatNonBidBuilder {
3212+
for _, nonBid := range nonBids {
3213+
for _, seatBid := range seatBids {
3214+
for _, bid := range seatBid.Bids {
3215+
// ensure non bids are not present in seat bids
3216+
if nonBid.ImpId == bid.Bid.ImpID {
3217+
assert.Fail(t, "imp id [%s] present in both seat bid and non seat bid", nonBid.ImpId)
3218+
}
3219+
}
3220+
}
3221+
}
3222+
}
3223+
})
3224+
}
3225+
}
3226+
3227+
type mockRoundTripper struct {
3228+
mock.Mock
3229+
}
3230+
3231+
func (rt *mockRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
3232+
args := rt.Called(request)
3233+
var response *http.Response
3234+
if args.Get(0) != nil {
3235+
response = args.Get(0).(*http.Response)
3236+
}
3237+
var err error
3238+
if args.Get(1) != nil {
3239+
err = args.Get(1).(error)
3240+
}
3241+
3242+
return response, err
3243+
}
3244+
30993245
type mockBidderTmaxCtx struct {
31003246
startTime, deadline, now time.Time
31013247
ok bool

0 commit comments

Comments
 (0)