Skip to content

Commit

Permalink
Core: Add Bidder Dimension to Price Floors (#3190)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoxaAntoxic authored Jul 10, 2024
1 parent c5f2939 commit feeccec
Show file tree
Hide file tree
Showing 42 changed files with 1,123 additions and 666 deletions.
29 changes: 22 additions & 7 deletions src/main/java/org/prebid/server/auction/ExchangeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import org.prebid.server.execution.TimeoutFactory;
import org.prebid.server.floors.PriceFloorAdjuster;
import org.prebid.server.floors.PriceFloorEnforcer;
import org.prebid.server.floors.PriceFloorProcessor;
import org.prebid.server.hooks.execution.HookStageExecutor;
import org.prebid.server.hooks.execution.model.ExecutionAction;
import org.prebid.server.hooks.execution.model.ExecutionStatus;
Expand Down Expand Up @@ -189,6 +190,7 @@ public class ExchangeService {
private final HttpInteractionLogger httpInteractionLogger;
private final PriceFloorAdjuster priceFloorAdjuster;
private final PriceFloorEnforcer priceFloorEnforcer;
private final PriceFloorProcessor priceFloorProcessor;
private final DsaEnforcer dsaEnforcer;
private final BidAdjustmentFactorResolver bidAdjustmentFactorResolver;
private final Metrics metrics;
Expand Down Expand Up @@ -218,6 +220,7 @@ public ExchangeService(double logSamplingRate,
HttpInteractionLogger httpInteractionLogger,
PriceFloorAdjuster priceFloorAdjuster,
PriceFloorEnforcer priceFloorEnforcer,
PriceFloorProcessor priceFloorProcessor,
DsaEnforcer dsaEnforcer,
BidAdjustmentFactorResolver bidAdjustmentFactorResolver,
Metrics metrics,
Expand Down Expand Up @@ -247,6 +250,7 @@ public ExchangeService(double logSamplingRate,
this.httpInteractionLogger = Objects.requireNonNull(httpInteractionLogger);
this.priceFloorAdjuster = Objects.requireNonNull(priceFloorAdjuster);
this.priceFloorEnforcer = Objects.requireNonNull(priceFloorEnforcer);
this.priceFloorProcessor = Objects.requireNonNull(priceFloorProcessor);
this.dsaEnforcer = Objects.requireNonNull(dsaEnforcer);
this.bidAdjustmentFactorResolver = Objects.requireNonNull(bidAdjustmentFactorResolver);
this.metrics = Objects.requireNonNull(metrics);
Expand Down Expand Up @@ -290,8 +294,8 @@ private Future<AuctionContext> runAuction(AuctionContext receivedContext) {

return storedResponseProcessor.getStoredResponseResult(bidRequest.getImp(), timeout)
.map(storedResponseResult -> populateStoredResponse(storedResponseResult, storedAuctionResponses))
.compose(storedResponseResult -> extractAuctionParticipations(
receivedContext, storedResponseResult, aliases, bidderToMultiBid)
.compose(storedResponseResult ->
extractAuctionParticipations(receivedContext, storedResponseResult, aliases, bidderToMultiBid)
.map(receivedContext::with))

.map(context -> updateRequestMetric(context, uidsCookie, aliases, account, requestTypeMetric))
Expand Down Expand Up @@ -876,8 +880,13 @@ private BidRequest prepareBidRequest(BidderPrivacyResult bidderPrivacyResult,
Map<String, JsonNode> bidderToPrebidBidders,
AuctionContext context) {

final BidRequest bidRequest = context.getBidRequest();
final String bidder = bidderPrivacyResult.getRequestBidder();
final BidRequest bidRequest = priceFloorProcessor.enrichWithPriceFloors(
context.getBidRequest().toBuilder().imp(imps).build(),
context.getAccount(),
bidder,
context.getPrebidErrors(),
context.getDebugWarnings());
final boolean transmitTid = transmitTransactionId(bidder, context);
final List<String> firstPartyDataBidders = firstPartyDataBidders(bidRequest.getExt());
final boolean useFirstPartyData = firstPartyDataBidders == null || firstPartyDataBidders.stream()
Expand Down Expand Up @@ -923,12 +932,19 @@ private BidRequest prepareBidRequest(BidderPrivacyResult bidderPrivacyResult,
final boolean isDooh = !isApp && preparedDooh != null;
final boolean isSite = !isApp && !isDooh && preparedSite != null;

final List<Imp> preparedImps = prepareImps(
bidder,
bidRequest,
transmitTid,
useFirstPartyData,
context.getAccount(),
context.getDebugWarnings());

return bidRequest.toBuilder()
// User was already prepared above
.user(bidderPrivacyResult.getUser())
.device(bidderPrivacyResult.getDevice())
.imp(prepareImps(bidder, imps, bidRequest, transmitTid,
useFirstPartyData, context.getAccount(), context.getDebugWarnings()))
.imp(preparedImps)
.app(isApp ? preparedApp : null)
.dooh(isDooh ? preparedDooh : null)
.site(isSite ? preparedSite : null)
Expand All @@ -955,14 +971,13 @@ private static boolean transmitTransactionId(String bidder, AuctionContext conte
}

private List<Imp> prepareImps(String bidder,
List<Imp> imps,
BidRequest bidRequest,
boolean transmitTid,
boolean useFirstPartyData,
Account account,
List<String> debugWarnings) {

return imps.stream()
return bidRequest.getImp().stream()
.filter(imp -> bidderParamsFromImpExt(imp.getExt()).hasNonNull(bidder))
.map(imp -> prepareImp(imp, bidder, bidRequest, transmitTid, useFirstPartyData, account, debugWarnings))
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,15 @@
import org.prebid.server.proto.openrtb.ext.request.ImpMediaType;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

public class FloorAdjustmentFactorResolver {

public BigDecimal resolve(ImpMediaType impMediaType,
ExtRequestBidAdjustmentFactors adjustmentFactors,
String bidder) {

final Set<ImpMediaType> impMediaTypes = impMediaType != null
? EnumSet.of(impMediaType)
: Collections.emptySet();

return resolve(impMediaTypes, adjustmentFactors, bidder);
}

public BigDecimal resolve(Set<ImpMediaType> impMediaTypes,
ExtRequestBidAdjustmentFactors adjustmentFactors,
String bidder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,6 @@ public Future<AuctionContext> fromRequest(RoutingContext routingContext, long st
.compose(auctionContext -> ortb2RequestFactory.executeProcessedAuctionRequestHooks(auctionContext)
.map(auctionContext::with))

.map(ortb2RequestFactory::enrichWithPriceFloors)

.map(ortb2RequestFactory::updateTimeout)

.recover(ortb2RequestFactory::restoreResultFromRejection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ public Future<AuctionContext> fromRequest(RoutingContext routingContext, long st
.compose(auctionContext -> ortb2RequestFactory.executeProcessedAuctionRequestHooks(auctionContext)
.map(auctionContext::with))

.map(ortb2RequestFactory::enrichWithPriceFloors)

.map(ortb2RequestFactory::updateTimeout)

.recover(ortb2RequestFactory::restoreResultFromRejection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,6 @@ private static BidRequest toBidRequest(HookStageExecutionResult<AuctionRequestPa
return stageResult.getPayload().bidRequest();
}

public AuctionContext enrichWithPriceFloors(AuctionContext auctionContext) {
return priceFloorProcessor.enrichWithPriceFloors(auctionContext);
}

public AuctionContext updateTimeout(AuctionContext auctionContext) {
final TimeoutContext timeoutContext = auctionContext.getTimeoutContext();
final long startTime = timeoutContext.getStartTime();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,6 @@ public Future<WithPodErrors<AuctionContext>> fromRequest(RoutingContext routingC
.compose(auctionContext -> ortb2RequestFactory.executeProcessedAuctionRequestHooks(auctionContext)
.map(auctionContext::with))

.map(ortb2RequestFactory::enrichWithPriceFloors)

.map(ortb2RequestFactory::updateTimeout)

.recover(ortb2RequestFactory::restoreResultFromRejection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ public class RubiconBidder implements Bidder<BidRequest> {
};
private static final boolean DEFAULT_MULTIFORMAT_VALUE = false;

private final String bidderName;
private final String endpointUrl;
private final Set<String> supportedVendors;
private final boolean generateBidId;
Expand All @@ -187,7 +188,8 @@ public class RubiconBidder implements Bidder<BidRequest> {

private final MultiMap headers;

public RubiconBidder(String endpoint,
public RubiconBidder(String bidderName,
String endpoint,
String xapiUsername,
String xapiPassword,
List<String> supportedVendors,
Expand All @@ -196,6 +198,7 @@ public RubiconBidder(String endpoint,
PriceFloorResolver floorResolver,
JacksonMapper mapper) {

this.bidderName = Objects.requireNonNull(bidderName);
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpoint));
this.supportedVendors = Set.copyOf(Objects.requireNonNull(supportedVendors));
this.generateBidId = generateBidId;
Expand Down Expand Up @@ -543,6 +546,7 @@ private PriceFloorResult resolvePriceFloors(BidRequest bidRequest,
imp,
mediaType,
null,
bidderName,
warnings);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;

public class BasicPriceFloorAdjuster implements PriceFloorAdjuster {

private static final int ADJUSTMENT_SCALE = 4;
private static final BiFunction<BigDecimal, BigDecimal, BigDecimal> DIVIDE_FUNCTION =
(priceFloor, factor) -> priceFloor.divide(factor, ADJUSTMENT_SCALE, RoundingMode.HALF_EVEN);
private static final BiFunction<BigDecimal, BigDecimal, BigDecimal> MULTIPLY_FUNCTION = BigDecimal::multiply;

private final FloorAdjustmentFactorResolver floorAdjustmentFactorResolver;

Expand All @@ -41,6 +45,20 @@ public Price adjustForImp(Imp imp,
Account account,
List<String> debugWarnings) {

return adjust(imp, bidder, bidRequest, account, DIVIDE_FUNCTION);
}

@Override
public Price revertAdjustmentForImp(Imp imp, String bidder, BidRequest bidRequest, Account account) {
return adjust(imp, bidder, bidRequest, account, MULTIPLY_FUNCTION);
}

private Price adjust(Imp imp,
String bidder,
BidRequest bidRequest,
Account account,
BiFunction<BigDecimal, BigDecimal, BigDecimal> function) {

final ExtRequestBidAdjustmentFactors extractBidAdjustmentFactors = extractBidAdjustmentFactors(bidRequest);
final BigDecimal impBidFloor = imp.getBidfloor();

Expand All @@ -52,8 +70,8 @@ public Price adjustForImp(Imp imp,
final BigDecimal factor = floorAdjustmentFactorResolver.resolve(
impMediaTypes, extractBidAdjustmentFactors, bidder);

final BigDecimal adjustedBidFloor = factor != null
? BidderUtil.roundFloor(impBidFloor.divide(factor, ADJUSTMENT_SCALE, RoundingMode.HALF_EVEN))
final BigDecimal adjustedBidFloor = factor != null && factor.compareTo(BigDecimal.ONE) != 0
? BidderUtil.roundFloor(function.apply(impBidFloor, factor))
: impBidFloor;

return Price.of(imp.getBidfloorcur(), adjustedBidFloor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.prebid.server.bidder.model.BidderBid;
import org.prebid.server.bidder.model.BidderError;
import org.prebid.server.bidder.model.BidderSeatBid;
import org.prebid.server.bidder.model.Price;
import org.prebid.server.bidder.model.PriceFloorInfo;
import org.prebid.server.currency.CurrencyConversionService;
import org.prebid.server.exception.PreBidException;
Expand Down Expand Up @@ -49,10 +50,15 @@ public class BasicPriceFloorEnforcer implements PriceFloorEnforcer {
private static final int ENFORCE_RATE_MAX = 100;

private final CurrencyConversionService currencyConversionService;
private final PriceFloorAdjuster priceFloorAdjuster;
private final Metrics metrics;

public BasicPriceFloorEnforcer(CurrencyConversionService currencyConversionService, Metrics metrics) {
public BasicPriceFloorEnforcer(CurrencyConversionService currencyConversionService,
PriceFloorAdjuster priceFloorAdjuster,
Metrics metrics) {

this.currencyConversionService = Objects.requireNonNull(currencyConversionService);
this.priceFloorAdjuster = Objects.requireNonNull(priceFloorAdjuster);
this.metrics = Objects.requireNonNull(metrics);
}

Expand Down Expand Up @@ -135,17 +141,19 @@ private AuctionParticipation applyEnforcement(BidRequest bidRequest,
final BidderResponse bidderResponse = auctionParticipation.getBidderResponse();
final BidderSeatBid seatBid = ObjectUtil.getIfNotNull(bidderResponse, BidderResponse::getSeatBid);
final List<BidderBid> bidderBids = ObjectUtil.getIfNotNull(seatBid, BidderSeatBid::getBids);
if (CollectionUtils.isEmpty(bidderBids)) {

final BidRequest bidderBidRequest = Optional.ofNullable(auctionParticipation.getBidderRequest())
.map(BidderRequest::getBidRequest)
.orElse(null);

if (CollectionUtils.isEmpty(bidderBids) || bidderBidRequest == null) {
return auctionParticipation;
}

final List<BidderBid> updatedBidderBids = new ArrayList<>(bidderBids);
final List<BidderError> errors = new ArrayList<>(seatBid.getErrors());
final List<BidderError> warnings = new ArrayList<>(seatBid.getWarnings());

final BidRequest bidderBidRequest = Optional.ofNullable(auctionParticipation.getBidderRequest())
.map(BidderRequest::getBidRequest)
.orElse(null);
final boolean enforceDealFloors = enforceDealFloors(auctionParticipation, account);

for (BidderBid bidderBid : bidderBids) {
Expand All @@ -157,7 +165,13 @@ private AuctionParticipation applyEnforcement(BidRequest bidRequest,
}

final BigDecimal price = bid.getPrice();
final BigDecimal floor = resolveFloor(bidderBid, bidderBidRequest, bidRequest, errors);
final BigDecimal floor = resolveFloor(
bidderResponse.getBidder(),
account,
bidderBid,
bidderBidRequest,
bidRequest,
errors);

if (isPriceBelowFloor(price, floor)) {
final String impId = bid.getImpid();
Expand Down Expand Up @@ -197,7 +211,9 @@ private static boolean enforceDealFloors(AuctionParticipation auctionParticipati
return BooleanUtils.isTrue(requestEnforceDealFloors) && BooleanUtils.isTrue(accountEnforceDealFloors);
}

private BigDecimal resolveFloor(BidderBid bidderBid,
private BigDecimal resolveFloor(String bidder,
Account account,
BidderBid bidderBid,
BidRequest bidderBidRequest,
BidRequest bidRequest,
List<BidderError> errors) {
Expand All @@ -210,9 +226,15 @@ private BigDecimal resolveFloor(BidderBid bidderBid,
return convertIfRequired(customBidderFloor, priceFloorInfo.getCurrency(), bidderBidRequest, bidRequest);
}

final Imp imp = correspondingImp(bidderBid.getBid(), bidRequest.getImp());
final Imp imp = correspondingImp(bidderBid.getBid(), bidderBidRequest.getImp());
final Price correctedImpFloor = priceFloorAdjuster.revertAdjustmentForImp(imp, bidder, bidRequest, account);
final String bidRequestCurrency = resolveBidRequestCurrency(bidRequest);
return convertCurrency(imp.getBidfloor(), bidRequest, imp.getBidfloorcur(), bidRequestCurrency);

return convertCurrency(
correctedImpFloor.getValue(),
bidRequest,
correctedImpFloor.getCurrency(),
bidRequestCurrency);
} catch (PreBidException e) {
final String logMessage = "Price floors enforcement failed for request id: %s, reason: %s"
.formatted(bidRequest.getId(), e.getMessage());
Expand Down
Loading

0 comments on commit feeccec

Please sign in to comment.