2121import org .prebid .server .log .ConditionalLogger ;
2222import org .prebid .server .log .Logger ;
2323import org .prebid .server .log .LoggerFactory ;
24+ import org .prebid .server .metric .MetricName ;
25+ import org .prebid .server .metric .Metrics ;
2426import org .prebid .server .proto .openrtb .ext .request .ExtImpPrebidFloors ;
2527import org .prebid .server .proto .openrtb .ext .request .ExtRequest ;
2628import org .prebid .server .proto .openrtb .ext .request .ExtRequestPrebid ;
@@ -50,19 +52,35 @@ public class BasicPriceFloorProcessor implements PriceFloorProcessor {
5052 private static final int MODEL_WEIGHT_MAX_VALUE = 100 ;
5153 private static final int MODEL_WEIGHT_MIN_VALUE = 1 ;
5254
55+ private static final String FETCH_FAILED_ERROR_MESSAGE = "Price floors processing failed: %s. "
56+ + "Following parsing of request price floors is failed: %s" ;
57+ private static final String DYNAMIC_DATA_NOT_ALLOWED_MESSAGE =
58+ "Price floors processing failed: Using dynamic data is not allowed. "
59+ + "Following parsing of request price floors is failed: %s" ;
60+ private static final String INVALID_REQUEST_WARNING_MESSAGE =
61+ "Price floors processing failed: parsing of request price floors is failed: %s" ;
62+ private static final String ERROR_LOG_MESSAGE =
63+ "Price Floors can't be resolved for account %s and request %s, reason: %s" ;
64+
5365 private final PriceFloorFetcher floorFetcher ;
5466 private final PriceFloorResolver floorResolver ;
67+ private final Metrics metrics ;
5568 private final JacksonMapper mapper ;
69+ private final double logSamplingRate ;
5670
5771 private final RandomWeightedEntrySupplier <PriceFloorModelGroup > modelPicker ;
5872
5973 public BasicPriceFloorProcessor (PriceFloorFetcher floorFetcher ,
6074 PriceFloorResolver floorResolver ,
61- JacksonMapper mapper ) {
75+ Metrics metrics ,
76+ JacksonMapper mapper ,
77+ double logSamplingRate ) {
6278
6379 this .floorFetcher = Objects .requireNonNull (floorFetcher );
6480 this .floorResolver = Objects .requireNonNull (floorResolver );
81+ this .metrics = Objects .requireNonNull (metrics );
6582 this .mapper = Objects .requireNonNull (mapper );
83+ this .logSamplingRate = logSamplingRate ;
6684
6785 modelPicker = new RandomPositiveWeightedEntrySupplier <>(BasicPriceFloorProcessor ::resolveModelGroupWeight );
6886 }
@@ -82,7 +100,7 @@ public BidRequest enrichWithPriceFloors(BidRequest bidRequest,
82100 return disableFloorsForRequest (bidRequest );
83101 }
84102
85- final PriceFloorRules floors = resolveFloors (account , bidRequest , errors );
103+ final PriceFloorRules floors = resolveFloors (account , bidRequest , warnings );
86104 return updateBidRequestWithFloors (bidRequest , bidder , floors , errors , warnings );
87105 }
88106
@@ -122,49 +140,13 @@ private static PriceFloorRules extractRequestFloors(BidRequest bidRequest) {
122140 return ObjectUtil .getIfNotNull (prebid , ExtRequestPrebid ::getFloors );
123141 }
124142
125- private PriceFloorRules resolveFloors (Account account , BidRequest bidRequest , List <String > errors ) {
143+ private PriceFloorRules resolveFloors (Account account , BidRequest bidRequest , List <String > warnings ) {
126144 final PriceFloorRules requestFloors = extractRequestFloors (bidRequest );
127145
128146 final FetchResult fetchResult = floorFetcher .fetch (account );
129- final FetchStatus fetchStatus = ObjectUtil .getIfNotNull (fetchResult , FetchResult ::getFetchStatus );
130-
131- if (fetchResult != null && fetchStatus == FetchStatus .success && shouldUseDynamicData (account , fetchResult )) {
132- final PriceFloorRules mergedFloors = mergeFloors (requestFloors , fetchResult .getRulesData ());
133- return createFloorsFrom (mergedFloors , fetchStatus , PriceFloorLocation .fetch );
134- }
147+ final FetchStatus fetchStatus = fetchResult .getFetchStatus ();
135148
136- if (requestFloors != null ) {
137- try {
138- final Optional <AccountPriceFloorsConfig > priceFloorsConfig = Optional .of (account )
139- .map (Account ::getAuction )
140- .map (AccountAuctionConfig ::getPriceFloors );
141-
142- final Long maxRules = priceFloorsConfig .map (AccountPriceFloorsConfig ::getMaxRules )
143- .orElse (null );
144- final Long maxDimensions = priceFloorsConfig .map (AccountPriceFloorsConfig ::getMaxSchemaDims )
145- .orElse (null );
146-
147- PriceFloorRulesValidator .validateRules (
148- requestFloors ,
149- PriceFloorsConfigResolver .resolveMaxValue (maxRules ),
150- PriceFloorsConfigResolver .resolveMaxValue (maxDimensions ));
151-
152- return createFloorsFrom (requestFloors , fetchStatus , PriceFloorLocation .request );
153- } catch (PreBidException e ) {
154- errors .add ("Failed to parse price floors from request, with a reason: %s" .formatted (e .getMessage ()));
155- conditionalLogger .error (
156- "Failed to parse price floors from request with id: '%s', with a reason: %s"
157- .formatted (bidRequest .getId (), e .getMessage ()),
158- 0.01d );
159- }
160- }
161-
162- return createFloorsFrom (null , fetchStatus , PriceFloorLocation .noData );
163- }
164-
165- private static boolean shouldUseDynamicData (Account account , FetchResult fetchResult ) {
166- final boolean isUsingDynamicDataAllowed = Optional .of (account )
167- .map (Account ::getAuction )
149+ final boolean isUsingDynamicDataAllowed = Optional .ofNullable (account .getAuction ())
168150 .map (AccountAuctionConfig ::getPriceFloors )
169151 .map (AccountPriceFloorsConfig ::getUseDynamicData )
170152 .map (BooleanUtils ::isNotFalse )
@@ -175,12 +157,68 @@ private static boolean shouldUseDynamicData(Account account, FetchResult fetchRe
175157 .map (rate -> ThreadLocalRandom .current ().nextInt (USE_FETCH_DATA_RATE_MAX ) < rate )
176158 .orElse (true );
177159
178- return isUsingDynamicDataAllowed && shouldUseDynamicData ;
160+ if (fetchStatus == FetchStatus .success && isUsingDynamicDataAllowed && shouldUseDynamicData ) {
161+ final PriceFloorRules mergedFloors = mergeFloors (requestFloors , fetchResult .getRulesData ());
162+ return createFloorsFrom (mergedFloors , fetchStatus , PriceFloorLocation .fetch );
163+ }
164+
165+ return requestFloors == null
166+ ? createFloorsFrom (null , fetchStatus , PriceFloorLocation .noData )
167+ : getPriceFloorRules (
168+ bidRequest , account , requestFloors , fetchResult , isUsingDynamicDataAllowed , warnings );
169+ }
170+
171+ private PriceFloorRules getPriceFloorRules (BidRequest bidRequest ,
172+ Account account ,
173+ PriceFloorRules requestFloors ,
174+ FetchResult fetchResult ,
175+ boolean isDynamicDataAllowed ,
176+ List <String > warnings ) {
177+
178+ try {
179+ final Optional <AccountPriceFloorsConfig > priceFloorsConfig = Optional .of (account .getAuction ())
180+ .map (AccountAuctionConfig ::getPriceFloors );
181+
182+ final Long maxRules = priceFloorsConfig .map (AccountPriceFloorsConfig ::getMaxRules )
183+ .orElse (null );
184+ final Long maxDimensions = priceFloorsConfig .map (AccountPriceFloorsConfig ::getMaxSchemaDims )
185+ .orElse (null );
186+
187+ PriceFloorRulesValidator .validateRules (
188+ requestFloors ,
189+ PriceFloorsConfigResolver .resolveMaxValue (maxRules ),
190+ PriceFloorsConfigResolver .resolveMaxValue (maxDimensions ));
191+
192+ return createFloorsFrom (requestFloors , fetchResult .getFetchStatus (), PriceFloorLocation .request );
193+ } catch (PreBidException e ) {
194+ logErrorMessage (fetchResult , isDynamicDataAllowed , e , account .getId (), bidRequest .getId (), warnings );
195+ return createFloorsFrom (null , fetchResult .getFetchStatus (), PriceFloorLocation .noData );
196+ }
179197 }
180198
181- private PriceFloorRules mergeFloors (PriceFloorRules requestFloors ,
182- PriceFloorData providerRulesData ) {
199+ private void logErrorMessage (FetchResult fetchResult ,
200+ boolean isDynamicDataAllowed ,
201+ PreBidException requestFloorsValidationException ,
202+ String accountId ,
203+ String requestId ,
204+ List <String > warnings ) {
205+
206+ final String validationMessage = requestFloorsValidationException .getMessage ();
207+ final String errorMessage = switch (fetchResult .getFetchStatus ()) {
208+ case inprogress -> null ;
209+ case error , timeout , none -> FETCH_FAILED_ERROR_MESSAGE .formatted (
210+ fetchResult .getErrorMessage (), validationMessage );
211+ case success -> isDynamicDataAllowed ? null : DYNAMIC_DATA_NOT_ALLOWED_MESSAGE .formatted (validationMessage );
212+ };
213+
214+ if (errorMessage != null ) {
215+ warnings .add (INVALID_REQUEST_WARNING_MESSAGE .formatted (validationMessage ));
216+ conditionalLogger .error (ERROR_LOG_MESSAGE .formatted (accountId , requestId , errorMessage ), logSamplingRate );
217+ metrics .updateAlertsMetrics (MetricName .general );
218+ }
219+ }
183220
221+ private PriceFloorRules mergeFloors (PriceFloorRules requestFloors , PriceFloorData providerRulesData ) {
184222 final Price floorMinPrice = resolveFloorMinPrice (requestFloors );
185223
186224 return (requestFloors != null ? requestFloors .toBuilder () : PriceFloorRules .builder ())
0 commit comments