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 ;
@@ -52,17 +54,23 @@ public class BasicPriceFloorProcessor implements PriceFloorProcessor {
5254
5355 private final PriceFloorFetcher floorFetcher ;
5456 private final PriceFloorResolver floorResolver ;
57+ private final Metrics metrics ;
5558 private final JacksonMapper mapper ;
59+ private final double logSamplingRate ;
5660
5761 private final RandomWeightedEntrySupplier <PriceFloorModelGroup > modelPicker ;
5862
5963 public BasicPriceFloorProcessor (PriceFloorFetcher floorFetcher ,
6064 PriceFloorResolver floorResolver ,
61- JacksonMapper mapper ) {
65+ Metrics metrics ,
66+ JacksonMapper mapper ,
67+ double logSamplingRate ) {
6268
6369 this .floorFetcher = Objects .requireNonNull (floorFetcher );
6470 this .floorResolver = Objects .requireNonNull (floorResolver );
71+ this .metrics = Objects .requireNonNull (metrics );
6572 this .mapper = Objects .requireNonNull (mapper );
73+ this .logSamplingRate = logSamplingRate ;
6674
6775 modelPicker = new RandomPositiveWeightedEntrySupplier <>(BasicPriceFloorProcessor ::resolveModelGroupWeight );
6876 }
@@ -82,7 +90,7 @@ public BidRequest enrichWithPriceFloors(BidRequest bidRequest,
8290 return disableFloorsForRequest (bidRequest );
8391 }
8492
85- final PriceFloorRules floors = resolveFloors (account , bidRequest , errors );
93+ final PriceFloorRules floors = resolveFloors (account , bidRequest , warnings );
8694 return updateBidRequestWithFloors (bidRequest , bidder , floors , errors , warnings );
8795 }
8896
@@ -122,49 +130,13 @@ private static PriceFloorRules extractRequestFloors(BidRequest bidRequest) {
122130 return ObjectUtil .getIfNotNull (prebid , ExtRequestPrebid ::getFloors );
123131 }
124132
125- private PriceFloorRules resolveFloors (Account account , BidRequest bidRequest , List <String > errors ) {
133+ private PriceFloorRules resolveFloors (Account account , BidRequest bidRequest , List <String > warnings ) {
126134 final PriceFloorRules requestFloors = extractRequestFloors (bidRequest );
127135
128136 final FetchResult fetchResult = floorFetcher .fetch (account );
129- final FetchStatus fetchStatus = ObjectUtil . getIfNotNull ( fetchResult , FetchResult :: getFetchStatus );
137+ final FetchStatus fetchStatus = fetchResult . getFetchStatus ( );
130138
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- }
135-
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 )
139+ final boolean isUsingDynamicDataAllowed = Optional .ofNullable (account .getAuction ())
168140 .map (AccountAuctionConfig ::getPriceFloors )
169141 .map (AccountPriceFloorsConfig ::getUseDynamicData )
170142 .map (BooleanUtils ::isNotFalse )
@@ -175,12 +147,73 @@ private static boolean shouldUseDynamicData(Account account, FetchResult fetchRe
175147 .map (rate -> ThreadLocalRandom .current ().nextInt (USE_FETCH_DATA_RATE_MAX ) < rate )
176148 .orElse (true );
177149
178- return isUsingDynamicDataAllowed && shouldUseDynamicData ;
150+ if (fetchStatus == FetchStatus .success && isUsingDynamicDataAllowed && shouldUseDynamicData ) {
151+ final PriceFloorRules mergedFloors = mergeFloors (requestFloors , fetchResult .getRulesData ());
152+ return createFloorsFrom (mergedFloors , fetchStatus , PriceFloorLocation .fetch );
153+ }
154+
155+ final String fetchErrorMessage = resolveFetchErrorMessage (fetchResult , isUsingDynamicDataAllowed );
156+ return requestFloors == null
157+ ? noPriceFloorData (fetchStatus , account .getId (), bidRequest .getId (), fetchErrorMessage , warnings )
158+ : getPriceFloorRules (bidRequest , account , requestFloors , fetchStatus , fetchErrorMessage , warnings );
159+ }
160+
161+ private static String resolveFetchErrorMessage (FetchResult fetchResult , boolean isUsingDynamicDataAllowed ) {
162+ return switch (fetchResult .getFetchStatus ()) {
163+ case inprogress -> null ;
164+ case error , timeout , none -> fetchResult .getErrorMessage ();
165+ case success -> isUsingDynamicDataAllowed ? null : "Using dynamic data is not allowed" ;
166+ };
167+ }
168+
169+ private PriceFloorRules noPriceFloorData (FetchStatus fetchStatus ,
170+ String accountId ,
171+ String requestId ,
172+ String errorMessage ,
173+ List <String > warnings ) {
174+
175+ if (errorMessage != null ) {
176+ warnings .add (errorMessage );
177+ conditionalLogger .error ("No price floor data for account %s and request %s, reason: %s"
178+ .formatted (accountId , requestId , errorMessage ), logSamplingRate );
179+ metrics .updateAlertsMetrics (MetricName .general );
180+ }
181+
182+ return createFloorsFrom (null , fetchStatus , PriceFloorLocation .noData );
179183 }
180184
181- private PriceFloorRules mergeFloors (PriceFloorRules requestFloors ,
182- PriceFloorData providerRulesData ) {
185+ private PriceFloorRules getPriceFloorRules (BidRequest bidRequest ,
186+ Account account ,
187+ PriceFloorRules requestFloors ,
188+ FetchStatus fetchStatus ,
189+ String fetchErrorMessage ,
190+ List <String > warnings ) {
191+
192+ try {
193+ final Optional <AccountPriceFloorsConfig > priceFloorsConfig = Optional .of (account .getAuction ())
194+ .map (AccountAuctionConfig ::getPriceFloors );
195+
196+ final Long maxRules = priceFloorsConfig .map (AccountPriceFloorsConfig ::getMaxRules )
197+ .orElse (null );
198+ final Long maxDimensions = priceFloorsConfig .map (AccountPriceFloorsConfig ::getMaxSchemaDims )
199+ .orElse (null );
200+
201+ PriceFloorRulesValidator .validateRules (
202+ requestFloors ,
203+ PriceFloorsConfigResolver .resolveMaxValue (maxRules ),
204+ PriceFloorsConfigResolver .resolveMaxValue (maxDimensions ));
205+
206+ return createFloorsFrom (requestFloors , fetchStatus , PriceFloorLocation .request );
207+ } catch (PreBidException e ) {
208+ final String errorMessage = fetchErrorMessage == null
209+ ? null
210+ : "%s. Following parsing of request price floors is failed: %s"
211+ .formatted (fetchErrorMessage , e .getMessage ());
212+ return noPriceFloorData (fetchStatus , account .getId (), bidRequest .getId (), errorMessage , warnings );
213+ }
214+ }
183215
216+ private PriceFloorRules mergeFloors (PriceFloorRules requestFloors , PriceFloorData providerRulesData ) {
184217 final Price floorMinPrice = resolveFloorMinPrice (requestFloors );
185218
186219 return (requestFloors != null ? requestFloors .toBuilder () : PriceFloorRules .builder ())
0 commit comments