diff --git a/exchange/exchange.go b/exchange/exchange.go index 992f756a5ba..04af7729273 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -764,49 +764,51 @@ func applyCategoryMapping(ctx context.Context, bidRequest *openrtb2.BidRequest, categoryDuration = fmt.Sprintf("%s_%s", categoryDuration, bidderName.String()) } - if dupe, ok := dedupe[dupeKey]; ok { + if !brandCatExt.SkipDedup { + if dupe, ok := dedupe[dupeKey]; ok { - dupeBidPrice, err := strconv.ParseFloat(dupe.bidPrice, 64) - if err != nil { - dupeBidPrice = 0 - } - currBidPrice, err := strconv.ParseFloat(pb, 64) - if err != nil { - currBidPrice = 0 - } - if dupeBidPrice == currBidPrice { - if rand.Intn(100) < 50 { - dupeBidPrice = -1 - } else { - currBidPrice = -1 + dupeBidPrice, err := strconv.ParseFloat(dupe.bidPrice, 64) + if err != nil { + dupeBidPrice = 0 + } + currBidPrice, err := strconv.ParseFloat(pb, 64) + if err != nil { + currBidPrice = 0 + } + if dupeBidPrice == currBidPrice { + if rand.Intn(100) < 50 { + dupeBidPrice = -1 + } else { + currBidPrice = -1 + } } - } - if dupeBidPrice < currBidPrice { - if dupe.bidderName == bidderName { - // An older bid from the current bidder - bidsToRemove = append(bidsToRemove, dupe.bidIndex) - rejections = updateRejections(rejections, dupe.bidID, "Bid was deduplicated") - } else { - // An older bid from a different seatBid we've already finished with - oldSeatBid := (seatBids)[dupe.bidderName] - if len(oldSeatBid.bids) == 1 { - seatBidsToRemove = append(seatBidsToRemove, dupe.bidderName) + if dupeBidPrice < currBidPrice { + if dupe.bidderName == bidderName { + // An older bid from the current bidder + bidsToRemove = append(bidsToRemove, dupe.bidIndex) rejections = updateRejections(rejections, dupe.bidID, "Bid was deduplicated") } else { - oldSeatBid.bids = append(oldSeatBid.bids[:dupe.bidIndex], oldSeatBid.bids[dupe.bidIndex+1:]...) + // An older bid from a different seatBid we've already finished with + oldSeatBid := (seatBids)[dupe.bidderName] + if len(oldSeatBid.bids) == 1 { + seatBidsToRemove = append(seatBidsToRemove, dupe.bidderName) + rejections = updateRejections(rejections, dupe.bidID, "Bid was deduplicated") + } else { + oldSeatBid.bids = append(oldSeatBid.bids[:dupe.bidIndex], oldSeatBid.bids[dupe.bidIndex+1:]...) + } } + delete(res, dupe.bidID) + } else { + // Remove this bid + bidsToRemove = append(bidsToRemove, bidInd) + rejections = updateRejections(rejections, bidID, "Bid was deduplicated") + continue } - delete(res, dupe.bidID) - } else { - // Remove this bid - bidsToRemove = append(bidsToRemove, bidInd) - rejections = updateRejections(rejections, bidID, "Bid was deduplicated") - continue } + dedupe[dupeKey] = bidDedupe{bidderName: bidderName, bidIndex: bidInd, bidID: bidID, bidPrice: pb} } res[bidID] = categoryDuration - dedupe[dupeKey] = bidDedupe{bidderName: bidderName, bidIndex: bidInd, bidID: bidID, bidPrice: pb} } if len(bidsToRemove) > 0 { diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 24fa2338e51..718fb98c8f1 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -3092,3 +3092,47 @@ type nilCategoryFetcher struct{} func (nilCategoryFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { return "", nil } + +func TestRecordAdaptorDuplicateBidIDs(t *testing.T) { + type bidderCollisions = map[string]int + testCases := []struct { + scenario string + bidderCollisions *bidderCollisions // represents no of collisions detected for bid.id at bidder level for given request + hasCollision bool + }{ + {scenario: "invalid collision value", bidderCollisions: &map[string]int{"bidder-1": -1}, hasCollision: false}, + {scenario: "no collision", bidderCollisions: &map[string]int{"bidder-1": 0}, hasCollision: false}, + {scenario: "one collision", bidderCollisions: &map[string]int{"bidder-1": 1}, hasCollision: false}, + {scenario: "multiple collisions", bidderCollisions: &map[string]int{"bidder-1": 2}, hasCollision: true}, // when 2 collisions it counter will be 1 + {scenario: "multiple bidders", bidderCollisions: &map[string]int{"bidder-1": 2, "bidder-2": 4}, hasCollision: true}, + {scenario: "multiple bidders with bidder-1 no collision", bidderCollisions: &map[string]int{"bidder-1": 1, "bidder-2": 4}, hasCollision: true}, + {scenario: "no bidders", bidderCollisions: nil, hasCollision: false}, + } + testEngine := metricsConf.NewMetricsEngine(&config.Configuration{}, nil) + + for _, testcase := range testCases { + var adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid + if nil == testcase.bidderCollisions { + break + } + adapterBids = make(map[openrtb_ext.BidderName]*pbsOrtbSeatBid) + for bidder, collisions := range *testcase.bidderCollisions { + bids := make([]*pbsOrtbBid, 0) + testBidID := "bid_id_for_bidder_" + bidder + // add bids as per collisions value + bidCount := 0 + for ; bidCount < collisions; bidCount++ { + bids = append(bids, &pbsOrtbBid{ + bid: &openrtb2.Bid{ + ID: testBidID, + }, + }) + } + if nil == adapterBids[openrtb_ext.BidderName(bidder)] { + adapterBids[openrtb_ext.BidderName(bidder)] = new(pbsOrtbSeatBid) + } + adapterBids[openrtb_ext.BidderName(bidder)].bids = bids + } + assert.Equal(t, testcase.hasCollision, recordAdaptorDuplicateBidIDs(testEngine, adapterBids)) + } +}