From 47359d2efc0d753f6e1ec01159a57cd908d03053 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 9 Apr 2026 01:14:47 +0300 Subject: [PATCH 01/18] Bumped vertx to 5.0 --- extra/pom.xml | 2 +- .../server/floors/PriceFloorFetcher.java | 4 +- ...rcuitBreakerSecuredGeoLocationService.java | 2 +- .../prebid/server/handler/SetuidHandler.java | 7 +- .../org/prebid/server/json/JacksonMapper.java | 5 +- .../prebid/server/log/CriteriaLogManager.java | 4 +- .../gdpr/vendorlist/VendorListService.java | 19 +-- .../spring/config/ServiceConfiguration.java | 9 +- .../server/spring/config/VerticleStarter.java | 5 +- .../database/DatabaseConfiguration.java | 37 ++--- .../server/admin/AdminServerAuthProvider.java | 27 ++-- .../ApplicationServerConfiguration.java | 2 +- .../java/org/prebid/server/util/HttpUtil.java | 7 +- .../prebid/server/vertx/CircuitBreaker.java | 77 +--------- .../prebid/server/vertx/CloseableAdapter.java | 9 +- .../prebid/server/vertx/ContextRunner.java | 5 + .../CircuitBreakerSecuredDatabaseClient.java | 3 +- .../CircuitBreakerSecuredHttpClient.java | 8 +- .../verticles/server/ServerVerticle.java | 22 +-- .../bidder/criteo/CriteoBidderTest.java | 6 +- .../bidder/gothamads/GothamAdsBidderTest.java | 6 +- .../server/bidder/pwbid/PwbidBidderTest.java | 6 +- .../cookie/CookieDeprecationServiceTest.java | 16 ++- .../server/cookie/UidsCookieServiceTest.java | 121 +++++++++------- .../file/syncer/RemoteFileSyncerTest.java | 8 +- ...tBreakerSecuredGeoLocationServiceTest.java | 6 +- .../server/handler/SetuidHandlerTest.java | 135 +++++++----------- .../execution/HookStageExecutorTest.java | 6 +- .../server/log/ConditionalLoggerTest.java | 5 +- .../vendorlist/VendorListServiceTest.java | 42 ++---- .../settings/S3ApplicationSettingsTest.java | 6 +- .../service/S3PeriodicRefreshServiceTest.java | 6 +- .../org/prebid/server/util/HttpUtilTest.java | 8 +- .../server/vertx/CircuitBreakerTest.java | 13 +- .../server/vertx/CloseableAdapterTest.java | 4 +- ...rcuitBreakerSecuredDatabaseClientTest.java | 6 +- .../vertx/httpclient/BasicHttpClientTest.java | 8 +- .../CircuitBreakerSecuredHttpClientTest.java | 8 +- 38 files changed, 297 insertions(+), 373 deletions(-) diff --git a/extra/pom.xml b/extra/pom.xml index 1a43c1b8ace..26214b9d33f 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -34,7 +34,7 @@ 3.5.10 - 4.5.20 + 5.0.10 2.0.1.Final 4.4 1.27.1 diff --git a/src/main/java/org/prebid/server/floors/PriceFloorFetcher.java b/src/main/java/org/prebid/server/floors/PriceFloorFetcher.java index b1cc8c257b2..bbaeec77a4c 100644 --- a/src/main/java/org/prebid/server/floors/PriceFloorFetcher.java +++ b/src/main/java/org/prebid/server/floors/PriceFloorFetcher.java @@ -5,7 +5,6 @@ import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.http.HttpHeaders; -import io.vertx.core.impl.ConcurrentHashSet; import lombok.Value; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; @@ -37,6 +36,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; @@ -79,7 +79,7 @@ public PriceFloorFetcher(ApplicationSettings applicationSettings, this.debugProperties = debugProperties; this.mapper = Objects.requireNonNull(mapper); - fetchInProgress = new ConcurrentHashSet<>(); + fetchInProgress = ConcurrentHashMap.newKeySet(); fetchedData = Caffeine.newBuilder() .maximumSize(MAXIMUM_CACHE_SIZE) .build() diff --git a/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java b/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java index f0fc0be3574..c8f04701de6 100755 --- a/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java +++ b/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java @@ -49,7 +49,7 @@ public CircuitBreakerSecuredGeoLocationService(Vertx vertx, @Override public Future lookup(String ip, Timeout timeout) { - return breaker.execute(promise -> geoLocationService.lookup(ip, timeout).onComplete(promise)); + return breaker.execute(() -> geoLocationService.lookup(ip, timeout)); } private void circuitOpened() { diff --git a/src/main/java/org/prebid/server/handler/SetuidHandler.java b/src/main/java/org/prebid/server/handler/SetuidHandler.java index bce568db2d7..fb2e2bb581e 100644 --- a/src/main/java/org/prebid/server/handler/SetuidHandler.java +++ b/src/main/java/org/prebid/server/handler/SetuidHandler.java @@ -4,7 +4,6 @@ import io.vertx.core.AsyncResult; import io.vertx.core.CompositeFuture; import io.vertx.core.Future; -import io.vertx.core.http.Cookie; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerRequest; @@ -333,7 +332,7 @@ private void respondWithCookie(SetuidContext setuidContext) { setuidContext.getUidsCookie(), bidder, uid); uidsCookieService.splitUidsIntoCookies(uidsCookieUpdateResult.getValue()) - .forEach(cookie -> addCookie(routingContext, cookie)); + .forEach(routingContext.response()::addCookie); if (uidsCookieUpdateResult.isUpdated()) { metrics.updateUserSyncSetsMetric(bidder); @@ -412,8 +411,4 @@ private void handleErrors(Throwable error, RoutingContext routingContext, TcfCon analyticsDelegator.processEvent(setuidEvent, tcfContext); } } - - private void addCookie(RoutingContext routingContext, Cookie cookie) { - routingContext.response().headers().add(HttpUtil.SET_COOKIE_HEADER, cookie.encode()); - } } diff --git a/src/main/java/org/prebid/server/json/JacksonMapper.java b/src/main/java/org/prebid/server/json/JacksonMapper.java index ec6d7f94d88..a56160e7aa8 100644 --- a/src/main/java/org/prebid/server/json/JacksonMapper.java +++ b/src/main/java/org/prebid/server/json/JacksonMapper.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.buffer.ByteBufInputStream; import io.vertx.core.buffer.Buffer; +import io.vertx.core.internal.buffer.BufferInternal; import org.prebid.server.proto.openrtb.ext.FlexibleExtension; import java.io.IOException; @@ -66,7 +67,7 @@ public T decodeValue(String str, TypeReference type) throws DecodeExcepti public T decodeValue(Buffer buf, Class clazz) throws DecodeException { try { - return mapper.readValue((InputStream) new ByteBufInputStream(buf.getByteBuf()), clazz); + return mapper.readValue((InputStream) new ByteBufInputStream(((BufferInternal) buf).getByteBuf()), clazz); } catch (IOException e) { throw new DecodeException(FAILED_TO_DECODE.formatted(e.getMessage()), e); } @@ -74,7 +75,7 @@ public T decodeValue(Buffer buf, Class clazz) throws DecodeException { public T decodeValue(Buffer buf, TypeReference type) throws DecodeException { try { - return mapper.readValue(new ByteBufInputStream(buf.getByteBuf()), type); + return mapper.readValue(new ByteBufInputStream(((BufferInternal) buf).getByteBuf()), type); } catch (IOException e) { throw new DecodeException(FAILED_TO_DECODE.formatted(e.getMessage()), e); } diff --git a/src/main/java/org/prebid/server/log/CriteriaLogManager.java b/src/main/java/org/prebid/server/log/CriteriaLogManager.java index 75ca28f5f81..58e1f377ce9 100644 --- a/src/main/java/org/prebid/server/log/CriteriaLogManager.java +++ b/src/main/java/org/prebid/server/log/CriteriaLogManager.java @@ -2,18 +2,18 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.response.BidResponse; -import io.vertx.core.impl.ConcurrentHashSet; import org.prebid.server.json.EncodeException; import org.prebid.server.json.JacksonMapper; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; public class CriteriaLogManager { private static final Logger logger = LoggerFactory.getLogger(CriteriaLogManager.class); - private final Set criterias = new ConcurrentHashSet<>(); + private final Set criterias = ConcurrentHashMap.newKeySet(); private final JacksonMapper mapper; diff --git a/src/main/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListService.java b/src/main/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListService.java index 51c9b3e9252..2468828f9e9 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListService.java @@ -3,7 +3,6 @@ import com.github.benmanes.caffeine.cache.Caffeine; import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.file.FileProps; @@ -317,23 +316,15 @@ private VendorListResult processResponse(HttpClientResponse response * Saves given vendor list on file system. */ private Future> saveToFile(VendorListResult vendorListResult) { - final Promise> promise = Promise.promise(); final int version = vendorListResult.getVersion(); final String filepath = new File(cacheDir, version + JSON_SUFFIX).getPath(); - fileSystem.writeFile(filepath, Buffer.buffer(vendorListResult.getVendorListAsString()), result -> { - if (result.succeeded()) { - promise.complete(vendorListResult); - } else { - conditionalLogger.error( + return fileSystem.writeFile(filepath, Buffer.buffer(vendorListResult.getVendorListAsString())) + .map(vendorListResult) + .onFailure(error -> conditionalLogger.error( "Could not create new vendor list for version %s.%s, file: %s, trace: %s".formatted( - generationVersion, version, filepath, ExceptionUtils.getStackTrace(result.cause())), - logSamplingRate); - promise.fail(result.cause()); - } - }); - - return promise.future(); + generationVersion, version, filepath, ExceptionUtils.getStackTrace(error.getCause())), + logSamplingRate)); } private Void updateCache(VendorListResult vendorListResult) { diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java index 64a8dc7614a..024f6888b09 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -5,6 +5,7 @@ import io.vertx.core.Vertx; import io.vertx.core.file.FileSystem; import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.PoolOptions; import io.vertx.core.net.JksOptions; import lombok.Data; import org.apache.commons.lang3.ObjectUtils; @@ -676,11 +677,13 @@ CircuitBreakerSecuredHttpClient circuitBreakerSecuredHttpClient( } private static BasicHttpClient createBasicHttpClient(Vertx vertx, HttpClientProperties httpClientProperties) { + final PoolOptions poolOptions = new PoolOptions() + .setHttp1MaxSize(httpClientProperties.getMaxPoolSize()) + .setCleanerPeriod(httpClientProperties.getPoolCleanerPeriodMs()); + final HttpClientOptions options = new HttpClientOptions() - .setMaxPoolSize(httpClientProperties.getMaxPoolSize()) .setIdleTimeoutUnit(TimeUnit.MILLISECONDS) .setIdleTimeout(httpClientProperties.getIdleTimeoutMs()) - .setPoolCleanerPeriod(httpClientProperties.getPoolCleanerPeriodMs()) .setDecompressionSupported(httpClientProperties.getUseCompression()) .setConnectTimeout(httpClientProperties.getConnectTimeoutMs()) // Vert.x's HttpClientRequest needs this value to be 2 for redirections to be followed once, @@ -697,7 +700,7 @@ private static BasicHttpClient createBasicHttpClient(Vertx vertx, HttpClientProp .setKeyCertOptions(jksOptions); } - return new BasicHttpClient(vertx, vertx.createHttpClient(options)); + return new BasicHttpClient(vertx, vertx.createHttpClient(options, poolOptions)); } @Bean diff --git a/src/main/java/org/prebid/server/spring/config/VerticleStarter.java b/src/main/java/org/prebid/server/spring/config/VerticleStarter.java index cb519b88477..ae6500ba766 100644 --- a/src/main/java/org/prebid/server/spring/config/VerticleStarter.java +++ b/src/main/java/org/prebid/server/spring/config/VerticleStarter.java @@ -30,11 +30,10 @@ public void start() { continue; } - contextRunner.runBlocking(promise -> + contextRunner.runBlocking(() -> vertx.deployVerticle( definition.getFactory(), - new DeploymentOptions().setInstances(definition.getAmount()), - promise)); + new DeploymentOptions().setInstances(definition.getAmount()))); } } } diff --git a/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java b/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java index 6a1a1531f1c..770bf56bafe 100644 --- a/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java @@ -1,6 +1,7 @@ package org.prebid.server.spring.config.database; import io.vertx.core.Vertx; +import io.vertx.core.net.NetClientOptions; import io.vertx.mysqlclient.MySQLBuilder; import io.vertx.mysqlclient.MySQLConnectOptions; import io.vertx.pgclient.PgBuilder; @@ -84,19 +85,20 @@ Pool mysqlConnectionPool(Vertx vertx, .setDatabase(databaseAddress.getDatabaseName()) .setUser(connectionPoolSettings.getUser()) .setPassword(connectionPoolSettings.getPassword()) - .setSsl(false) - .setTcpKeepAlive(true) .setCachePreparedStatements(connectionPoolSettings.getEnablePreparedStatementCaching()) - .setPreparedStatementCacheMaxSize(connectionPoolSettings.getMaxPreparedStatementCacheSize()) - .setIdleTimeout(connectionPoolSettings.getIdleTimeout()) - .setIdleTimeoutUnit(TimeUnit.SECONDS); + .setPreparedStatementCacheMaxSize(connectionPoolSettings.getMaxPreparedStatementCacheSize()); + + final NetClientOptions netClientOptions = new NetClientOptions() + .setTcpKeepAlive(true); final PoolOptions poolOptions = new PoolOptions() - .setMaxSize(connectionPoolSettings.getPoolSize()); + .setMaxSize(connectionPoolSettings.getPoolSize()) + .setIdleTimeout(connectionPoolSettings.getIdleTimeout()) + .setIdleTimeoutUnit(TimeUnit.SECONDS); - return MySQLBuilder - .pool() + return MySQLBuilder.pool() .with(poolOptions) + .with(netClientOptions) .connectingTo(sqlConnectOptions) .using(vertx) .build(); @@ -114,19 +116,20 @@ Pool postgresConnectionPool(Vertx vertx, .setDatabase(databaseAddress.getDatabaseName()) .setUser(connectionPoolSettings.getUser()) .setPassword(connectionPoolSettings.getPassword()) - .setSsl(false) - .setTcpKeepAlive(true) .setCachePreparedStatements(connectionPoolSettings.getEnablePreparedStatementCaching()) - .setPreparedStatementCacheMaxSize(connectionPoolSettings.getMaxPreparedStatementCacheSize()) - .setIdleTimeout(connectionPoolSettings.getIdleTimeout()) - .setIdleTimeoutUnit(TimeUnit.SECONDS); + .setPreparedStatementCacheMaxSize(connectionPoolSettings.getMaxPreparedStatementCacheSize()); + + final NetClientOptions netClientOptions = new NetClientOptions() + .setTcpKeepAlive(true); final PoolOptions poolOptions = new PoolOptions() - .setMaxSize(connectionPoolSettings.getPoolSize()); + .setMaxSize(connectionPoolSettings.getPoolSize()) + .setIdleTimeout(connectionPoolSettings.getIdleTimeout()) + .setIdleTimeoutUnit(TimeUnit.SECONDS); - return PgBuilder - .pool() + return PgBuilder.pool() .with(poolOptions) + .with(netClientOptions) .connectingTo(sqlConnectOptions) .using(vertx) .build(); @@ -175,7 +178,7 @@ private static BasicDatabaseClient createBasicDatabaseClient(Pool pool, final BasicDatabaseClient basicDatabaseClient = new BasicDatabaseClient(pool, metrics, clock); - contextRunner.runBlocking(promise -> basicDatabaseClient.initialize().onComplete(promise)); + contextRunner.runBlocking(basicDatabaseClient::initialize); return basicDatabaseClient; } diff --git a/src/main/java/org/prebid/server/spring/config/server/admin/AdminServerAuthProvider.java b/src/main/java/org/prebid/server/spring/config/server/admin/AdminServerAuthProvider.java index f95980d1a60..6796f7e13f6 100644 --- a/src/main/java/org/prebid/server/spring/config/server/admin/AdminServerAuthProvider.java +++ b/src/main/java/org/prebid/server/spring/config/server/admin/AdminServerAuthProvider.java @@ -1,17 +1,16 @@ package org.prebid.server.spring.config.server.admin; -import io.vertx.core.AsyncResult; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; -import io.vertx.ext.auth.AuthProvider; import io.vertx.ext.auth.User; +import io.vertx.ext.auth.authentication.AuthenticationProvider; +import io.vertx.ext.auth.authentication.Credentials; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import java.util.Map; -public class AdminServerAuthProvider implements AuthProvider { +public class AdminServerAuthProvider implements AuthenticationProvider { private final Map credentials; @@ -20,20 +19,18 @@ public AdminServerAuthProvider(Map credentials) { } @Override - public void authenticate(JsonObject authInfo, Handler> resultHandler) { + public Future authenticate(Credentials userCredentials) { if (MapUtils.isEmpty(credentials)) { - resultHandler.handle(Future.failedFuture("Credentials not set in configuration.")); - return; + return Future.failedFuture("Credentials not set in configuration."); } - final String requestUsername = authInfo.getString("username"); - final String requestPassword = StringUtils.chomp(authInfo.getString("password")); - + final JsonObject principal = userCredentials.toJson(); + final String requestUsername = principal.getString("username"); + final String requestPassword = StringUtils.chomp(principal.getString("password")); final String storedPassword = credentials.get(requestUsername); - if (StringUtils.isNotBlank(requestPassword) && StringUtils.equals(storedPassword, requestPassword)) { - resultHandler.handle(Future.succeededFuture()); - } else { - resultHandler.handle(Future.failedFuture("No such user, or password incorrect.")); - } + + return StringUtils.isNotBlank(requestPassword) && StringUtils.equals(storedPassword, requestPassword) + ? Future.succeededFuture() + : Future.failedFuture("Password does not match."); } } diff --git a/src/main/java/org/prebid/server/spring/config/server/application/ApplicationServerConfiguration.java b/src/main/java/org/prebid/server/spring/config/server/application/ApplicationServerConfiguration.java index c6ae167ae94..a6b56622c42 100644 --- a/src/main/java/org/prebid/server/spring/config/server/application/ApplicationServerConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/server/application/ApplicationServerConfiguration.java @@ -197,7 +197,7 @@ NoCacheHandler noCacheHandler() { @Bean CorsHandler corsHandler() { return CorsHandler.create() - .addRelativeOrigin(".*") + .addOriginWithRegex(".*") .allowCredentials(true) .allowedHeaders(new HashSet<>(Arrays.asList( HttpUtil.ORIGIN_HEADER.toString(), diff --git a/src/main/java/org/prebid/server/util/HttpUtil.java b/src/main/java/org/prebid/server/util/HttpUtil.java index e08a276c6fa..95b686abf9b 100644 --- a/src/main/java/org/prebid/server/util/HttpUtil.java +++ b/src/main/java/org/prebid/server/util/HttpUtil.java @@ -181,13 +181,12 @@ public static Map cookiesAsMap(HttpRequestContext httpRequest) { } public static Map cookiesAsMap(RoutingContext routingContext) { - return routingContext.cookieMap().entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getValue())); + return routingContext.request().cookies().stream() + .collect(Collectors.toMap(Cookie::getName, Cookie::getValue)); } public static String createCookiesHeader(RoutingContext routingContext) { - return routingContext.cookieMap().entrySet().stream() - .map(entry -> Cookie.cookie(entry.getKey(), entry.getValue().getValue())) + return routingContext.request().cookies().stream() .map(Cookie::encode) .collect(Collectors.joining("; ")); } diff --git a/src/main/java/org/prebid/server/vertx/CircuitBreaker.java b/src/main/java/org/prebid/server/vertx/CircuitBreaker.java index 104545470f5..02d30a8f76d 100644 --- a/src/main/java/org/prebid/server/vertx/CircuitBreaker.java +++ b/src/main/java/org/prebid/server/vertx/CircuitBreaker.java @@ -1,16 +1,13 @@ package org.prebid.server.vertx; import io.vertx.circuitbreaker.CircuitBreakerOptions; -import io.vertx.circuitbreaker.CircuitBreakerState; import io.vertx.core.Future; import io.vertx.core.Handler; -import io.vertx.core.Promise; import io.vertx.core.Vertx; -import org.prebid.server.log.Logger; -import org.prebid.server.log.LoggerFactory; import java.time.Clock; import java.util.Objects; +import java.util.function.Supplier; /** * Wrapper over Vert.x {@link io.vertx.circuitbreaker.CircuitBreaker} with functionality @@ -18,14 +15,7 @@ */ public class CircuitBreaker { - private static final Logger logger = LoggerFactory.getLogger(CircuitBreaker.class); - private final io.vertx.circuitbreaker.CircuitBreaker breaker; - private final Vertx vertx; - private final long openingIntervalMs; - private final Clock clock; - - private volatile long lastFailureTime; public CircuitBreaker(String name, Vertx vertx, @@ -41,74 +31,13 @@ public CircuitBreaker(String name, .setNotificationPeriod(0) .setMaxFailures(openingThreshold) .setResetTimeout(closingIntervalMs)); - - this.vertx = vertx; - this.openingIntervalMs = openingIntervalMs; - this.clock = Objects.requireNonNull(clock); } /** * Executes the given operation with the circuit breaker control. */ - public Future execute(Handler> command) { - return breaker.execute(promise -> execute(command, promise)); - } - - /** - * Executes operation and handle result of it on given {@link Promise}. - */ - private void execute(Handler> command, Promise promise) { - final Promise passedPromise = Promise.promise(); - command.handle(passedPromise); - - passedPromise.future() - .compose(response -> succeedBreaker(response, promise)) - .recover(exception -> failBreaker(exception, promise)); - } - - /** - * Succeeds given {@link Promise} and returns corresponding {@link Future}. - */ - private static Future succeedBreaker(T result, Promise promise) { - promise.complete(result); - return promise.future(); - } - - /** - * Fails given {@link Promise} and returns corresponding {@link Future}. - */ - private Future failBreaker(Throwable exception, Promise promise) { - final Promise ensureStatePromise = Promise.promise(); - vertx.executeBlocking(this::ensureState, false, ensureStatePromise); - - return ensureStatePromise.future() - .recover(throwable -> { - logger.warn("Resetting circuit breaker state failed", throwable); - promise.fail(throwable); - return promise.future(); - }) - .compose(ignored -> { // ensuring state succeeded, propagate real error - promise.fail(exception); - return promise.future(); - }); - } - - /** - * Resets failure counter to adjust open-circuit time frame. - *

- * Note: the operations {@link io.vertx.circuitbreaker.CircuitBreaker#state()} - * and {@link io.vertx.circuitbreaker.CircuitBreaker#reset()} can take a while, - * so it is better to perform them on a worker thread. - */ - private void ensureState(Promise executeBlockingPromise) { - final long currentTime = clock.millis(); - if (breaker.state() == CircuitBreakerState.CLOSED && lastFailureTime > 0 - && currentTime - lastFailureTime > openingIntervalMs) { - breaker.reset(); - } - - lastFailureTime = currentTime; - executeBlockingPromise.complete(); + public Future execute(Supplier> command) { + return breaker.execute(command); } /** diff --git a/src/main/java/org/prebid/server/vertx/CloseableAdapter.java b/src/main/java/org/prebid/server/vertx/CloseableAdapter.java index 708796c63c7..6f714f8f0ae 100644 --- a/src/main/java/org/prebid/server/vertx/CloseableAdapter.java +++ b/src/main/java/org/prebid/server/vertx/CloseableAdapter.java @@ -1,7 +1,6 @@ package org.prebid.server.vertx; -import io.vertx.core.Future; -import io.vertx.core.Promise; +import io.vertx.core.Completable; import java.io.Closeable; import java.io.IOException; @@ -20,12 +19,12 @@ public CloseableAdapter(Closeable closeable) { } @Override - public void close(Promise promise) { + public void close(Completable completable) { try { closeable.close(); - promise.handle(Future.succeededFuture()); + completable.succeed(); } catch (IOException e) { - promise.handle(Future.failedFuture(e)); + completable.fail(e); } } } diff --git a/src/main/java/org/prebid/server/vertx/ContextRunner.java b/src/main/java/org/prebid/server/vertx/ContextRunner.java index 43dc35c3683..1e59fc394e9 100644 --- a/src/main/java/org/prebid/server/vertx/ContextRunner.java +++ b/src/main/java/org/prebid/server/vertx/ContextRunner.java @@ -7,6 +7,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; public class ContextRunner { @@ -18,6 +19,10 @@ public ContextRunner(Vertx vertx, long timeoutMs) { this.timeoutMs = timeoutMs; } + public void runBlocking(Supplier> action) { + runBlocking(promise -> action.get().onComplete(promise)); + } + public void runBlocking(Handler> action) { final CountDownLatch completionLatch = new CountDownLatch(1); final Promise promise = Promise.promise(); diff --git a/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java b/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java index ea59c9f9670..36cd9de6b26 100644 --- a/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java +++ b/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java @@ -61,8 +61,7 @@ public Future executeQuery(String query, Function, T> mapper, Timeout timeout) { - return breaker.execute( - promise -> databaseClient.executeQuery(query, params, mapper, timeout).onComplete(promise)); + return breaker.execute(() -> databaseClient.executeQuery(query, params, mapper, timeout)); } private void circuitOpened() { diff --git a/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java b/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java index 0843a04de12..5cbee944a75 100644 --- a/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java +++ b/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java @@ -69,9 +69,7 @@ public Future request(HttpMethod method, long maxResponseSize) { return circuitBreakerByName.computeIfAbsent(nameFrom(url), circuitBreakerCreator) - .execute(promise -> - httpClient.request(method, url, headers, body, timeoutMs, maxResponseSize) - .onComplete(promise)); + .execute(() -> httpClient.request(method, url, headers, body, timeoutMs, maxResponseSize)); } @Override @@ -82,9 +80,7 @@ public Future request(HttpMethod method, long timeoutMs, long maxResponseSize) { return circuitBreakerByName.computeIfAbsent(nameFrom(url), circuitBreakerCreator) - .execute(promise -> - httpClient.request(method, url, headers, body, timeoutMs, maxResponseSize) - .onComplete(promise)); + .execute(() -> httpClient.request(method, url, headers, body, timeoutMs, maxResponseSize)); } private CircuitBreaker createCircuitBreaker(String name, diff --git a/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java b/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java index 147de2210c4..94046bfc0d5 100644 --- a/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java +++ b/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java @@ -1,7 +1,6 @@ package org.prebid.server.vertx.verticles.server; import io.vertx.core.AbstractVerticle; -import io.vertx.core.AsyncResult; import io.vertx.core.Promise; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; @@ -55,19 +54,12 @@ public void start(Promise startPromise) { server.exceptionHandler(exceptionHandler); } - server.listen(address, result -> onServerStarted(result, startPromise)); - } - - private void onServerStarted(AsyncResult result, Promise startPromise) { - if (result.succeeded()) { - startPromise.tryComplete(); - logger.info( - "Successfully started {} instance on address: {}, thread: {}", - name, - address, - Thread.currentThread().getName()); - } else { - startPromise.tryFail(result.cause()); - } + server.listen(address) + .onComplete(result -> startPromise.complete(null, result.cause())) + .onSuccess(ignored -> logger.info( + "Successfully started {} instance on address: {}, thread: {}", + name, + address, + Thread.currentThread().getName())); } } diff --git a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java index 291dd35d415..ecfd0622a7a 100644 --- a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java @@ -9,6 +9,7 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.impl.headers.HeadersMultiMap; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.prebid.server.VertxTest; @@ -71,7 +72,10 @@ public void makeHttpRequestsShouldEncodePassedBidRequest() { .body(jacksonMapper.encodeToBytes(bidRequest)) .payload(bidRequest) .build()); - assertThat(result.getValue()).usingRecursiveComparison().isEqualTo(expectedResult.getValue()); + + assertThat(result.getValue()).usingRecursiveComparison() + .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, HeadersMultiMap.class) + .isEqualTo(expectedResult.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java b/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java index 41c38db12ad..eeb982d351b 100644 --- a/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java @@ -10,6 +10,7 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.impl.headers.HeadersMultiMap; import org.junit.jupiter.api.Test; import org.prebid.server.VertxTest; import org.prebid.server.bidder.gotthamads.GothamAdsBidder; @@ -103,7 +104,10 @@ public void makeHttpRequestsShouldMakeCorrectRequest() { .build() ); - assertThat(result.getValue()).usingRecursiveComparison().isEqualTo(expectedResult.getValue()); + assertThat(result.getValue()) + .usingRecursiveComparison() + .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, HeadersMultiMap.class) + .isEqualTo(expectedResult.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java b/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java index b790ad9033f..ee6f8dcfe0b 100644 --- a/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java @@ -12,6 +12,7 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.impl.headers.HeadersMultiMap; import org.junit.jupiter.api.Test; import org.prebid.server.VertxTest; import org.prebid.server.bidder.model.BidderBid; @@ -72,7 +73,10 @@ public void makeHttpRequestsShouldReturnExpectedHttpRequest() { .payload(bidRequest) .build()); - assertThat(result.getValue()).usingRecursiveComparison().isEqualTo(expectedResults.getValue()); + assertThat(result.getValue()) + .usingRecursiveComparison() + .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, HeadersMultiMap.class) + .isEqualTo(expectedResults.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/cookie/CookieDeprecationServiceTest.java b/src/test/java/org/prebid/server/cookie/CookieDeprecationServiceTest.java index 111d4f441f5..da03b34ec0e 100644 --- a/src/test/java/org/prebid/server/cookie/CookieDeprecationServiceTest.java +++ b/src/test/java/org/prebid/server/cookie/CookieDeprecationServiceTest.java @@ -5,6 +5,7 @@ import com.iab.openrtb.request.Device; import io.vertx.core.http.Cookie; import io.vertx.core.http.CookieSameSite; +import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.BeforeEach; @@ -26,6 +27,7 @@ import org.prebid.server.settings.model.AccountPrivacySandboxCookieDeprecationConfig; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.UnaryOperator; @@ -41,18 +43,22 @@ public class CookieDeprecationServiceTest extends VertxTest { @Mock(strictness = LENIENT) private RoutingContext routingContext; + @Mock(strictness = LENIENT) + private HttpServerRequest request; + private final CookieDeprecationService target = new CookieDeprecationService(); @BeforeEach public void before() { - given(routingContext.cookieMap()).willReturn(Map.of()); + given(routingContext.request()).willReturn(request); + given(request.cookies()).willReturn(Collections.emptySet()); } @Test public void makeCookieShouldReturnNullWhenRequestContainsDeprecationCookie() { // given - given(routingContext.cookieMap()) - .willReturn(Map.of("receive-cookie-deprecation", Cookie.cookie("receive-cookie-deprecation", "1"))); + given(routingContext.request().cookies()) + .willReturn(Collections.singleton(Cookie.cookie("receive-cookie-deprecation", "1"))); // when final PartitionedCookie actualCookie = target.makeCookie( @@ -66,8 +72,8 @@ public void makeCookieShouldReturnNullWhenRequestContainsDeprecationCookie() { @Test public void makeCookieShouldReturnNullWhenRequestContainsDeprecationCookieAndAccountIsEmpty() { // given - given(routingContext.cookieMap()) - .willReturn(Map.of("receive-cookie-deprecation", Cookie.cookie("receive-cookie-deprecation", "1"))); + given(request.cookies()).willReturn( + Collections.singleton(Cookie.cookie("receive-cookie-deprecation", "1"))); // when final PartitionedCookie actualCookie = target.makeCookie(Account.builder().build(), routingContext); diff --git a/src/test/java/org/prebid/server/cookie/UidsCookieServiceTest.java b/src/test/java/org/prebid/server/cookie/UidsCookieServiceTest.java index c73ee163935..2f930fdf0de 100644 --- a/src/test/java/org/prebid/server/cookie/UidsCookieServiceTest.java +++ b/src/test/java/org/prebid/server/cookie/UidsCookieServiceTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.http.Cookie; import io.vertx.core.http.CookieSameSite; +import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -25,15 +26,19 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static java.util.function.Function.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.within; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -49,8 +54,10 @@ public class UidsCookieServiceTest extends VertxTest { // Zero means size checking is disabled private static final int MAX_COOKIE_SIZE_BYTES = 0; - @Mock + @Mock(strictness = LENIENT) private RoutingContext routingContext; + @Mock(strictness = LENIENT) + private HttpServerRequest request; @Mock private PrioritizedCoopSyncProvider prioritizedCoopSyncProvider; @Mock @@ -72,6 +79,8 @@ public void setUp() { prioritizedCoopSyncProvider, metrics, jacksonMapper); + + given(routingContext.request()).willReturn(request); } @Test @@ -145,12 +154,13 @@ public void shouldReturnNonEmptyUidsCookie() { // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", // "expires": "2023-12-05T19:00:05.103329-03:00" } } } - given(routingContext.cookieMap()).willReturn(singletonMap("uids", Cookie.cookie( - "tempUIDs", - "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0ZUIiwg" - + "ImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" - + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" - + "4xMDMzMjktMDM6MDAiIH0gfSB9"))); + given(request.cookies()).willReturn(singleton( + Cookie.cookie( + "uids", + "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0ZUIiwg" + + "ImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" + + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" + + "4xMDMzMjktMDM6MDAiIH0gfSB9"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -173,7 +183,7 @@ public void shouldReturnNonNullUidsCookieIfUidsCookieIsMissing() { @Test public void shouldReturnNonNullUidsCookieIfUidsCookieIsNonBase64() { // given - given(routingContext.cookieMap()).willReturn(singletonMap("uids", Cookie.cookie("uids", "abcde"))); + given(request.cookies()).willReturn(singleton(Cookie.cookie("uids", "abcde"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -186,7 +196,7 @@ public void shouldReturnNonNullUidsCookieIfUidsCookieIsNonBase64() { public void shouldReturnNonNullUidsCookieIfUidsCookieIsNonJson() { // given // this uids cookie value stands for "abcde" - given(routingContext.cookieMap()).willReturn(singletonMap("uids", Cookie.cookie("tempUIDs", "bm9uLWpzb24="))); + given(request.cookies()).willReturn(singleton(Cookie.cookie("uids", "bm9uLWpzb24="))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -198,8 +208,8 @@ public void shouldReturnNonNullUidsCookieIfUidsCookieIsNonJson() { @Test public void shouldReturnUidsCookieWithOptoutTrueIfUidsCookieIsMissingAndOptoutCookieHasExpectedValue() { // given - given(routingContext.cookieMap()).willReturn( - singletonMap(OPT_OUT_COOKIE_NAME, Cookie.cookie(OPT_OUT_COOKIE_NAME, OPT_OUT_COOKIE_VALUE))); + given(request.cookies()).willReturn( + singleton(Cookie.cookie(OPT_OUT_COOKIE_NAME, OPT_OUT_COOKIE_VALUE))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -211,19 +221,18 @@ public void shouldReturnUidsCookieWithOptoutTrueIfUidsCookieIsMissingAndOptoutCo @Test public void shouldReturnUidsCookieWithOptoutTrueIfUidsCookieIsPresentAndOptoutCookieHasExpectedValue() { // given - final Map cookies = new HashMap<>(); - // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", - // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", - // "expires": "2023-12-05T19:00:05.103329-03:00" } } } - cookies.put("uids", + final Set cookies = Set.of( + // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", + // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", + // "expires": "2023-12-05T19:00:05.103329-03:00" } } } Cookie.cookie("uids", "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0" + "ZUIiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" - + "4xMDMzMjktMDM6MDAiIH0gfSB9")); + + "4xMDMzMjktMDM6MDAiIH0gfSB9"), - cookies.put(OPT_OUT_COOKIE_NAME, Cookie.cookie(OPT_OUT_COOKIE_NAME, OPT_OUT_COOKIE_VALUE)); + Cookie.cookie(OPT_OUT_COOKIE_NAME, OPT_OUT_COOKIE_VALUE)); - given(routingContext.cookieMap()).willReturn(cookies); + given(request.cookies()).willReturn(cookies); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -285,19 +294,20 @@ public void aliveCookieShouldSetPath() { @Test public void shouldReturnUidsCookieWithOptoutFalseIfOptoutCookieHasNotExpectedValue() { // given - final Map cookies = new HashMap<>(); - // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", - // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", - // "expires": "2023-12-05T19:00:05.103329-03:00" } } } - cookies.put("uids", Cookie.cookie( - "tempUIDs", - "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0ZUIiwg" - + "ImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" - + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" - + "4xMDMzMjktMDM6MDAiIH0gfSB9")); - cookies.put(OPT_OUT_COOKIE_NAME, Cookie.cookie(OPT_OUT_COOKIE_NAME, "dummy")); + final Set cookies = Set.of( + // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", + // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", + // "expires": "2023-12-05T19:00:05.103329-03:00" } } } + Cookie.cookie( + "uids", + "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0ZUIiwg" + + "ImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" + + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" + + "4xMDMzMjktMDM6MDAiIH0gfSB9"), - given(routingContext.cookieMap()).willReturn(cookies); + Cookie.cookie(OPT_OUT_COOKIE_NAME, "dummy")); + + given(request.cookies()).willReturn(cookies); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -323,8 +333,9 @@ public void shouldReturnUidsCookieWithOptoutFalseIfOptoutCookieNameNotSpecified( prioritizedCoopSyncProvider, metrics, jacksonMapper); - given(routingContext.cookieMap()).willReturn( - singletonMap(OPT_OUT_COOKIE_NAME, Cookie.cookie("trp_optout", "true"))); + + given(request.cookies()).willReturn( + singleton(Cookie.cookie(OPT_OUT_COOKIE_NAME, "true"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -348,8 +359,9 @@ public void shouldReturnUidsCookieWithOptoutFalseIfOptoutCookieValueNotSpecified prioritizedCoopSyncProvider, metrics, jacksonMapper); - given(routingContext.cookieMap()).willReturn( - singletonMap(OPT_OUT_COOKIE_NAME, Cookie.cookie("trp_optout", "true"))); + + given(request.cookies()).willReturn( + singleton(Cookie.cookie(OPT_OUT_COOKIE_NAME, "true"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -373,7 +385,8 @@ public void shouldReturnRubiconCookieValueFromHostCookieWhenUidValueIsAbsent() { prioritizedCoopSyncProvider, metrics, jacksonMapper); - given(routingContext.cookieMap()).willReturn(singletonMap("khaos", Cookie.cookie("khaos", "abc123"))); + + given(request.cookies()).willReturn(singleton(Cookie.cookie("khaos", "abc123"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -398,18 +411,18 @@ public void shouldReturnRubiconCookieValueFromHostCookieWhenUidValueIsPresentBut metrics, jacksonMapper); - final Map cookies = new HashMap<>(); - // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", - // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", - // "expires": "2023-12-05T19:00:05.103329-03:00" } } } - cookies.put("uids", + final Set cookies = Set.of( + // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", + // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", + // "expires": "2023-12-05T19:00:05.103329-03:00" } } } Cookie.cookie("uids", "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0" + "ZUIiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" - + "4xMDMzMjktMDM6MDAiIH0gfSB9")); - cookies.put("khaos", Cookie.cookie("khaos", "abc123")); + + "4xMDMzMjktMDM6MDAiIH0gfSB9"), + + Cookie.cookie("khaos", "abc123")); - given(routingContext.cookieMap()).willReturn(cookies); + given(request.cookies()).willReturn(cookies); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -427,7 +440,7 @@ public void shouldSkipFacebookSentinelFromUidsCookie() throws JsonProcessingExce final Uids uids = Uids.builder().uids(uidsWithExpiry).build(); final String encodedUids = encodeUids(uids); - given(routingContext.cookieMap()).willReturn(singletonMap("uids", Cookie.cookie("uids", encodedUids))); + given(request.cookies()).willReturn(singleton(Cookie.cookie("uids", encodedUids))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -594,11 +607,11 @@ public void hostCookieUidToSyncShouldReturnHostCookieUidWhenHostCookieUidIsAbsen jacksonMapper); final String uidsCookieBase64 = Base64.getUrlEncoder().encodeToString(uidsCookie.toJson().getBytes()); - final Map cookieMap = Map.of( - "khaos", Cookie.cookie("khaos", "hostCookieUid"), - "uids", Cookie.cookie("uids", uidsCookieBase64)); + final Set cookies = Set.of( + Cookie.cookie("khaos", "hostCookieUid"), + Cookie.cookie("uids", uidsCookieBase64)); - given(routingContext.cookieMap()).willReturn(cookieMap); + given(request.cookies()).willReturn(cookies); // when final String result = target.hostCookieUidToSync(routingContext, RUBICON); @@ -623,7 +636,7 @@ public void hostCookieUidToSyncShouldReturnNullWhenUidsCookieHasNoUidForHostCook metrics, jacksonMapper); - given(routingContext.cookieMap()).willReturn(emptyMap()); + given(request.cookies()).willReturn(emptySet()); // when final String result = target.hostCookieUidToSync(routingContext, RUBICON); @@ -653,11 +666,11 @@ public void hostCookieUidToSyncShouldReturnNullWhenUidInUidsCookieSameAsUidInHos jacksonMapper); final String uidsCookieBase64 = Base64.getUrlEncoder().encodeToString(uidsCookie.toJson().getBytes()); - final Map cookieMap = Map.of( - "khaos", Cookie.cookie("khaos", "hostCookieUid"), - "uids", Cookie.cookie("uids", uidsCookieBase64)); + final Set cookies = Set.of( + Cookie.cookie("khaos", "hostCookieUid"), + Cookie.cookie("uids", uidsCookieBase64)); - given(routingContext.cookieMap()).willReturn(cookieMap); + given(request.cookies()).willReturn(cookies); // when final String result = target.hostCookieUidToSync(routingContext, RUBICON); diff --git a/src/test/java/org/prebid/server/execution/file/syncer/RemoteFileSyncerTest.java b/src/test/java/org/prebid/server/execution/file/syncer/RemoteFileSyncerTest.java index 2da614c4e50..33bef039bab 100644 --- a/src/test/java/org/prebid/server/execution/file/syncer/RemoteFileSyncerTest.java +++ b/src/test/java/org/prebid/server/execution/file/syncer/RemoteFileSyncerTest.java @@ -207,7 +207,7 @@ public void syncForFilepathShouldNotUpdateWhenHeadRequestReturnInvalidHead() { verify(fileSystem, times(2)).exists(eq(FILE_PATH)); verify(httpClient).request(any()); verify(fileProcessor).setDataPath(any()); - verify(fileSystem, never()).move(eq(TMP_FILE_PATH), eq(FILE_PATH), any(), any()); + verify(fileSystem, never()).move(eq(TMP_FILE_PATH), eq(FILE_PATH), any()); verify(vertx).setPeriodic(eq(UPDATE_INTERVAL), any()); verifyNoMoreInteractions(httpClient); } @@ -376,9 +376,7 @@ public void syncForFilepathShouldRetryWhenFileOpeningFailed() { given(vertx.setTimer(eq(RETRY_INTERVAL), any())) .willAnswer(withReturnObjectAndPassObjectToHandler(0L, 10L, 1)); - given(fileSystem.delete(any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.succeededFuture())) - .willAnswer(withSelfAndPassObjectToHandler(Future.failedFuture(new RuntimeException()))); + given(fileSystem.delete(any())).willReturn(Future.failedFuture(new RuntimeException())); given(fileProcessor.setDataPath(anyString())) .willReturn(Future.succeededFuture()); @@ -528,7 +526,7 @@ public void syncShouldNotUpdateFileWhenServerRespondsWithNonOkStatusCode() { verify(fileSystem, times(1)).exists(eq(FILE_PATH)); verify(fileSystem, never()).open(any(), any()); verify(fileSystem, never()).delete(any()); - verify(fileSystem, never()).move(any(), any(), any(), any()); + verify(fileSystem, never()).move(any(), any(), any()); verify(asyncFile, never()).close(); verify(httpClient, times(1)).request(any()); verify(httpClientResponse).statusCode(); diff --git a/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java b/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java index 084d64d1bcf..d278baa7f34 100644 --- a/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java +++ b/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java @@ -19,6 +19,7 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; +import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import static org.assertj.core.api.Assertions.assertThat; @@ -50,8 +51,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java index ed88f7f6fdd..d58992dcb34 100644 --- a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java @@ -1,5 +1,6 @@ package org.prebid.server.handler; +import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.core.http.Cookie; @@ -55,7 +56,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; @@ -64,6 +64,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mock.Strictness.LENIENT; @@ -344,7 +345,7 @@ public void shouldRespondWithoutCookieIfGdprProcessingPreventsCookieSetting() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); + verify(httpResponse, never()).addCookie(any(Cookie.class)); verify(httpResponse).setStatusCode(eq(451)); verify(httpResponse).end(eq("The gdpr_consent param prevents cookies from being saved")); verify(metrics).updateUserSyncTcfBlockedMetric(RUBICON); @@ -368,7 +369,7 @@ public void shouldRespondWithBadRequestStatusIfGdprProcessingFailsWithInvalidReq setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); + verify(httpResponse, never()).addCookie(any(Cookie.class)); verify(httpResponse).setStatusCode(eq(400)); verify(httpResponse).end(eq("Invalid request format: gdpr exception")); @@ -391,8 +392,8 @@ public void shouldRespondWithInternalServerErrorStatusIfGdprProcessingFailsWithU setuidHandler.handle(routingContext); // then - verify(httpResponse, never()).sendFile(any()); - verify(routingContext, never()).addCookie(any(Cookie.class)); + verify(httpResponse, never()).sendFile(anyString()); + verify(httpResponse, never()).addCookie(any(Cookie.class)); verify(httpResponse).setStatusCode(eq(500)); verify(httpResponse).end(eq("Unexpected setuid processing error: unexpected error TCF")); } @@ -452,11 +453,13 @@ public void shouldPassAccountToPrivacyEnforcementServiceWhenAccountIsNotFound() public void shouldRespondWithCookieFromRequestParam() throws IOException { // given final UidsCookie uidsCookie = emptyUidsCookie(); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, RUBICON, "J5VLCWQP-26-CWFT")) - .willReturn(updated(uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT"); @@ -465,21 +468,19 @@ public void shouldRespondWithCookieFromRequestParam() throws IOException { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(1); - assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); } @Test public void shouldRespondWithCookieFromRequestParamWhenBidderAndCookieFamilyAreDifferent() throws IOException { // given final UidsCookie uidsCookie = emptyUidsCookie(); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(ADNXS, "J5VLCWQP-26-CWFT"); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, ADNXS, "J5VLCWQP-26-CWFT")) - .willReturn(updated(uidsCookie.updateUid(ADNXS, "J5VLCWQP-26-CWFT"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(ADNXS); given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT"); @@ -488,21 +489,18 @@ public void shouldRespondWithCookieFromRequestParamWhenBidderAndCookieFamilyAreD setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(1); - assertThat(decodedUids.getUids().get(ADNXS).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); } @Test - public void shouldSendPixelWhenFParamIsEqualToIWhenTypeIsIframe() { + public void shouldSendPixelWhenFParamIsEqualToIWhenTypeIsIframe() throws JsonProcessingException { // given - given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) - .willReturn(new UidsCookie(Uids.builder().uids(emptyMap()).build(), jacksonMapper)); + final UidsCookie uidsCookie = emptyUidsCookie(); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) + .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(any(), any(), any())) - .willReturn(updated(emptyUidsCookie())); + .willReturn(updated(uidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("f")).willReturn("i"); @@ -512,12 +510,12 @@ public void shouldSendPixelWhenFParamIsEqualToIWhenTypeIsIframe() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse).sendFile(any()); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(uidsCookie)); } @Test - public void shouldSendEmptyResponseWhenFParamIsEqualToBWhenTypeIsRedirect() { + public void shouldSendEmptyResponseWhenFParamIsEqualToBWhenTypeIsRedirect() throws JsonProcessingException { // given given(tcfDefinerService.getGdprHostVendorId()).willReturn(null); @@ -551,14 +549,14 @@ public void shouldSendEmptyResponseWhenFParamIsEqualToBWhenTypeIsRedirect() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse, never()).sendFile(any()); + verify(httpResponse, never()).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(uidsCookie)); verify(httpResponse).putHeader(eq(HttpHeaders.CONTENT_LENGTH), eq("0")); verify(httpResponse).putHeader(eq(HttpHeaders.CONTENT_TYPE), eq(HttpHeaders.TEXT_HTML)); } @Test - public void shouldSendEmptyResponseWhenFParamNotDefinedAndTypeIsIframe() { + public void shouldSendEmptyResponseWhenFParamNotDefinedAndTypeIsIframe() throws JsonProcessingException { // given given(tcfDefinerService.getGdprHostVendorId()).willReturn(null); @@ -591,14 +589,14 @@ public void shouldSendEmptyResponseWhenFParamNotDefinedAndTypeIsIframe() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse, never()).sendFile(any()); + verify(httpResponse, never()).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(uidsCookie)); verify(httpResponse).putHeader(eq(HttpHeaders.CONTENT_LENGTH), eq("0")); verify(httpResponse).putHeader(eq(HttpHeaders.CONTENT_TYPE), eq(HttpHeaders.TEXT_HTML)); } @Test - public void shouldSendPixelWhenFParamNotDefinedAndTypeIsRedirect() { + public void shouldSendPixelWhenFParamNotDefinedAndTypeIsRedirect() throws JsonProcessingException { // given given(tcfDefinerService.getGdprHostVendorId()).willReturn(null); @@ -631,8 +629,8 @@ public void shouldSendPixelWhenFParamNotDefinedAndTypeIsRedirect() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse).sendFile(any()); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(uidsCookie)); } @Test @@ -642,11 +640,12 @@ public void shouldInCookieWithRequestValue() throws IOException { RUBICON, UidWithExpiry.live("J5VLCWQP-26-CWFT"), ADNXS, UidWithExpiry.live("12345")); final UidsCookie uidsCookie = new UidsCookie(Uids.builder().uids(uids).build(), jacksonMapper); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(RUBICON, "updatedUid"); given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, RUBICON, "updatedUid")) - .willReturn(updated(uidsCookie.updateUid(RUBICON, "updatedUid"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("uid")).willReturn("updatedUid"); @@ -655,18 +654,12 @@ public void shouldInCookieWithRequestValue() throws IOException { setuidHandler.handle(routingContext); // then - verify(httpResponse).sendFile(any()); - verify(routingContext, never()).addCookie(any(Cookie.class)); - - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(2); - assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("updatedUid"); - assertThat(decodedUids.getUids().get(ADNXS).getUid()).isEqualTo("12345"); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); } @Test - public void shouldReturnMultipleCookies() throws IOException { + public void shouldReturnMultipleCookies() { // given final Map uids = Map.of( RUBICON, UidWithExpiry.live("J5VLCWQP-26-CWFT"), @@ -694,23 +687,11 @@ public void shouldReturnMultipleCookies() throws IOException { setuidHandler.handle(routingContext); // then - verify(httpResponse).sendFile(any()); - verify(routingContext, never()).addCookie(any(Cookie.class)); - - final Map encodedUidsCookie = httpResponse.headers().getAll("Set-Cookie").stream() - .collect(Collectors.toMap(value -> value.split("=")[0], value -> value.split("=")[1])); - - assertThat(encodedUidsCookie).hasSize(2); - final Uids decodedUids1 = mapper.readValue(Base64.getUrlDecoder() - .decode(encodedUidsCookie.get("uids")), Uids.class); - final Uids decodedUids2 = mapper.readValue(Base64.getUrlDecoder() - .decode(encodedUidsCookie.get("uids2")), Uids.class); - - assertThat(decodedUids1.getUids()).hasSize(1); - assertThat(decodedUids1.getUids().get(RUBICON).getUid()).isEqualTo("updatedUid"); - - assertThat(decodedUids2.getUids()).hasSize(1); - assertThat(decodedUids2.getUids().get(ADNXS).getUid()).isEqualTo("12345"); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie( + cookieEqualTo("uids", "eyJ0ZW1wVUlEcyI6eyJydWJpY29uIjp7InVpZCI6InVwZGF0ZWRVaWQifX19")); + verify(httpResponse).addCookie( + cookieEqualTo("uids2", "eyJ0ZW1wVUlEcyI6eyJhZG54cyI6eyJ1aWQiOiIxMjM0NSJ9fX0")); } @Test @@ -720,10 +701,12 @@ public void shouldRespondWithCookieIfUserIsNotInGdprScope() throws IOException { .willReturn(Future.succeededFuture(TcfResponse.of(false, emptyMap(), null))); final UidsCookie uidsCookie = emptyUidsCookie(); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, RUBICON, "J5VLCWQP-26-CWFT")) - .willReturn(updated(uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT"); @@ -732,13 +715,8 @@ public void shouldRespondWithCookieIfUserIsNotInGdprScope() throws IOException { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse).sendFile(any()); - - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(1); - assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); + verify(httpResponse).sendFile(anyString()); } @Test @@ -761,10 +739,12 @@ public void shouldSkipTcfChecksAndRespondWithCookieIfHostVendorIdNotDefined() th given(tcfDefinerService.getGdprHostVendorId()).willReturn(null); final UidsCookie uidsCookie = emptyUidsCookie(); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, RUBICON, "J5VLCWQP-26-CWFT")) - .willReturn(updated(uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT"); @@ -774,13 +754,8 @@ public void shouldSkipTcfChecksAndRespondWithCookieIfHostVendorIdNotDefined() th // then verify(tcfDefinerService, never()).resultForVendorIds(anySet(), any()); - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse).sendFile(any()); - - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(1); - assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); } @Test @@ -877,13 +852,13 @@ public void shouldThrowExceptionInCaseOfBaseBidderCookieFamilyNameDuplicates() { assertThat(values).containsExactlyInAnyOrder("audienceNetwork", "rubicon"); } - private String getUidsCookie() { - return httpResponse.headers().get("Set-Cookie"); + private static Cookie equalToUidsCookie(UidsCookie uidsCookie) throws JsonProcessingException { + final String value = Base64.getUrlEncoder().encodeToString(mapper.writeValueAsBytes(uidsCookie.getCookieUids())); + return cookieEqualTo("uids", value); } - private static Uids decodeUids(String value) throws IOException { - final String uids = value.substring(5).split(";")[0]; - return mapper.readValue(Base64.getUrlDecoder().decode(uids), Uids.class); + private static Cookie cookieEqualTo(String name, String value) { + return argThat(cookie -> cookie.getName().equals(name) && cookie.getValue().equals(value)); } private SetuidEvent captureSetuidEvent() { diff --git a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java index 24210ae6b72..6e760120fbf 100644 --- a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java +++ b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java @@ -98,6 +98,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; @@ -149,8 +150,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java b/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java index c1e6c56cf13..8fd380d9df1 100644 --- a/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java +++ b/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java @@ -36,8 +36,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java b/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java index 82ef85987d2..26cfca57284 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java @@ -293,7 +293,7 @@ public void shouldNotPerformHttpRequestIfVendorListNotFoundAndFetchNotAllowed() // then verify(httpClient, never()).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -306,7 +306,7 @@ public void shouldNotAskToSaveFileIfReadingHttpResponseFails() { // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -319,7 +319,7 @@ public void shouldNotAskToSaveFileIfResponseCodeIsNot200() { // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -332,7 +332,7 @@ public void shouldNotAskToSaveFileIfResponseBodyCouldNotBeParsed() { // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -346,7 +346,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasInvalidVendorListVersion // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -360,7 +360,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasInvalidLastUpdated() thr // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -374,7 +374,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasNoVendors() throws JsonP // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -388,7 +388,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasEmptyVendors() throws Js // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -402,7 +402,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasAtLeastOneInvalidVendor( // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } // File system related tests @@ -419,7 +419,7 @@ public void shouldSaveFileWithExpectedPathAndContentIfVendorListNotFound() throw target.forVersion(1); // then - verify(fileSystem).writeFile(eq(filePath), eq(Buffer.buffer(vendorListAsString)), any()); + verify(fileSystem).writeFile(eq(filePath), eq(Buffer.buffer(vendorListAsString))); } // In-memory cache related tests @@ -456,8 +456,7 @@ public void shouldReturnVendorListFromCache() throws JsonProcessingException { // given givenHttpClientReturnsResponse(200, mapper.writeValueAsString(givenVendorList())); - given(fileSystem.writeFile(anyString(), any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.succeededFuture())); + given(fileSystem.writeFile(anyString(), any())).willReturn(Future.succeededFuture()); // when target.forVersion(1); // populate cache @@ -504,8 +503,8 @@ public void shouldKeepPurposesForAllVendors() throws JsonProcessingException { final VendorList vendorList = VendorList.of(1, new Date(), idToVendor); givenHttpClientReturnsResponse(200, mapper.writeValueAsString(vendorList)); - given(fileSystem.writeFile(anyString(), any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.succeededFuture())); + given(fileSystem.writeFile(anyString(), any())) + .willReturn(Future.succeededFuture()); // when target.forVersion(1); // populate cache @@ -572,8 +571,7 @@ public void shouldIncrementVendorListErrorMetricWhenFileIsNotSaved() throws Json // given givenHttpClientReturnsResponse(200, mapper.writeValueAsString(givenVendorList())); - given(fileSystem.writeFile(anyString(), any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.failedFuture("error"))); + given(fileSystem.writeFile(anyString(), any())).willReturn(Future.failedFuture("error")); // when target.forVersion(1); @@ -587,8 +585,7 @@ public void shouldIncrementVendorListOkMetric() throws JsonProcessingException { // given givenHttpClientReturnsResponse(200, mapper.writeValueAsString(givenVendorList())); - given(fileSystem.writeFile(anyString(), any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.succeededFuture())); + given(fileSystem.writeFile(anyString(), any())).willReturn(Future.succeededFuture()); // when target.forVersion(1); @@ -635,13 +632,4 @@ private void givenHttpClientProducesException(Throwable throwable) { given(httpClient.get(anyString(), anyLong())) .willReturn(Future.failedFuture(throwable)); } - - @SuppressWarnings("unchecked") - private static Answer withSelfAndPassObjectToHandler(T obj) { - return inv -> { - // invoking handler right away passing mock to it - ((Handler) inv.getArgument(2)).handle(obj); - return inv.getMock(); - }; - } } diff --git a/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java index 13fda67839e..1ffc9052637 100644 --- a/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static java.util.Collections.emptySet; @@ -73,8 +74,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java b/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java index b46587f01a8..b340b7f0ca4 100644 --- a/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java +++ b/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java @@ -26,6 +26,7 @@ import java.time.Clock; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; @@ -95,8 +96,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/util/HttpUtilTest.java b/src/test/java/org/prebid/server/util/HttpUtilTest.java index 7926e05ed46..e02a8bf6577 100644 --- a/src/test/java/org/prebid/server/util/HttpUtilTest.java +++ b/src/test/java/org/prebid/server/util/HttpUtilTest.java @@ -3,6 +3,7 @@ import io.vertx.core.MultiMap; import io.vertx.core.http.Cookie; import io.vertx.core.http.HttpHeaders; +import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; import org.junit.jupiter.api.BeforeEach; @@ -16,7 +17,7 @@ import java.util.Map; import java.util.function.Consumer; -import static java.util.Collections.singletonMap; +import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.entry; @@ -35,11 +36,14 @@ public class HttpUtilTest { @Mock(strictness = LENIENT) private RoutingContext routingContext; + @Mock(strictness = LENIENT) + private HttpServerRequest httpRequest; @Mock private HttpServerResponse httpResponse; @BeforeEach public void setUp() { + given(routingContext.request()).willReturn(httpRequest); given(routingContext.response()).willReturn(httpResponse); } @@ -131,7 +135,7 @@ public void getHostFromUrlShouldReturnNullIfUrlIsMalformed() { @Test public void cookiesAsMapShouldReturnExpectedResult() { // given - given(routingContext.cookieMap()).willReturn(singletonMap("name", Cookie.cookie("name", "value"))); + given(httpRequest.cookies()).willReturn(singleton(Cookie.cookie("name", "value"))); // when final Map cookies = HttpUtil.cookiesAsMap(routingContext); diff --git a/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java b/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java index 12984132eae..ef081ac0e79 100644 --- a/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java +++ b/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java @@ -15,6 +15,8 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; @@ -36,8 +38,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test @@ -131,14 +134,14 @@ public void executeShouldFailsWithOriginalExceptionIfOpeningIntervalExceeds() { } private Future executeWithSuccess(String result) { - return execute(operationPromise -> operationPromise.complete(result)); + return execute(() -> Future.succeededFuture(result)); } private Future executeWithFail(String errorMessage) { - return execute(operationPromise -> operationPromise.fail(new RuntimeException(errorMessage))); + return execute(() -> Future.failedFuture(new RuntimeException(errorMessage))); } - private Future execute(Handler> handler) { + private Future execute(Supplier> handler) { final Future future = circuitBreaker.execute(handler); final Promise promise = Promise.promise(); diff --git a/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java b/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java index 5b8cafd5251..cdbebc2d54b 100644 --- a/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java +++ b/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java @@ -35,7 +35,7 @@ public void closeShouldInvokeHandlerWithSuccededFuture() { new CloseableAdapter(closeable).close(completionPromise); // then - verify(completionPromise).handle(eq(Future.succeededFuture())); + verify(completionPromise).succeed(); } @Test @@ -48,6 +48,6 @@ public void closeShouldInvokeHandlerWithFailedFutureIfIOExceptionThrown() throws new CloseableAdapter(closeable).close(completionPromise); // then - verify(completionPromise).handle(argThat(future -> future.failed() && future.cause() == exception)); + verify(completionPromise).fail(exception); } } diff --git a/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java b/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java index abc2d8af479..8e751f4ba53 100644 --- a/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java +++ b/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java @@ -22,6 +22,7 @@ import java.time.Instant; import java.time.ZoneId; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import static java.util.Arrays.asList; @@ -60,8 +61,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/vertx/httpclient/BasicHttpClientTest.java b/src/test/java/org/prebid/server/vertx/httpclient/BasicHttpClientTest.java index 61618b362ea..3c3ab67bf39 100644 --- a/src/test/java/org/prebid/server/vertx/httpclient/BasicHttpClientTest.java +++ b/src/test/java/org/prebid/server/vertx/httpclient/BasicHttpClientTest.java @@ -40,6 +40,8 @@ @ExtendWith(VertxExtension.class) public class BasicHttpClientTest { + private static final String CRLF = "\r\n"; + @Mock private Vertx vertx; @Mock(strictness = LENIENT) @@ -203,12 +205,12 @@ private static void startServer(int port, long entireResponseDelay, long bodyRes } out.write("HTTP/1.1 200 OK"); - out.newLine(); + out.write(CRLF); out.write("Content-Length: 6"); // set body size greater then length of "start" word - out.newLine(); + out.write(CRLF); - out.newLine(); + out.write(CRLF); out.write("start"); out.flush(); diff --git a/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java b/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java index 75f2b8c6a52..f0788671e1f 100644 --- a/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java +++ b/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java @@ -21,6 +21,7 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; +import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import java.util.function.LongSupplier; @@ -56,8 +57,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test @@ -192,6 +194,8 @@ public void requestShouldFailWithOriginalExceptionIfOpeningIntervalExceeds() { @Test public void circuitBreakerNumberGaugeShouldReportActualNumber() { // when + givenHttpClientReturning(new RuntimeException("exception")); + doRequest(); // then From 00eff377c0a46a61902e0bc61858158c1cb14349 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 9 Apr 2026 02:50:18 +0300 Subject: [PATCH 02/18] Replaced custom attempt to implement rolling window for circuit breaker failures with "out of the box" to simplify code. --- docs/config-app.md | 447 ++++++++++++------ ...rcuitBreakerSecuredGeoLocationService.java | 12 +- .../config/GeoLocationConfiguration.java | 17 +- .../spring/config/ServiceConfiguration.java | 6 +- .../database/DatabaseConfiguration.java | 3 +- .../prebid/server/vertx/CircuitBreaker.java | 21 +- .../CircuitBreakerSecuredDatabaseClient.java | 7 +- .../CircuitBreakerSecuredHttpClient.java | 10 +- ...tBreakerSecuredGeoLocationServiceTest.java | 9 +- .../server/vertx/CircuitBreakerTest.java | 11 +- ...rcuitBreakerSecuredDatabaseClientTest.java | 8 +- .../CircuitBreakerSecuredHttpClientTest.java | 9 +- 12 files changed, 344 insertions(+), 216 deletions(-) diff --git a/docs/config-app.md b/docs/config-app.md index a661f5a74a2..569e4c09b15 100644 --- a/docs/config-app.md +++ b/docs/config-app.md @@ -1,22 +1,31 @@ - # Full list of application configuration options This document describes all configuration properties available for Prebid Server. ## Spring -- `spring.main.banner-mode` - determine if the banner has to be printed on System.out (console), using the configured logger (log) or not at all (off). -This section can be extended against standard [Spring configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html) options. +- `spring.main.banner-mode` - determine if the banner has to be printed on System.out (console), using the configured + logger (log) or not at all (off). + +This section can be extended against +standard [Spring configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html) +options. ## Vert.x + - `vertx.worker-pool-size` - set the maximum number of worker threads to be used by the Vert.x instance. -- `vertx.uploads-dir` - directory that Vert.x [BodyHandler](http://vertx.io/docs/apidocs/io/vertx/ext/web/handler/BodyHandler.html) will use to store multi-part file uploads. -This parameter exists to allow to change the location of the directory Vert.x will create because it will and there is no way to make it not. -- `vertx.init-timeout-ms` - time to wait for asynchronous initialization steps completion before considering them stuck. When exceeded - exception is thrown and Prebid Server stops. +- `vertx.uploads-dir` - directory that + Vert.x [BodyHandler](http://vertx.io/docs/apidocs/io/vertx/ext/web/handler/BodyHandler.html) will use to store + multi-part file uploads. + This parameter exists to allow to change the location of the directory Vert.x will create because it will and there is + no way to make it not. +- `vertx.init-timeout-ms` - time to wait for asynchronous initialization steps completion before considering them stuck. + When exceeded - exception is thrown and Prebid Server stops. - `vertx.enable-per-client-endpoint-metrics` - enables HTTP client metrics per destination endpoint (`host:port`) - `vertx.round-robin-inet-address` - enables round-robin inet address selection of the ip address to use ## Server + - `server.max-headers-size` - set the maximum length of all headers. - `server.ssl` - enable SSL/TLS support. - `server.jks-path` - path to the java keystore (if ssl is enabled). @@ -24,6 +33,7 @@ This parameter exists to allow to change the location of the directory Vert.x wi - `server.cpu-load-monitoring.measurement-interval-ms` - the CPU load monitoring interval (milliseconds) ## HTTP Server + - `server.max-headers-size` - set the maximum length of all headers, deprecated(use server.max-headers-size instead). - `server.ssl` - enable SSL/TLS support, deprecated(use server.ssl instead). - `server.jks-path` - path to the java keystore (if ssl is enabled), deprecated(use server.jks-path instead). @@ -33,215 +43,291 @@ This parameter exists to allow to change the location of the directory Vert.x wi - `server.enable-quickack` - enables the TCP_QUICKACK option - only with linux native transport. - `server.enable-reuseport` - set the value of reuse port - `server.http.server-instances` - how many http server instances should be created. - This parameter affects how many CPU cores will be utilized by the application. Rough assumption - one http server instance will keep 1 CPU core busy. + This parameter affects how many CPU cores will be utilized by the application. Rough assumption - one http server + instance will keep 1 CPU core busy. - `server.http.enabled` - if set to `true` enables http server - `server.http.port` - the port to listen on. ## Unix Domain Socket Server + - `server.unix-socket.server-instances` - how many http server instances should be created. - `server.unix-socket.enabled` - if set to `true` enables unix socket server - `server.unix-socket.path` - the path to unix socket to listen on. ## HTTP Client + - `http-client.max-pool-size` - set the maximum pool size for outgoing connections (per host). - `http-client.idle-timeout-ms` - set the maximum time idle connections could exist before being reaped - `http-client.pool-cleaner-period-ms` - set how often idle connections will be closed removed from pool - `http-client.connect-timeout-ms` - set the connect timeout. -- `http-client.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make http client more robust. +- `http-client.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make http client more + robust. - `http-client.circuit-breaker.opening-threshold` - the number of failures before opening the circuit. -- `http-client.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached. +- `http-client.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count + reached (must be a multiple of 1000). - `http-client.circuit-breaker.closing-interval-ms` - time spent in open state before attempting to re-try. - `http-client.circuit-breaker.idle-expire-hours` - idle time to clean the circuit breaker up. -- `http-client.use-compression` - if equals to `true` httpclient compression is enabled for requests (see [also](https://vertx.io/docs/apidocs/io/vertx/core/http/HttpClientOptions.html#setTryUseCompression-boolean-)) -- `http-client.max-redirects` - set the maximum amount of HTTP redirections to follow. A value of 0 (the default) prevents redirections from being followed. +- `http-client.use-compression` - if equals to `true` httpclient compression is enabled for requests ( + see [also](https://vertx.io/docs/apidocs/io/vertx/core/http/HttpClientOptions.html#setTryUseCompression-boolean-)) +- `http-client.max-redirects` - set the maximum amount of HTTP redirections to follow. A value of 0 (the default) + prevents redirections from being followed. - `http-client.ssl` - enable SSL/TLS support. - `http-client.jks-path` - path to the java keystore (if ssl is enabled). - `http-client.jks-password` - password for the keystore (if ssl is enabled). ## Remote-file-syncer + Remote File Syncer can be related to particular entity like geolocation maxmind service etc. Removes and downloads file again if depending service cant process probably corrupted file in the first start. - `.remote-file-syncer.download-url` - url to database file to download. -- `.remote-file-syncer.save-filepath` - full path to the usable file, which will be consumed by internal service. +- `.remote-file-syncer.save-filepath` - full path to the usable file, which will be consumed by internal + service. - `.remote-file-syncer.tmp-filepath` - full path to the temporary file. - `.remote-file-syncer.retry-count` - how many times try to download. - `.remote-file-syncer.retry-interval-ms` - how long to wait between failed retries. - `.remote-file-syncer.retry.delay-millis` - initial time of how long to wait between failed retries. - `.remote-file-syncer.retry.max-delay-millis` - maximum allowed value for `delay-millis`. -- `.remote-file-syncer.retry.factor` - factor for the `delay-millis` value, that will be applied after each failed retry to modify `delay-millis` value. +- `.remote-file-syncer.retry.factor` - factor for the `delay-millis` value, that will be applied after each + failed retry to modify `delay-millis` value. - `.remote-file-syncer.retry.jitter` - jitter (multiplicative) for `delay-millis` parameter. - `.remote-file-syncer.timeout-ms` - default operation timeout for obtaining database file. - `.remote-file-syncer.update-interval-ms` - time interval between updates of the usable file. - `.remote-file-syncer.http-client.connect-timeout-ms` - set the connect timeout. -- `.remote-file-syncer.http-client.max-redirects` - set the maximum amount of HTTP redirections to follow. A value of 0 (the default) prevents redirections from being followed. +- `.remote-file-syncer.http-client.max-redirects` - set the maximum amount of HTTP redirections to follow. A + value of 0 (the default) prevents redirections from being followed. ## General settings -- `host-id` - the ID of node where prebid server deployed. -- `external-url` - the setting stands for external URL prebid server is reachable by, for example address of the load-balancer e.g. http://prebid.host.com. + +- `host-id` - the ID of node where prebid server deployed. +- `external-url` - the setting stands for external URL prebid server is reachable by, for example address of the + load-balancer e.g. http://prebid.host.com. - `admin.port` - the port to listen on administration requests. ## Default bid request + - `default-request.file.path` - path to a JSON file containing the default request ## Auction (OpenRTB) + - `auction.blocklisted-accounts` - comma separated list of blocklisted account IDs. -- `auction.blocklisted-apps` - comma separated list of blocklisted applications IDs, requests from which should not be processed. +- `auction.blocklisted-apps` - comma separated list of blocklisted applications IDs, requests from which should not be + processed. - `auction.biddertmax.min` - minimum operation timeout for OpenRTB Auction requests. - `auction.biddertmax.max` - maximum operation timeout for OpenRTB Auction requests. - `auction.biddertmax.percent` - adjustment factor for `request.tmax` for bidders. - `auction.tmax-upstream-response-time` - the amount of time that PBS needs to respond to the original caller. - `auction.max-request-size` - set the maximum size in bytes of OpenRTB Auction request. - `auction.stored-requests-timeout-ms` - timeout for stored requests fetching. -- `auction.ad-server-currency` - default currency for auction, if its value was not specified in request. Important note: PBS uses ISO-4217 codes for the representation of currencies. +- `auction.ad-server-currency` - default currency for auction, if its value was not specified in request. Important + note: PBS uses ISO-4217 codes for the representation of currencies. - `auction.cache.expected-request-time-ms` - approximate value in milliseconds for Cache Service interacting. -- `auction.cache.only-winning-bids` - if equals to `true` only the winning bids would be cached. Has lower priority than request-specific flags. +- `auction.cache.only-winning-bids` - if equals to `true` only the winning bids would be cached. Has lower priority than + request-specific flags. - `auction.generate-bid-id` - whether to generate seatbid[].bid[].ext.prebid.bidid in the OpenRTB response. -- `auction.enforce-random-bid-id` - whether to enforce generating a robust random seatbid[].bid[].id in the OpenRTB response if the initial value is less than 17 characters. -- `auction.validations.banner-creative-max-size` - enables creative max size validation for banners. Possible values: `skip`, `enforce`, `warn`. Default is `skip`. -- `auction.validations.secure-markup` - enables secure markup validation. Possible values: `skip`, `enforce`, `warn`. Default is `skip`. -- `auction.host-schain-node` - defines global schain node that will be appended to `request.source.ext.schain.nodes` passed to bidders +- `auction.enforce-random-bid-id` - whether to enforce generating a robust random seatbid[].bid[].id in the OpenRTB + response if the initial value is less than 17 characters. +- `auction.validations.banner-creative-max-size` - enables creative max size validation for banners. Possible values: + `skip`, `enforce`, `warn`. Default is `skip`. +- `auction.validations.secure-markup` - enables secure markup validation. Possible values: `skip`, `enforce`, `warn`. + Default is `skip`. +- `auction.host-schain-node` - defines global schain node that will be appended to `request.source.ext.schain.nodes` + passed to bidders - `auction.category-mapping-enabled` - if equals to `true` the category mapping feature will be active while auction. -- `auction.strict-app-site-dooh` - if set to `true`, it will reject requests that contain more than one of app/site/dooh. Defaults to `false`. +- `auction.strict-app-site-dooh` - if set to `true`, it will reject requests that contain more than one of + app/site/dooh. Defaults to `false`. ## Event + - `event.default-timeout-ms` - timeout for event notifications ## Timeout notification + - `auction.timeout-notification.timeout-ms` - HTTP timeout to use when sending notifications about bidder timeouts - `auction.timeout-notification.log-result` - causes bidder timeout notification result to be logged - `auction.timeout-notification.log-failure-only` - causes only bidder timeout notification failures to be logged -- `auction.timeout-notification.log-sampling-rate` - instructs apply sampling when logging bidder timeout notification results +- `auction.timeout-notification.log-sampling-rate` - instructs apply sampling when logging bidder timeout notification + results ## Video + - `video.stored-request-required` - flag forces to merge with stored request - `video.stored-requests-timeout-ms` - timeout for stored requests fetching. - `auction.blocklisted-accounts` - comma separated list of blocklisted account IDs. - `auction.video.escape-log-cache-regex` - regex to remove from cache debug log xml. -- `auction.ad-server-currency` - default currency for video auction, if its value was not specified in request. Important note: PBS uses ISO-4217 codes for the representation of currencies. +- `auction.ad-server-currency` - default currency for video auction, if its value was not specified in request. + Important note: PBS uses ISO-4217 codes for the representation of currencies. ## Setuid + - `setuid.default-timeout-ms` - default operation timeout for requests to `/setuid` endpoint. -- `setuid.number-of-uid-cookies` - specifies the maximum number of UID cookies that can be returned in the `/setuid` endpoint response. If it's not specified `1` will be taken as the default value. +- `setuid.number-of-uid-cookies` - specifies the maximum number of UID cookies that can be returned in the `/setuid` + endpoint response. If it's not specified `1` will be taken as the default value. ## Cookie Sync + - `cookie-sync.default-timeout-ms` - default operation timeout for requests to `/cookie_sync` endpoint. - `cookie-sync.coop-sync.default` - default value for coopSync when it missing in requests to `/cookie_sync` endpoint. - `cookie-sync.pri` - lists of bidders prioritised in groups. -- `cookie-sync.default-limit` - default bidder limit, that is applied when limit parameter is not sent in the request and absent in account config -- `cookie-sync.max-limit` - default maximum possible limit value for the limit parameter, that is applied when maximum limit parameter is absent in account config +- `cookie-sync.default-limit` - default bidder limit, that is applied when limit parameter is not sent in the request + and absent in account config +- `cookie-sync.max-limit` - default maximum possible limit value for the limit parameter, that is applied when maximum + limit parameter is absent in account config ## Vtrack -- `vtrack.allow-unknown-bidder` - flag that allows servicing requests with bidders who were not configured in Prebid Server. -- `vtrack.modify-vast-for-unknown-bidder` - flag that allows modifying the VAST value and adding the impression tag to it, for bidders who were not configured in Prebid Server. + +- `vtrack.allow-unknown-bidder` - flag that allows servicing requests with bidders who were not configured in Prebid + Server. +- `vtrack.modify-vast-for-unknown-bidder` - flag that allows modifying the VAST value and adding the impression tag to + it, for bidders who were not configured in Prebid Server. - `vtrack.default-timeout-ms` - a default timeout in ms for the vtrack request ## Adapters + - `adapters.*` - the section for bidder specific configuration options. There are several typical keys: -- `adapters..enabled` - indicates the bidder should be active and ready for auction. By default all bidders are disabled. + +- `adapters..enabled` - indicates the bidder should be active and ready for auction. By default all bidders + are disabled. - `adapters..endpoint` - the url for submitting bids. -- `adapters..pbs-enforces-ccpa` - indicates if PBS server provides CCPA support for bidder or bidder will handle it itself. -- `adapters..modifying-vast-xml-allowed` - indicates if PBS server is allowed to modify VAST creatives received from this bidder. +- `adapters..pbs-enforces-ccpa` - indicates if PBS server provides CCPA support for bidder or bidder will + handle it itself. +- `adapters..modifying-vast-xml-allowed` - indicates if PBS server is allowed to modify VAST creatives + received from this bidder. - `adapters..deprecated-names` - comma separated deprecated names of bidder. -- `adapters..meta-info.maintainer-email` - specifies maintainer e-mail address that will be shown in bidder info endpoint response. -- `adapters..meta-info.app-media-types` - specifies media types supported for app requests that will be shown in bidder info endpoint response. -- `adapters..meta-info.site-media-types` - specifies media types supported for site requests that will be shown in bidder info endpoint response. +- `adapters..meta-info.maintainer-email` - specifies maintainer e-mail address that will be shown in bidder + info endpoint response. +- `adapters..meta-info.app-media-types` - specifies media types supported for app requests that will be + shown in bidder info endpoint response. +- `adapters..meta-info.site-media-types` - specifies media types supported for site requests that will be + shown in bidder info endpoint response. - `adapters..meta-info.supported-vendors` - specifies viewability vendors supported by the bidder. - `adapters..meta-info.vendor-id` - specifies TCF vendor ID. - `adapters..usersync.url` - the url for synchronizing UIDs cookie. - `adapters..usersync.redirect-url` - the redirect part of url for synchronizing UIDs cookie. -- `adapters..usersync.cookie-family-name` - the family name by which user ids within adapter's realm are stored in uidsCookie. +- `adapters..usersync.cookie-family-name` - the family name by which user ids within adapter's realm are + stored in uidsCookie. - `adapters..usersync.type` - usersync type (i.e. redirect, iframe). - `adapters..usersync.support-cors` - flag signals if CORS supported by usersync. -- `adapters..debug.allow` - enables debug output in the auction response for the given bidder. Default `true`. -- `adapters..tmax-deduction-ms` - adjusts the tmax sent to the bidder by deducting the provided value (ms). Default `0 ms` - no deduction. - -In addition, each bidder could have arbitrary aliases configured that will look and act very much the same as the bidder itself. -Aliases are configured by adding child configuration object at `adapters..aliases..`, aliases -support the same configuration options that their bidder counterparts support except `aliases` (i.e. it's not possible -to declare alias of an alias). Another restriction of aliases configuration is that they cannot declare support for media types -not supported by their bidders (however aliases could narrow down media types they support). For example: if the bidder -is written to not support native site requests, then an alias cannot magically decide to change that; however, if a bidder -supports native site requests, and the alias does not want to for some reason, it has the ability to remove that support. +- `adapters..debug.allow` - enables debug output in the auction response for the given bidder. Default + `true`. +- `adapters..tmax-deduction-ms` - adjusts the tmax sent to the bidder by deducting the provided value (ms). + Default `0 ms` - no deduction. + +In addition, each bidder could have arbitrary aliases configured that will look and act very much the same as the bidder +itself. +Aliases are configured by adding child configuration object at `adapters..aliases..`, aliases +support the same configuration options that their bidder counterparts support except `aliases` (i.e. it's not possible +to declare alias of an alias). Another restriction of aliases configuration is that they cannot declare support for +media types +not supported by their bidders (however aliases could narrow down media types they support). For example: if the bidder +is written to not support native site requests, then an alias cannot magically decide to change that; however, if a +bidder +supports native site requests, and the alias does not want to for some reason, it has the ability to remove that +support. Also, each bidder could have its own bidder-specific options. ## Logging + - `logging.http-interaction.max-limit` - maximum value for the number of interactions to log in one take. - `logging.change-level.max-duration-ms` - maximum duration (in milliseconds) for which logging level could be changed. - `logging.sampling-rate` - a percentage of messages that are logged ## Currency Converter -- `currency-converter.external-rates.enabled` - if equals to `true` the currency conversion service will be enabled to fetch updated rates and convert bid currencies from external source. Also enables `/currency-rates` endpoint on admin port. -- `currency-converter.external-rates.url` - the url for Prebid.org’s currency file. [More details](http://prebid.org/dev-docs/modules/currency.html) + +- `currency-converter.external-rates.enabled` - if equals to `true` the currency conversion service will be enabled to + fetch updated rates and convert bid currencies from external source. Also enables `/currency-rates` endpoint on admin + port. +- `currency-converter.external-rates.url` - the url for Prebid.org’s currency + file. [More details](http://prebid.org/dev-docs/modules/currency.html) - `currency-converter.external-rates.default-timeout-ms` - default operation timeout for fetching currency rates. - `currency-converter.external-rates.refresh-period-ms` - default refresh period for currency rates updates. - `currency-converter.external-rates.stale-after-ms` - how old currency rates should be to become considered stale. -- `currency-converter.external-rates.stale-period-ms` - stale period after which the latest external currency rates get discarded. +- `currency-converter.external-rates.stale-period-ms` - stale period after which the latest external currency rates get + discarded. ## Admin Endpoints + - `admin-endpoints.version.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.version.path` - the server context path where the endpoint will be accessible. - `admin-endpoints.version.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.version.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.version.protected` - when equals to `true` endpoint will be protected by basic authentication + configured in `admin-endpoints.credentials` - `admin-endpoints.currency-rates.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.currency-rates.path` - the server context path where the endpoint will be accessible. - `admin-endpoints.currency-rates.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.currency-rates.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.currency-rates.protected` - when equals to `true` endpoint will be protected by basic authentication + configured in `admin-endpoints.credentials` - `admin-endpoints.storedrequest.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.storedrequest.path` - the server context path where the endpoint will be accessible. - `admin-endpoints.storedrequest.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.storedrequest.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.storedrequest.protected` - when equals to `true` endpoint will be protected by basic authentication + configured in `admin-endpoints.credentials` - `admin-endpoints.storedrequest-amp.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.storedrequest-amp.path` - the server context path where the endpoint will be accessible. -- `admin-endpoints.storedrequest-amp.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.storedrequest-amp.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.storedrequest-amp.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.storedrequest-amp.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` - `admin-endpoints.cache-invalidation.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.cache-invalidation.path` - the server context path where the endpoint will be accessible. -- `admin-endpoints.cache-invalidation.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.cache-invalidation.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.cache-invalidation.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.cache-invalidation.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` - `admin-endpoints.logging-httpinteraction.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.logging-httpinteraction.path` - the server context path where the endpoint will be accessible. -- `admin-endpoints.logging-httpinteraction.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.logging-httpinteraction.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.logging-httpinteraction.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.logging-httpinteraction.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` - `admin-endpoints.tracelog.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.tracelog.path` - the server context path where the endpoint will be accessible. - `admin-endpoints.tracelog.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.tracelog.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.tracelog.protected` - when equals to `true` endpoint will be protected by basic authentication + configured in `admin-endpoints.credentials` - `admin-endpoints.collected-metrics.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.collected-metrics.path` - the server context path where the endpoint will be accessible. -- `admin-endpoints.collected-metrics.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.collected-metrics.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.collected-metrics.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.collected-metrics.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` - `admin-endpoints.logging-changelevel.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.logging-changelevel.path` - the server context path where the endpoint will be accessible -- `admin-endpoints.logging-changelevel.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.logging-changelevel.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.logging-changelevel.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.logging-changelevel.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` -- `admin-endpoints.credentials` - user and password for access to admin endpoints if `admin-endpoints.[NAME].protected` is true`. +- `admin-endpoints.credentials` - user and password for access to admin endpoints if `admin-endpoints.[NAME].protected` + is true`. ## Metrics -- `metrics.metricType` - set the type of metric counter for [Dropwizard Metrics](http://metrics.dropwizard.io). Can be `flushingCounter` (default), `counter` or `meter`. -So far metrics cannot be submitted simultaneously to many backends. Currently we support `graphite` and `influxdb`. +- `metrics.metricType` - set the type of metric counter for [Dropwizard Metrics](http://metrics.dropwizard.io). Can be + `flushingCounter` (default), `counter` or `meter`. + +So far metrics cannot be submitted simultaneously to many backends. Currently we support `graphite` and `influxdb`. Also, for debug purposes you can use `console` as metrics backend. For `logback` backend type available next options: + - `metrics.logback.enabled` - if equals to `true` then logback reporter will be started. - `metrics.logback.name` - name of logger element in the logback configuration file. - `metrics.logback.interval` - interval in seconds between successive sending metrics. - For `graphite` backend type available next options: + - `metrics.graphite.enabled` - if equals to `true` then `graphite` will be used to submit metrics. - `metrics.graphite.prefix` - the prefix of all metric names. - `metrics.graphite.host` - the graphite host for sending statistics. @@ -249,6 +335,7 @@ For `graphite` backend type available next options: - `metrics.graphite.interval` - interval in seconds between successive sending metrics. For `influxdb` backend type available next options: + - `metrics.influxdb.enabled` - if equals to `true` then `influxdb` will be used to submit metrics. - `metrics.influxdb.prefix` - the prefix of all metric names. - `metrics.influxdb.protocol` - external service destination protocol. @@ -262,48 +349,63 @@ For `influxdb` backend type available next options: - `metrics.influxdb.tags` - the influxDb tags, optional key-value metrics metadata. For `console` backend type available next options: + - `metrics.console.enabled` - if equals to `true` then `console` will be used to submit metrics. - `metrics.console.interval` - interval in seconds between successive sending metrics. For `prometheus` backend type available next options: + - `metrics.prometheus.enabled` - if equals to `true` then prometheus reporter will be started - `metrics.prometheus.port` - prometheus reporter port - `metrics.prometheus.namespace` - optional namespace prefix for metrics - `metrics.prometheus.subsystem` - optional subsystem prefix for metrics -- `metrics.prometheus.custom-labels-enabled` - If set to `true` it enables tags/labels for prometheus metrics instead of including them in the metrics path +- `metrics.prometheus.custom-labels-enabled` - If set to `true` it enables tags/labels for prometheus metrics instead of + including them in the metrics path It is possible to define how many account-level metrics will be submitted on per-account basis. See [metrics documentation](metrics.md) for complete list of metrics submitted at each verbosity level. -- `metrics.accounts.default-verbosity` - verbosity for accounts not specified in next sections. Allowed values: `none, basic, detailed`. Default is `none`. + +- `metrics.accounts.default-verbosity` - verbosity for accounts not specified in next sections. Allowed values: + `none, basic, detailed`. Default is `none`. - `metrics.accounts.basic-verbosity` - a list of accounts for which only basic metrics will be submitted. -- `metrics.accounts.detailed-verbosity` - a list of accounts for which all metrics will be submitted. +- `metrics.accounts.detailed-verbosity` - a list of accounts for which all metrics will be submitted. For `JVM` metrics + - `metrics.jmx.enabled` - if equals to `true` then `jvm.gc` and `jvm.memory` metrics will be submitted ## Cache + - `cache.scheme` - set the external Cache Service protocol: `http`, `https`, etc. - `cache.host` - set the external Cache Service destination in format `host:port`. - `cache.path` - set the external Cache Service path, for example `/cache`. -- `cache.internal.scheme` - set the internal Cache Service protocol: `http`, `https`, etc., the internal scheme get priority over the external one when provided. -- `cache.internal.host` - set the internal Cache Service destination in format `host:port`, the internal port get priority over the external one when provided. -- `cache.internal.path` - set the internal Cache Service path, for example `/cache`, the internal path get priority over the external one when provided. +- `cache.internal.scheme` - set the internal Cache Service protocol: `http`, `https`, etc., the internal scheme get + priority over the external one when provided. +- `cache.internal.host` - set the internal Cache Service destination in format `host:port`, the internal port get + priority over the external one when provided. +- `cache.internal.path` - set the internal Cache Service path, for example `/cache`, the internal path get priority over + the external one when provided. - `storage.pbc.enabled` - If set to true, this will allow storing modules’ data in third-party storage. - `storage.pbc.path` - set the external Cache Service path for module caching, for example `/pbc-storage`. -- `cache.api-key-secured` - if set to `true`, will cause Prebid Server to add a special API key header to Prebid Cache requests. +- `cache.api-key-secured` - if set to `true`, will cause Prebid Server to add a special API key header to Prebid Cache + requests. - `pbc.api.key` - set the external Cache Service api key for secured calls. - `cache.query` - appends to the cache path as query string params (used for legacy Auction requests). - `cache.banner-ttl-seconds` - how long (in seconds) banner will be available via the external Cache Service. - `cache.video-ttl-seconds` - how long (in seconds) video creative will be available via the external Cache Service. -- `cache.account..banner-ttl-seconds` - how long (in seconds) banner will be available in Cache Service -for particular publisher account. Overrides `cache.banner-ttl-seconds` property. -- `cache.account..video-ttl-seconds` - how long (in seconds) video creative will be available in Cache Service -for particular publisher account. Overrides `cache.video-ttl-seconds` property. -- `cache.default-ttl-seconds.{banner, video, audio, native}` - a default value how long (in seconds) a creative of the specific type will be available in Cache Service -- `cache.append-trace-info-to-cache-id` - if set to `true`, causes the addition account ID and datacenter to cache UUID: _ACCOUNT-DATACENTER-remainderOfUUID_. Implies that cache UUID will be generated by the Prebid Server. +- `cache.account..banner-ttl-seconds` - how long (in seconds) banner will be available in Cache Service + for particular publisher account. Overrides `cache.banner-ttl-seconds` property. +- `cache.account..video-ttl-seconds` - how long (in seconds) video creative will be available in Cache Service + for particular publisher account. Overrides `cache.video-ttl-seconds` property. +- `cache.default-ttl-seconds.{banner, video, audio, native}` - a default value how long (in seconds) a creative of the + specific type will be available in Cache Service +- `cache.append-trace-info-to-cache-id` - if set to `true`, causes the addition account ID and datacenter to cache UUID: + _ACCOUNT-DATACENTER-remainderOfUUID_. Implies that cache UUID will be generated by the Prebid Server. ## Application settings (account configuration, stored ad unit configurations, stored requests) -Preconfigured application settings can be obtained from multiple data sources consequently: + +Preconfigured application settings can be obtained from multiple data sources consequently: + 1. Try to fetch from filesystem data source (if configured). 2. Try to fetch from database data source (if configured). 3. Try to fetch from http data source (if configured). @@ -311,10 +413,12 @@ Preconfigured application settings can be obtained from multiple data sources co Warning! Application will not start in case of no one data source is defined and you'll get an exception in logs. For requests validation mode available next options: + - `settings.fail-on-unknown-bidders` - fail with validation error or just make warning for unknown bidders. - `settings.fail-on-disabled-bidders` - fail with validation error or just make warning for disabled bidders. For filesystem data source available next options: + - `settings.filesystem.settings-filename` - location of file settings. - `settings.filesystem.stored-requests-dir` - directory with stored requests. - `settings.filesystem.stored-imps-dir` - directory with stored imps. @@ -322,6 +426,7 @@ For filesystem data source available next options: - `settings.filesystem.categories-dir` - directory with categories. For database data source available next options: + - `settings.database.type` - type of database to be used: `mysql` or `postgres`. - `settings.database.host` - database destination host. - `settings.database.port` - database destination port. @@ -329,72 +434,96 @@ For database data source available next options: - `settings.database.user` - database user. - `settings.database.password` - database password. - `settings.database.pool-size` - set the initial/min/max pool size of database connections. -- `settings.database.idle-connection-timeout` - Set the idle timeout, time unit is seconds. Zero means don't timeout. This determines if a connection will timeout and be closed and get back to the pool if no data is received nor sent within the timeout. -- `settings.database.enable-prepared-statement-caching` - Enable caching of the prepared statements so that they can be reused. Defaults to `false`. Please be vary of the DB server limitations as cache instances is per-database-connection. -- `settings.database.max-prepared-statement-cache-size` - Set the maximum size of the prepared statement cache. Defaults to `256`. Has any effect only when `settings.database.enable-prepared-statement-caching` is set to `true`. Please note that the cache size is multiplied by `settings.database.pool-size`. +- `settings.database.idle-connection-timeout` - Set the idle timeout, time unit is seconds. Zero means don't timeout. + This determines if a connection will timeout and be closed and get back to the pool if no data is received nor sent + within the timeout. +- `settings.database.enable-prepared-statement-caching` - Enable caching of the prepared statements so that they can be + reused. Defaults to `false`. Please be vary of the DB server limitations as cache instances is + per-database-connection. +- `settings.database.max-prepared-statement-cache-size` - Set the maximum size of the prepared statement cache. Defaults + to `256`. Has any effect only when `settings.database.enable-prepared-statement-caching` is set to `true`. Please note + that the cache size is multiplied by `settings.database.pool-size`. - `settings.database.account-query` - the SQL query to fetch account. - `settings.database.stored-requests-query` - the SQL query to fetch stored requests. - `settings.database.amp-stored-requests-query` - the SQL query to fetch AMP stored requests. - `settings.database.stored-responses-query` - the SQL query to fetch stored responses. -- `settings.database.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make database client more robust. +- `settings.database.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make database client + more robust. - `settings.database.circuit-breaker.opening-threshold` - the number of failures before opening the circuit. -- `settings.database.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached. +- `settings.database.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures + count reached (must be a multiple of 1000). - `settings.database.circuit-breaker.closing-interval-ms` - time spent in open state before attempting to re-try. For HTTP data source available next options: + - `settings.http.endpoint` - the url to fetch stored requests. - `settings.http.amp-endpoint` - the url to fetch AMP stored requests. - `settings.http.video-endpoint` - the url to fetch video stored requests. - `settings.http.category-endpoint` - the url to fetch categories for long form video. -- `settings.http.rfc3986-compatible` - if equals to `true` the url will be build according to RFC 3986, `false` by default +- `settings.http.rfc3986-compatible` - if equals to `true` the url will be build according to RFC 3986, `false` by + default For account processing rules available next options: + - `settings.enforce-valid-account` - if equals to `true` then request without account id will be rejection with 401. -- `settings.generate-storedrequest-bidrequest-id` - overrides `bidrequest.id` in amp or app stored request with generated UUID if true. Default value is false. This flag can be overridden by setting `bidrequest.id` as `{{UUID}}` placeholder directly in stored request. +- `settings.generate-storedrequest-bidrequest-id` - overrides `bidrequest.id` in amp or app stored request with + generated UUID if true. Default value is false. This flag can be overridden by setting `bidrequest.id` as `{{UUID}}` + placeholder directly in stored request. -It is possible to specify default account configuration values that will be assumed if account config have them +It is possible to specify default account configuration values that will be assumed if account config have them unspecified or missing at all. Example: + ```yaml -settings: - default-account-config: > - { - "auction": { - "default-integration": "pbjs" - "events": { - "enabled": true +settings: + default-account-config: > + { + "auction": { + "default-integration": "pbjs" + "events": { + "enabled": true + } + }, + "privacy": { + "gdpr": { + "enabled": true + } + } } - }, - "privacy": { - "gdpr": { - "enabled": true - } - } - } ``` + See [application settings](application-settings.md) for full reference of available configuration parameters. For caching available next options: + - `settings.in-memory-cache.ttl-seconds` - how long (in seconds) data will be available in LRU cache. - `settings.in-memory-cache.cache-size` - the size of LRU cache. - `settings.in-memory-cache.jitter-seconds` - jitter (in seconds) for `settings.in-memory-cache.ttl-seconds` parameter. - `settings.in-memory-cache.notification-endpoints-enabled` - if equals to `true` two additional endpoints will be -available: [/storedrequests/openrtb2](endpoints/storedrequests/openrtb2.md) and [/storedrequests/amp](endpoints/storedrequests/amp.md). -- `settings.in-memory-cache.account-invalidation-enabled` - if equals to `true` additional admin protected endpoints will be -available: `/cache/invalidate?account={accountId}` which remove account from the cache. + available: [/storedrequests/openrtb2](endpoints/storedrequests/openrtb2.md) + and [/storedrequests/amp](endpoints/storedrequests/amp.md). +- `settings.in-memory-cache.account-invalidation-enabled` - if equals to `true` additional admin protected endpoints + will be + available: `/cache/invalidate?account={accountId}` which remove account from the cache. - `settings.in-memory-cache.http-update.endpoint` - the url to fetch stored request updates. - `settings.in-memory-cache.http-update.amp-endpoint` - the url to fetch AMP stored request updates. - `settings.in-memory-cache.http-update.refresh-rate` - refresh period in ms for stored request updates. - `settings.in-memory-cache.http-update.timeout` - timeout for obtaining stored request updates. - `settings.in-memory-cache.database-update.init-query` - initial query for fetching all stored requests at the startup. -- `settings.in-memory-cache.database-update.update-query` - a query for periodical update of stored requests, that should -contain 'WHERE last_updated > ?' for MySQL and 'WHERE last_updated > $1' for Postgresql to fetch only the records that were updated since previous check. -- `settings.in-memory-cache.database-update.amp-init-query` - initial query for fetching all AMP stored requests at the startup. -- `settings.in-memory-cache.database-update.amp-update-query` - a query for periodical update of AMP stored requests, that should -contain 'WHERE last_updated > ?' for MySQL and 'WHERE last_updated > $1' for Postgresql to fetch only the records that were updated since previous check. +- `settings.in-memory-cache.database-update.update-query` - a query for periodical update of stored requests, that + should + contain 'WHERE last_updated > ?' for MySQL and 'WHERE last_updated > $1' for Postgresql to fetch only the records that + were updated since previous check. +- `settings.in-memory-cache.database-update.amp-init-query` - initial query for fetching all AMP stored requests at the + startup. +- `settings.in-memory-cache.database-update.amp-update-query` - a query for periodical update of AMP stored requests, + that should + contain 'WHERE last_updated > ?' for MySQL and 'WHERE last_updated > $1' for Postgresql to fetch only the records that + were updated since previous check. - `settings.in-memory-cache.database-update.refresh-rate` - refresh period in ms for stored request updates. - `settings.in-memory-cache.database-update.timeout` - timeout for obtaining stored request updates. For S3 storage configuration + - `settings.in-memory-cache.s3-update.refresh-rate` - refresh period in ms for stored request updates in S3 - `settings.s3.access-key-id` - an access key (optional) - `settings.s3.secret-access-key` - a secret access key (optional) @@ -407,19 +536,23 @@ For S3 storage configuration - `settings.s3.stored-requests-dir` - a directory with stored requests - `settings.s3.stored-responses-dir` - a directory with stored responses -If `settings.s3.access-key-id` and `settings.s3.secret-access-key` are not specified in the Prebid Server configuration then AWS credentials will be looked up in this order: +If `settings.s3.access-key-id` and `settings.s3.secret-access-key` are not specified in the Prebid Server configuration +then AWS credentials will be looked up in this order: + - Java System Properties - `aws.accessKeyId` and `aws.secretAccessKey` - Environment Variables - `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` -- Web Identity Token credentials from system properties or environment variables +- Web Identity Token credentials from system properties or environment variables - Credential profiles file at the default location (`~/.aws/credentials`) shared by all AWS SDKs and the AWS CLI -- Credentials delivered through the Amazon EC2 container service if "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" environment variable is set and security manager has permission to access the variable, +- Credentials delivered through the Amazon EC2 container service if "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" environment + variable is set and security manager has permission to access the variable, - Instance profile credentials delivered through the Amazon EC2 metadata service - For targeting available next options: + - `settings.targeting.truncate-attr-chars` - set the max length for names of targeting keywords (0 means no truncation). ## Host Cookie + - `host-cookie.optout-cookie.name` - set the cookie name for optout checking. - `host-cookie.optout-cookie.value` - set the cookie value for optout checking. - `host-cookie.opt-out-url` - set the url for user redirect in case of opt out. @@ -431,53 +564,72 @@ For targeting available next options: - `host-cookie.max-cookie-size-bytes` - a size limit for UIDs Cookie. Valid values are `0` (disabled) and `>500`. ## Google Recaptcha + - `recaptcha-url` - the url for Google Recaptcha service to submit user verification. - `recaptcha-secret` - Google Recaptcha secret string given to certain domain account. ## Server status + - `status-response` - message returned by ApplicationChecker in /status endpoint when server is ready to serve requests. -If not defined in config all other Health Checkers would be disabled and endpoint will respond with 'No Content' (204) status with empty body. + If not defined in config all other Health Checkers would be disabled and endpoint will respond with 'No Content' (204) + status with empty body. ## Health Check -- `health-check.database.enabled` - if equals to `true` the database health check will be enabled to periodically check database status. + +- `health-check.database.enabled` - if equals to `true` the database health check will be enabled to periodically check + database status. - `health-check.database.refresh-period-ms` - the refresh period for database status updates. -- `health-check.geolocation.enabled` - if equals to `true` the geolocation service health check will be enabled to periodically check the status. +- `health-check.geolocation.enabled` - if equals to `true` the geolocation service health check will be enabled to + periodically check the status. - `health-check.geolocation.refresh-period-ms` - the refresh period for geolocation service status updates. ## GDPR + - `gdpr.eea-countries` - comma separated list of countries in European Economic Area (EEA). - `gdpr.default-value` - determines GDPR in scope default value (if no information in request and no geolocation data). - `gdpr.host-vendor-id` - the organization running a cluster of Prebid Servers. - `datacenter-region` - the datacenter region of a cluster of Prebid Servers - `gdpr.enabled` - gdpr feature switch. Default `true`. - `gdpr.purposes.pN.enforce-purpose` - define type of enforcement confirmation: `no`/`basic`/`full`. Default `full` -- `gdpr.purposes.pN.enforce-vendors` - if equals to `true`, user must give consent to use vendors. Purposes will be omitted. Default `true` +- `gdpr.purposes.pN.enforce-vendors` - if equals to `true`, user must give consent to use vendors. Purposes will be + omitted. Default `true` - `gdpr.purposes.pN.vendor-exceptions[]` - bidder names that will be treated opposite to `pN.enforce-vendors` value. -- `gdpr.special-features.sfN.enforce` - if equals to `true`, special feature will be enforced for purpose. Default `true` +- `gdpr.special-features.sfN.enforce` - if equals to `true`, special feature will be enforced for purpose. Default + `true` - `gdpr.special-features.sfN.vendor-exceptions[]` - bidder names that will be treated opposite to `sfN.enforce` value. - `gdpr.purpose-one-treatment-interpretation` - option that allows to skip the Purpose one enforcement workflow. - `gdpr.vendorlist.default-timeout-ms` - default operation timeout for obtaining new vendor list. - `gdpr.vendorlist.v2.http-endpoint-template` - template string for vendor list url version 2. -- `gdpr.vendorlist.v2.refresh-missing-list-period-ms` - time to wait between attempts to fetch vendor list version that previously was reported to be missing by origin. Default `3600000` (one hour). -- `gdpr.vendorlist.v2.fallback-vendor-list-path` - location on the file system of the fallback vendor list that will be used in place of missing vendor list versions. Optional. +- `gdpr.vendorlist.v2.refresh-missing-list-period-ms` - time to wait between attempts to fetch vendor list version that + previously was reported to be missing by origin. Default `3600000` (one hour). +- `gdpr.vendorlist.v2.fallback-vendor-list-path` - location on the file system of the fallback vendor list that will be + used in place of missing vendor list versions. Optional. - `gdpr.vendorlist.v2.deprecated` - Flag to show is this vendor list is deprecated or not. -- `gdpr.vendorlist.v2.cache-dir` - directory for local storage cache for vendor list. Should be with `WRITE` permissions for user application run from. +- `gdpr.vendorlist.v2.cache-dir` - directory for local storage cache for vendor list. Should be with `WRITE` permissions + for user application run from. ## CCPA + - `ccpa.enforce` - if equals to `true` enforces to check ccpa policy, otherwise ignore ccpa verification. ## LMT + - `lmt.enforce` - if equals to `true` enforces to check lmt policy, otherwise ignore lmt verification. ## Geo Location -- `geolocation.enabled` - if equals to `true` the geo location service will be used to determine the country for client request. -- `geolocation.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make geo location client more robust. + +- `geolocation.enabled` - if equals to `true` the geo location service will be used to determine the country for client + request. +- `geolocation.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make geo location client + more robust. - `geolocation.circuit-breaker.opening-threshold` - the number of failures before opening the circuit. -- `geolocation.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached. +- `geolocation.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count + reached (must be a multiple of 1000). - `geolocation.circuit-breaker.closing-interval-ms` - time spent in open state before attempting to re-try. - `geolocation.type` - set the geo location service provider, can be `maxmind` or custom provided by hosting company. - `geolocation.maxmind` - section for [MaxMind](https://www.maxmind.com) configuration as geo location service provider. -- `geolocation.maxmind.remote-file-syncer` - use RemoteFileSyncer component for downloading/updating MaxMind database file. See [RemoteFileSyncer](#remote-file-syncer) section for its configuration. +- `geolocation.maxmind.remote-file-syncer` - use RemoteFileSyncer component for downloading/updating MaxMind database + file. See [RemoteFileSyncer](#remote-file-syncer) section for its configuration. - `geolocation.configurations[]` - a list of geo-lookup configurations for the `configuration` `geolocation.type` - `geolocation.configurations[].address-pattern` - an address pattern for matching an IP to look up - `geolocation.configurations[].geo-info.continent` - a continent to return on the `configuration` geo-lookup @@ -488,31 +640,39 @@ If not defined in config all other Health Checkers would be disabled and endpoin - `geolocation.configurations[].geo-info.metro-google` - a metro Google to return on the `configuration` geo-lookup - `geolocation.configurations[].geo-info.metro-nielsen` - a metro Nielsen to return on the `configuration` geo-lookup - `geolocation.configurations[].geo-info.zip` - a zip to return on the `configuration` geo-lookup -- `geolocation.configurations[].geo-info.connection-speed` - a connection-speed to return on the `configuration` geo-lookup +- `geolocation.configurations[].geo-info.connection-speed` - a connection-speed to return on the `configuration` + geo-lookup - `geolocation.configurations[].geo-info.lat` - a lat to return on the `configuration` geo-lookup - `geolocation.configurations[].geo-info.lon` - a lon to return on the `configuration` geo-lookup - `geolocation.configurations[].geo-info.time-zone` - a time zone to return on the `configuration` geo-lookup ## IPv6 + - `ipv6.always-mask-right` - a bit mask for masking an IPv6 address of the device - `ipv6.anon-left-mask-bits` - a bit mask for anonymizing an IPv6 address of the device - `ipv6.private-networks` - a list of known private/local networks to skip masking of an IP address of the device ## Analytics -- `analytics.global.adapters` - Names of analytics adapters that will work for each request, except those disabled at the account level. + +- `analytics.global.adapters` - Names of analytics adapters that will work for each request, except those disabled at + the account level. For the `pubstack` analytics adapter -- `analytics.pubstack.enabled` - if equals to `true` the Pubstack analytics module will be enabled. Default value is `false`. -- `analytics.pubstack.endpoint` - url for reporting events and fetching configuration. + +- `analytics.pubstack.enabled` - if equals to `true` the Pubstack analytics module will be enabled. Default value is + `false`. +- `analytics.pubstack.endpoint` - url for reporting events and fetching configuration. - `analytics.pubstack.scopeid` - defined the scope provided by the Pubstack Support Team. - `analytics.pubstack.configuration-refresh-delay-ms` - delay in milliseconds between remote config updates. - `analytics.pubstack.timeout-ms` - timeout in milliseconds for report and fetch config requests. -- `analytics.pubstack.buffers.size-bytes` - threshold in bytes for buffer to send events. +- `analytics.pubstack.buffers.size-bytes` - threshold in bytes for buffer to send events. - `analytics.pubstack.buffers.count` - threshold in events count for buffer to send events - `analytics.pubstack.buffers.report-ttl-ms` - max period between two reports. For the `greenbids` analytics adapter -- `analytics.greenbids.enabled` - if equals to `true` the Greenbids analytics module will be enabled. Default value is `false`. + +- `analytics.greenbids.enabled` - if equals to `true` the Greenbids analytics module will be enabled. Default value is + `false`. - `analytics.greenbids.analytics-server-version` - a server version to add to the event - `analytics.greenbids.analytics-server` - url for reporting events - `analytics.greenbids.timeout-ms` - timeout in milliseconds for report requests. @@ -520,10 +680,12 @@ For the `greenbids` analytics adapter - `analytics.greenbids.default-sampling-rate` - a default sampling rate for report requests For the `agma` analytics adapter + - `analytics.agma.enabled` - if equals to `true` the Agma analytics module will be enabled. Default value is `false`. - `analytics.agma.endpoint.url` - url for reporting events - `analytics.agma.endpoint.timeout-ms` - timeout in milliseconds for report requests. -- `analytics.agma.endpoint.gzip` - if equals to `true` the Agma analytics module enables gzip encoding. Default value is `false`. +- `analytics.agma.endpoint.gzip` - if equals to `true` the Agma analytics module enables gzip encoding. Default value is + `false`. - `analytics.agma.buffers.size-bytes` - threshold in bytes for buffer to send events. - `analytics.agma.buffers.count` - threshold in events count for buffer to send events. - `analytics.agma.buffers.timeout-ms` - max period between two reports. @@ -532,27 +694,36 @@ For the `agma` analytics adapter - `analytics.agma.accounts[].site-app-id` - a site or app id to match an event to send ## Modules -- `hooks.admin.module-execution` - a key-value map, where a key is a module name and a value is a boolean, that defines whether modules hooks should/should not be always executed; if the module is not specified it is executed by default when it's present in the execution plan + +- `hooks.admin.module-execution` - a key-value map, where a key is a module name and a value is a boolean, that defines + whether modules hooks should/should not be always executed; if the module is not specified it is executed by default + when it's present in the execution plan - `settings.modules.require-config-to-invoke` - when enabled it requires a runtime config to exist for a module. ## Debugging -- `debug.override-token` - special string token for overriding Prebid Server account and/or adapter debug information presence in the auction response. + +- `debug.override-token` - special string token for overriding Prebid Server account and/or adapter debug information + presence in the auction response. To override (force enable) account and/or bidder adapter debug setting, a client must include `x-pbs-debug-override` HTTP header in the auction call containing same token as in the `debug.override-token` property. This will make Prebid Server ignore account `auction.debug-allow` and/or `adapters..debug.allow` properties. ## Privacy Sandbox + - `auction.privacysandbox.topicsdomain` - the list of Sec-Browsing-Topics for the Privacy Sandbox ## AMP + - `amp.custom-targeting` - a list of bidders that support custom targeting ## Hooks + - `hooks.host-execution-plan` - a host execution plan for modules - `hooks.default-account-execution-plan` - a default account execution plan ## Price Floors Debug + - `price-floors.enabled` - enables price floors for account if true. Defaults to true. - `price-floors.min-max-age-sec` - a price floors fetch data time to live in cache. - `price-floors.min-period-sec` - a refresh period for fetching price floors data. diff --git a/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java b/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java index c8f04701de6..a6e9c456479 100755 --- a/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java +++ b/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java @@ -10,7 +10,6 @@ import org.prebid.server.metric.Metrics; import org.prebid.server.vertx.CircuitBreaker; -import java.time.Clock; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -31,13 +30,16 @@ public CircuitBreakerSecuredGeoLocationService(Vertx vertx, Metrics metrics, int openingThreshold, long openingIntervalMs, - long closingIntervalMs, - Clock clock) { + long closingIntervalMs) { this.geoLocationService = Objects.requireNonNull(geoLocationService); - breaker = new CircuitBreaker("geo_cb", Objects.requireNonNull(vertx), - openingThreshold, openingIntervalMs, closingIntervalMs, Objects.requireNonNull(clock)) + breaker = new CircuitBreaker( + "geo_cb", + Objects.requireNonNull(vertx), + openingThreshold, + openingIntervalMs, + closingIntervalMs) .openHandler(ignored -> circuitOpened()) .halfOpenHandler(ignored -> circuitHalfOpened()) .closeHandler(ignored -> circuitClosed()); diff --git a/src/main/java/org/prebid/server/spring/config/GeoLocationConfiguration.java b/src/main/java/org/prebid/server/spring/config/GeoLocationConfiguration.java index bfb56b8c0c1..7058f06e903 100644 --- a/src/main/java/org/prebid/server/spring/config/GeoLocationConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/GeoLocationConfiguration.java @@ -30,7 +30,6 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; -import java.time.Clock; import java.time.ZoneId; import java.util.ArrayList; import java.util.List; @@ -70,13 +69,15 @@ CircuitBreakerSecuredGeoLocationService circuitBreakerSecuredGeoLocationService( Vertx vertx, Metrics metrics, FileSyncerProperties fileSyncerProperties, - @Qualifier("maxMindCircuitBreakerProperties") CircuitBreakerProperties circuitBreakerProperties, - Clock clock) { - - return new CircuitBreakerSecuredGeoLocationService(vertx, - createGeoLocationService(fileSyncerProperties, vertx), metrics, - circuitBreakerProperties.getOpeningThreshold(), circuitBreakerProperties.getOpeningIntervalMs(), - circuitBreakerProperties.getClosingIntervalMs(), clock); + @Qualifier("maxMindCircuitBreakerProperties") CircuitBreakerProperties circuitBreakerProperties) { + + return new CircuitBreakerSecuredGeoLocationService( + vertx, + createGeoLocationService(fileSyncerProperties, vertx), + metrics, + circuitBreakerProperties.getOpeningThreshold(), + circuitBreakerProperties.getOpeningIntervalMs(), + circuitBreakerProperties.getClosingIntervalMs()); } private GeoLocationService createGeoLocationService(FileSyncerProperties properties, Vertx vertx) { diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java index 024f6888b09..597feb1f1ac 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -660,8 +660,7 @@ CircuitBreakerSecuredHttpClient circuitBreakerSecuredHttpClient( Metrics metrics, HttpClientProperties httpClientProperties, @Qualifier("httpClientCircuitBreakerProperties") - HttpClientCircuitBreakerProperties circuitBreakerProperties, - Clock clock) { + HttpClientCircuitBreakerProperties circuitBreakerProperties) { final HttpClient httpClient = createBasicHttpClient(vertx, httpClientProperties); @@ -672,8 +671,7 @@ CircuitBreakerSecuredHttpClient circuitBreakerSecuredHttpClient( circuitBreakerProperties.getOpeningThreshold(), circuitBreakerProperties.getOpeningIntervalMs(), circuitBreakerProperties.getClosingIntervalMs(), - circuitBreakerProperties.getIdleExpireHours(), - clock); + circuitBreakerProperties.getIdleExpireHours()); } private static BasicHttpClient createBasicHttpClient(Vertx vertx, HttpClientProperties httpClientProperties) { diff --git a/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java b/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java index 770bf56bafe..ded875b91a7 100644 --- a/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java @@ -167,8 +167,7 @@ CircuitBreakerSecuredDatabaseClient circuitBreakerSecuredAsyncDatabaseClient( metrics, circuitBreakerProperties.getOpeningThreshold(), circuitBreakerProperties.getOpeningIntervalMs(), - circuitBreakerProperties.getClosingIntervalMs(), - clock); + circuitBreakerProperties.getClosingIntervalMs()); } private static BasicDatabaseClient createBasicDatabaseClient(Pool pool, diff --git a/src/main/java/org/prebid/server/vertx/CircuitBreaker.java b/src/main/java/org/prebid/server/vertx/CircuitBreaker.java index 02d30a8f76d..dfd57126952 100644 --- a/src/main/java/org/prebid/server/vertx/CircuitBreaker.java +++ b/src/main/java/org/prebid/server/vertx/CircuitBreaker.java @@ -5,14 +5,9 @@ import io.vertx.core.Handler; import io.vertx.core.Vertx; -import java.time.Clock; import java.util.Objects; import java.util.function.Supplier; -/** - * Wrapper over Vert.x {@link io.vertx.circuitbreaker.CircuitBreaker} with functionality - * to reset failure counter to adjust open-circuit time frame. - */ public class CircuitBreaker { private final io.vertx.circuitbreaker.CircuitBreaker breaker; @@ -21,8 +16,7 @@ public CircuitBreaker(String name, Vertx vertx, int openingThreshold, long openingIntervalMs, - long closingIntervalMs, - Clock clock) { + long closingIntervalMs) { breaker = io.vertx.circuitbreaker.CircuitBreaker.create( Objects.requireNonNull(name), @@ -30,35 +24,24 @@ public CircuitBreaker(String name, new CircuitBreakerOptions() .setNotificationPeriod(0) .setMaxFailures(openingThreshold) + .setFailuresRollingWindow(openingIntervalMs) .setResetTimeout(closingIntervalMs)); } - /** - * Executes the given operation with the circuit breaker control. - */ public Future execute(Supplier> command) { return breaker.execute(command); } - /** - * Sets a {@link Handler} invoked when the circuit breaker state switches to open. - */ public CircuitBreaker openHandler(Handler handler) { breaker.openHandler(handler); return this; } - /** - * Sets a {@link Handler} invoked when the circuit breaker state switches to half-open. - */ public CircuitBreaker halfOpenHandler(Handler handler) { breaker.halfOpenHandler(handler); return this; } - /** - * Sets a {@link Handler} invoked when the circuit breaker state switches to close. - */ public CircuitBreaker closeHandler(Handler handler) { breaker.closeHandler(handler); return this; diff --git a/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java b/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java index 36cd9de6b26..17ca4d0217f 100644 --- a/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java +++ b/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java @@ -11,7 +11,6 @@ import org.prebid.server.metric.Metrics; import org.prebid.server.vertx.CircuitBreaker; -import java.time.Clock; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -34,8 +33,7 @@ public CircuitBreakerSecuredDatabaseClient(Vertx vertx, Metrics metrics, int openingThreshold, long openingIntervalMs, - long closingIntervalMs, - Clock clock) { + long closingIntervalMs) { this.databaseClient = Objects.requireNonNull(databaseClient); @@ -44,8 +42,7 @@ public CircuitBreakerSecuredDatabaseClient(Vertx vertx, Objects.requireNonNull(vertx), openingThreshold, openingIntervalMs, - closingIntervalMs, - Objects.requireNonNull(clock)) + closingIntervalMs) .openHandler(ignored -> circuitOpened()) .halfOpenHandler(ignored -> circuitHalfOpened()) .closeHandler(ignored -> circuitClosed()); diff --git a/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java b/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java index 5cbee944a75..efae04b2177 100644 --- a/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java +++ b/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java @@ -15,7 +15,6 @@ import java.net.MalformedURLException; import java.net.URL; -import java.time.Clock; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -41,13 +40,12 @@ public CircuitBreakerSecuredHttpClient(Vertx vertx, int openingThreshold, long openingIntervalMs, long closingIntervalMs, - int idleExpireHours, - Clock clock) { + int idleExpireHours) { this.httpClient = Objects.requireNonNull(httpClient); circuitBreakerCreator = name -> createCircuitBreaker( - name, vertx, openingThreshold, openingIntervalMs, closingIntervalMs, clock, metrics); + name, vertx, openingThreshold, openingIntervalMs, closingIntervalMs, metrics); circuitBreakerByName = Caffeine.newBuilder() .expireAfterAccess(idleExpireHours, TimeUnit.HOURS) @@ -88,7 +86,6 @@ private CircuitBreaker createCircuitBreaker(String name, int openingThreshold, long openingIntervalMs, long closingIntervalMs, - Clock clock, Metrics metrics) { final CircuitBreaker circuitBreaker = new CircuitBreaker( @@ -96,8 +93,7 @@ private CircuitBreaker createCircuitBreaker(String name, Objects.requireNonNull(vertx), openingThreshold, openingIntervalMs, - closingIntervalMs, - Objects.requireNonNull(clock)) + closingIntervalMs) .openHandler(ignored -> circuitOpened(name)) .halfOpenHandler(ignored -> circuitHalfOpened(name)) .closeHandler(ignored -> circuitClosed(name)); diff --git a/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java b/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java index d278baa7f34..6afe645cc4d 100644 --- a/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java +++ b/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java @@ -16,9 +16,6 @@ import org.prebid.server.geolocation.model.GeoInfo; import org.prebid.server.metric.Metrics; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; @@ -34,7 +31,6 @@ public class CircuitBreakerSecuredGeoLocationServiceTest { private Vertx vertx; - private Clock clock; @Mock private GeoLocationService wrappedGeoLocationService; @Mock @@ -45,9 +41,8 @@ public class CircuitBreakerSecuredGeoLocationServiceTest { @BeforeEach public void setUp() { vertx = Vertx.vertx(); - clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); geoLocationService = new CircuitBreakerSecuredGeoLocationService(vertx, wrappedGeoLocationService, metrics, 1, - 100L, 200L, clock); + 1000L, 200L); } @AfterEach @@ -145,7 +140,7 @@ public void lookupShouldSucceedsIfCircuitIsHalfOpenedAndWrappedGeoLocationSuccee public void lookupShouldFailsWithOriginalExceptionIfOpeningIntervalExceeds() { // given geoLocationService = new CircuitBreakerSecuredGeoLocationService(vertx, wrappedGeoLocationService, metrics, 2, - 100L, 200L, clock); + 100L, 200L); givenWrappedGeoLocationReturning( Future.failedFuture(new RuntimeException("exception1")), diff --git a/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java b/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java index ef081ac0e79..a543f3d9faf 100644 --- a/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java +++ b/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java @@ -1,7 +1,6 @@ package org.prebid.server.vertx; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.junit5.VertxExtension; @@ -12,9 +11,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -26,15 +22,12 @@ public class CircuitBreakerTest { private Vertx vertx; - private Clock clock; - private CircuitBreaker circuitBreaker; @BeforeEach public void setUp() { vertx = Vertx.vertx(); - clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - circuitBreaker = new CircuitBreaker("name", vertx, 1, 100L, 200L, clock); + circuitBreaker = new CircuitBreaker("name", vertx, 1, 1000L, 200L); } @AfterEach @@ -118,7 +111,7 @@ public void executeShouldSucceedsIfCircuitIsHalfOpenedAndOperationSucceeds() { @Test public void executeShouldFailsWithOriginalExceptionIfOpeningIntervalExceeds() { // given - circuitBreaker = new CircuitBreaker("name", vertx, 2, 100L, 200L, clock); + circuitBreaker = new CircuitBreaker("name", vertx, 2, 100L, 200L); // when final Future future1 = executeWithFail("exception1"); diff --git a/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java b/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java index 8e751f4ba53..f91edaa1db3 100644 --- a/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java +++ b/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java @@ -41,7 +41,6 @@ public class CircuitBreakerSecuredDatabaseClientTest { private Vertx vertx; - private Clock clock; @Mock private DatabaseClient wrappedDatabaseClient; @Mock @@ -54,10 +53,9 @@ public class CircuitBreakerSecuredDatabaseClientTest { @BeforeEach public void setUp() { vertx = Vertx.vertx(); - clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - timeout = new TimeoutFactory(clock).create(500L); + timeout = new TimeoutFactory(Clock.fixed(Instant.now(), ZoneId.systemDefault())).create(500L); - target = new CircuitBreakerSecuredDatabaseClient(vertx, wrappedDatabaseClient, metrics, 1, 100L, 200L, clock); + target = new CircuitBreakerSecuredDatabaseClient(vertx, wrappedDatabaseClient, metrics, 1, 1000L, 200L); } @AfterEach @@ -186,7 +184,7 @@ public void executeQueryShouldReturnResultIfCircuitIsHalfOpenedAndQuerySucceeded @Test public void executeQueryShouldFailsWithOriginalExceptionIfOpeningIntervalExceeds(VertxTestContext context) { // given - target = new CircuitBreakerSecuredDatabaseClient(vertx, wrappedDatabaseClient, metrics, 2, 100L, 200L, clock); + target = new CircuitBreakerSecuredDatabaseClient(vertx, wrappedDatabaseClient, metrics, 2, 100L, 200L); givenExecuteQueryReturning(asList( Future.failedFuture(new RuntimeException("exception1")), diff --git a/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java b/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java index f0788671e1f..629446d6a4a 100644 --- a/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java +++ b/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java @@ -18,9 +18,6 @@ import org.prebid.server.metric.Metrics; import org.prebid.server.vertx.httpclient.model.HttpClientResponse; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import java.util.function.LongSupplier; @@ -41,7 +38,6 @@ public class CircuitBreakerSecuredHttpClientTest { private Vertx vertx; - private Clock clock; @Mock private HttpClient wrappedHttpClient; @Mock @@ -52,8 +48,7 @@ public class CircuitBreakerSecuredHttpClientTest { @BeforeEach public void setUp() { vertx = Vertx.vertx(); - clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - httpClient = new CircuitBreakerSecuredHttpClient(vertx, wrappedHttpClient, metrics, 1, 100L, 200L, 24, clock); + httpClient = new CircuitBreakerSecuredHttpClient(vertx, wrappedHttpClient, metrics, 1, 1000L, 200L, 24); } @AfterEach @@ -171,7 +166,7 @@ public void requestShouldSucceedIfCircuitIsHalfOpenedAndWrappedHttpClientSucceed @Test public void requestShouldFailWithOriginalExceptionIfOpeningIntervalExceeds() { // given - httpClient = new CircuitBreakerSecuredHttpClient(vertx, wrappedHttpClient, metrics, 2, 100L, 200L, 24, clock); + httpClient = new CircuitBreakerSecuredHttpClient(vertx, wrappedHttpClient, metrics, 2, 100L, 200L, 24); givenHttpClientReturning(new RuntimeException("exception1"), new RuntimeException("exception2")); From 28416bf5059e06b925529c3b93298ea3df16e3c2 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 14 Apr 2026 22:41:11 +0300 Subject: [PATCH 03/18] Removed circuit breaker wrapper entirely. --- ...rcuitBreakerSecuredGeoLocationService.java | 38 ++--- .../prebid/server/vertx/CircuitBreaker.java | 56 ------ .../CircuitBreakerSecuredDatabaseClient.java | 37 ++-- .../CircuitBreakerSecuredHttpClient.java | 21 ++- .../server/handler/SetuidHandlerTest.java | 4 +- .../vendorlist/VendorListServiceTest.java | 2 - .../server/vertx/CircuitBreakerTest.java | 160 ------------------ .../server/vertx/CloseableAdapterTest.java | 3 - 8 files changed, 37 insertions(+), 284 deletions(-) delete mode 100644 src/main/java/org/prebid/server/vertx/CircuitBreaker.java delete mode 100644 src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java diff --git a/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java b/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java index a6e9c456479..57d01997b96 100755 --- a/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java +++ b/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java @@ -1,17 +1,17 @@ package org.prebid.server.geolocation; +import io.vertx.circuitbreaker.CircuitBreaker; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.circuitbreaker.CircuitBreakerState; import io.vertx.core.Future; import io.vertx.core.Vertx; import org.prebid.server.execution.timeout.Timeout; import org.prebid.server.geolocation.model.GeoInfo; -import org.prebid.server.log.ConditionalLogger; import org.prebid.server.log.Logger; import org.prebid.server.log.LoggerFactory; import org.prebid.server.metric.Metrics; -import org.prebid.server.vertx.CircuitBreaker; import java.util.Objects; -import java.util.concurrent.TimeUnit; /** * Wrapper for geolocation service with circuit breaker. @@ -19,8 +19,6 @@ public class CircuitBreakerSecuredGeoLocationService implements GeoLocationService { private static final Logger logger = LoggerFactory.getLogger(CircuitBreakerSecuredGeoLocationService.class); - private static final ConditionalLogger conditionalLogger = new ConditionalLogger(logger); - private static final int LOG_PERIOD_SECONDS = 5; private final GeoLocationService geoLocationService; private final CircuitBreaker breaker; @@ -34,17 +32,16 @@ public CircuitBreakerSecuredGeoLocationService(Vertx vertx, this.geoLocationService = Objects.requireNonNull(geoLocationService); - breaker = new CircuitBreaker( + breaker = CircuitBreaker.create( "geo_cb", Objects.requireNonNull(vertx), - openingThreshold, - openingIntervalMs, - closingIntervalMs) - .openHandler(ignored -> circuitOpened()) - .halfOpenHandler(ignored -> circuitHalfOpened()) - .closeHandler(ignored -> circuitClosed()); + new CircuitBreakerOptions() + .setNotificationPeriod(0) + .setMaxFailures(openingThreshold) + .setFailuresRollingWindow(openingIntervalMs) + .setResetTimeout(closingIntervalMs)); - metrics.createGeoLocationCircuitBreakerGauge(breaker::isOpen); + metrics.createGeoLocationCircuitBreakerGauge(() -> breaker.state() != CircuitBreakerState.CLOSED); logger.info("Initialized GeoLocation service with Circuit Breaker"); } @@ -53,19 +50,4 @@ public CircuitBreakerSecuredGeoLocationService(Vertx vertx, public Future lookup(String ip, Timeout timeout) { return breaker.execute(() -> geoLocationService.lookup(ip, timeout)); } - - private void circuitOpened() { - conditionalLogger.warn( - "GeoLocation service is unavailable, circuit opened.", - LOG_PERIOD_SECONDS, - TimeUnit.SECONDS); - } - - private void circuitHalfOpened() { - logger.warn("GeoLocation service is ready to try again, circuit half-opened."); - } - - private void circuitClosed() { - logger.warn("GeoLocation service becomes working, circuit closed."); - } } diff --git a/src/main/java/org/prebid/server/vertx/CircuitBreaker.java b/src/main/java/org/prebid/server/vertx/CircuitBreaker.java deleted file mode 100644 index dfd57126952..00000000000 --- a/src/main/java/org/prebid/server/vertx/CircuitBreaker.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.prebid.server.vertx; - -import io.vertx.circuitbreaker.CircuitBreakerOptions; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Vertx; - -import java.util.Objects; -import java.util.function.Supplier; - -public class CircuitBreaker { - - private final io.vertx.circuitbreaker.CircuitBreaker breaker; - - public CircuitBreaker(String name, - Vertx vertx, - int openingThreshold, - long openingIntervalMs, - long closingIntervalMs) { - - breaker = io.vertx.circuitbreaker.CircuitBreaker.create( - Objects.requireNonNull(name), - Objects.requireNonNull(vertx), - new CircuitBreakerOptions() - .setNotificationPeriod(0) - .setMaxFailures(openingThreshold) - .setFailuresRollingWindow(openingIntervalMs) - .setResetTimeout(closingIntervalMs)); - } - - public Future execute(Supplier> command) { - return breaker.execute(command); - } - - public CircuitBreaker openHandler(Handler handler) { - breaker.openHandler(handler); - return this; - } - - public CircuitBreaker halfOpenHandler(Handler handler) { - breaker.halfOpenHandler(handler); - return this; - } - - public CircuitBreaker closeHandler(Handler handler) { - breaker.closeHandler(handler); - return this; - } - - public boolean isOpen() { - return switch (breaker.state()) { - case OPEN, HALF_OPEN -> true; - case CLOSED -> false; - }; - } -} diff --git a/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java b/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java index 17ca4d0217f..867f2e8c67d 100644 --- a/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java +++ b/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java @@ -1,29 +1,27 @@ package org.prebid.server.vertx.database; +import io.vertx.circuitbreaker.CircuitBreaker; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.circuitbreaker.CircuitBreakerState; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.RowSet; import org.prebid.server.execution.timeout.Timeout; -import org.prebid.server.log.ConditionalLogger; import org.prebid.server.log.Logger; import org.prebid.server.log.LoggerFactory; import org.prebid.server.metric.Metrics; -import org.prebid.server.vertx.CircuitBreaker; import java.util.List; import java.util.Objects; -import java.util.concurrent.TimeUnit; import java.util.function.Function; /** - * Database Client wrapped by {@link CircuitBreaker} to achieve robust operating. + * Database Client wrapped by CircuitBreaker to achieve robust operating. */ public class CircuitBreakerSecuredDatabaseClient implements DatabaseClient { private static final Logger logger = LoggerFactory.getLogger(CircuitBreakerSecuredDatabaseClient.class); - private static final ConditionalLogger conditionalLogger = new ConditionalLogger(logger); - private static final int LOG_PERIOD_SECONDS = 5; private final DatabaseClient databaseClient; private final CircuitBreaker breaker; @@ -37,17 +35,16 @@ public CircuitBreakerSecuredDatabaseClient(Vertx vertx, this.databaseClient = Objects.requireNonNull(databaseClient); - breaker = new CircuitBreaker( + breaker = CircuitBreaker.create( "db_cb", Objects.requireNonNull(vertx), - openingThreshold, - openingIntervalMs, - closingIntervalMs) - .openHandler(ignored -> circuitOpened()) - .halfOpenHandler(ignored -> circuitHalfOpened()) - .closeHandler(ignored -> circuitClosed()); + new CircuitBreakerOptions() + .setNotificationPeriod(0) + .setMaxFailures(openingThreshold) + .setFailuresRollingWindow(openingIntervalMs) + .setResetTimeout(closingIntervalMs)); - metrics.createDatabaseCircuitBreakerGauge(breaker::isOpen); + metrics.createDatabaseCircuitBreakerGauge(() -> breaker.state() != CircuitBreakerState.CLOSED); logger.info("Initialized database client with Circuit Breaker"); } @@ -60,16 +57,4 @@ public Future executeQuery(String query, return breaker.execute(() -> databaseClient.executeQuery(query, params, mapper, timeout)); } - - private void circuitOpened() { - conditionalLogger.warn("Database is unavailable, circuit opened.", LOG_PERIOD_SECONDS, TimeUnit.SECONDS); - } - - private void circuitHalfOpened() { - logger.warn("Database is ready to try again, circuit half-opened."); - } - - private void circuitClosed() { - logger.warn("Database becomes working, circuit closed."); - } } diff --git a/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java b/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java index efae04b2177..7097be2de56 100644 --- a/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java +++ b/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java @@ -1,6 +1,9 @@ package org.prebid.server.vertx.httpclient; import com.github.benmanes.caffeine.cache.Caffeine; +import io.vertx.circuitbreaker.CircuitBreaker; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.circuitbreaker.CircuitBreakerState; import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.core.Vertx; @@ -10,7 +13,6 @@ import org.prebid.server.log.Logger; import org.prebid.server.log.LoggerFactory; import org.prebid.server.metric.Metrics; -import org.prebid.server.vertx.CircuitBreaker; import org.prebid.server.vertx.httpclient.model.HttpClientResponse; import java.net.MalformedURLException; @@ -88,12 +90,14 @@ private CircuitBreaker createCircuitBreaker(String name, long closingIntervalMs, Metrics metrics) { - final CircuitBreaker circuitBreaker = new CircuitBreaker( - "http_cb_" + name, - Objects.requireNonNull(vertx), - openingThreshold, - openingIntervalMs, - closingIntervalMs) + final CircuitBreakerOptions options = new CircuitBreakerOptions() + .setNotificationPeriod(0) + .setMaxFailures(openingThreshold) + .setFailuresRollingWindow(openingIntervalMs) + .setResetTimeout(closingIntervalMs); + + final CircuitBreaker circuitBreaker = CircuitBreaker.create( + "http_cb_" + name, Objects.requireNonNull(vertx), options) .openHandler(ignored -> circuitOpened(name)) .halfOpenHandler(ignored -> circuitHalfOpened(name)) .closeHandler(ignored -> circuitClosed(name)); @@ -104,7 +108,8 @@ private CircuitBreaker createCircuitBreaker(String name, } private void createCircuitBreakerGauge(String name, CircuitBreaker circuitBreaker, Metrics metrics) { - metrics.createHttpClientCircuitBreakerGauge(idFrom(name), circuitBreaker::isOpen); + metrics.createHttpClientCircuitBreakerGauge( + idFrom(name), () -> circuitBreaker.state() != CircuitBreakerState.CLOSED); } private void removeCircuitBreakerGauge(String name, Metrics metrics) { diff --git a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java index d58992dcb34..8cb36af2a28 100644 --- a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java @@ -853,7 +853,9 @@ public void shouldThrowExceptionInCaseOfBaseBidderCookieFamilyNameDuplicates() { } private static Cookie equalToUidsCookie(UidsCookie uidsCookie) throws JsonProcessingException { - final String value = Base64.getUrlEncoder().encodeToString(mapper.writeValueAsBytes(uidsCookie.getCookieUids())); + final String value = Base64.getUrlEncoder() + .encodeToString(mapper.writeValueAsBytes(uidsCookie.getCookieUids())); + return cookieEqualTo("uids", value); } diff --git a/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java b/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java index 26cfca57284..6476487c340 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.file.FileSystem; @@ -12,7 +11,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.stubbing.Answer; import org.prebid.server.VertxTest; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.exception.PreBidException; diff --git a/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java b/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java deleted file mode 100644 index a543f3d9faf..00000000000 --- a/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java +++ /dev/null @@ -1,160 +0,0 @@ -package org.prebid.server.vertx; - -import io.vertx.core.Future; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; -import io.vertx.junit5.VertxExtension; -import io.vertx.junit5.VertxTestContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -import static org.assertj.core.api.Assertions.assertThat; - -@ExtendWith(MockitoExtension.class) -@ExtendWith(VertxExtension.class) -public class CircuitBreakerTest { - - private Vertx vertx; - - private CircuitBreaker circuitBreaker; - - @BeforeEach - public void setUp() { - vertx = Vertx.vertx(); - circuitBreaker = new CircuitBreaker("name", vertx, 1, 1000L, 200L); - } - - @AfterEach - public void tearDown(VertxTestContext context) throws InterruptedException { - vertx.close().onComplete(context.succeedingThenComplete()); - context.awaitCompletion(1000, TimeUnit.MILLISECONDS); - } - - @Test - public void executeShouldSucceedsIfOperationSucceeds() { - // when - final Future future = executeWithSuccess("value"); - - // then - assertThat(future.succeeded()).isTrue(); - assertThat(future.result()).isEqualTo("value"); - } - - @Test - public void executeShouldFailsIfCircuitIsClosedAndOperationFails() { - // when - final Future future = executeWithFail("exception"); - - // then - assertThat(future.failed()).isTrue(); - assertThat(future.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception"); - } - - @Test - public void executeShouldFailsIfCircuitIsHalfOpenedAndOperationFailsAndClosingTimeIsNotPassedBy() { - // when - final Future future1 = executeWithFail("exception1"); - final Future future2 = executeWithFail(null); - - // then - assertThat(future1.failed()).isTrue(); - assertThat(future1.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception1"); - - assertThat(future2.failed()).isTrue(); - assertThat(future2.cause()).isInstanceOf(RuntimeException.class).hasMessage("open circuit"); - } - - @Test - public void executeShouldFailsIfCircuitIsHalfOpenedAndOperationFails() { - // when - final Future future1 = executeWithFail("exception1"); - final Future future2 = executeWithFail(null); - waitForClosingInterval(); - final Future future3 = executeWithFail("exception3"); - - // then - assertThat(future1.failed()).isTrue(); - assertThat(future1.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception1"); - - assertThat(future2.failed()).isTrue(); - assertThat(future2.cause()).isInstanceOf(RuntimeException.class).hasMessage("open circuit"); - - assertThat(future3.failed()).isTrue(); - assertThat(future3.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception3"); - } - - @Test - public void executeShouldSucceedsIfCircuitIsHalfOpenedAndOperationSucceeds() { - // when - final Future future1 = executeWithFail("exception1"); - final Future future2 = executeWithFail("exception2"); - waitForClosingInterval(); - final Future future3 = executeWithSuccess("value after half-open"); - - // then - assertThat(future1.failed()).isTrue(); - assertThat(future1.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception1"); - - assertThat(future2.failed()).isTrue(); - assertThat(future2.cause()).isInstanceOf(RuntimeException.class).hasMessage("open circuit"); - - assertThat(future3.succeeded()).isTrue(); - assertThat(future3.result()).isEqualTo("value after half-open"); - } - - @Test - public void executeShouldFailsWithOriginalExceptionIfOpeningIntervalExceeds() { - // given - circuitBreaker = new CircuitBreaker("name", vertx, 2, 100L, 200L); - - // when - final Future future1 = executeWithFail("exception1"); - waitForOpeningInterval(); - final Future future2 = executeWithFail("exception2"); - - // then - assertThat(future1.failed()).isTrue(); - assertThat(future1.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception1"); - - assertThat(future2.failed()).isTrue(); - assertThat(future2.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception2"); - } - - private Future executeWithSuccess(String result) { - return execute(() -> Future.succeededFuture(result)); - } - - private Future executeWithFail(String errorMessage) { - return execute(() -> Future.failedFuture(new RuntimeException(errorMessage))); - } - - private Future execute(Supplier> handler) { - final Future future = circuitBreaker.execute(handler); - - final Promise promise = Promise.promise(); - future.onComplete(ar -> promise.complete()); - promise.future().toCompletionStage().toCompletableFuture().join(); - - return future; - } - - private void waitForOpeningInterval() { - waitForInterval(150L); - } - - private void waitForClosingInterval() { - waitForInterval(250L); - } - - private void waitForInterval(long timeout) { - final Promise promise = Promise.promise(); - vertx.setTimer(timeout, id -> promise.complete()); - promise.future().toCompletionStage().toCompletableFuture().join(); - } -} diff --git a/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java b/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java index cdbebc2d54b..dc3284b4cf0 100644 --- a/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java +++ b/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java @@ -1,6 +1,5 @@ package org.prebid.server.vertx; -import io.vertx.core.Future; import io.vertx.core.Promise; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -11,8 +10,6 @@ import java.io.IOException; import static org.assertj.core.api.Assertions.assertThatNullPointerException; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.verify; From 68d2154d7b93b11825da6318a8a0f60d92931ea2 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 28 May 2026 15:30:33 +0300 Subject: [PATCH 04/18] Fixed modules. --- .../confiant/adquality/core/BidsScanner.java | 95 ++++++------------- .../confiant/adquality/core/RedisClient.java | 3 +- .../targeting/v1/net/APIClientImpl.java | 2 +- .../optable/targeting/v1/BaseOptableTest.java | 2 +- 4 files changed, 35 insertions(+), 67 deletions(-) diff --git a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/BidsScanner.java b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/BidsScanner.java index 1b3afe3092d..0fbe25832a2 100644 --- a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/BidsScanner.java +++ b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/BidsScanner.java @@ -55,83 +55,50 @@ public void disableScan() { } public Future submitBids(RedisBidsData bids) { - final Promise scanResult = Promise.promise(); - final RedisAPI readRedisNodeAPI = this.readRedisNode.getRedisAPI(); - final boolean shouldSubmit = !isScanDisabled - && readRedisNodeAPI != null && !bids.getBresps().isEmpty(); - - if (shouldSubmit) { - readRedisNodeAPI.get("function_submit_bids", submitHash -> { - final Object submitHashResult = submitHash.result(); - if (submitHashResult != null) { - final List readArgs = List.of( - submitHashResult.toString(), - "0", - toBidsAsJson(bids), - apiKey, - "true"); - - readRedisNodeAPI.evalsha(readArgs, response -> { - if (response.result() != null) { - final BidsScanResult parserResult = redisParser - .parseBidsScanResult(response.result().toString()); - final boolean isAnyRoSkipped = parserResult.getBidScanResults() - .stream().anyMatch(BidScanResult::isRoSkipped); - - if (isAnyRoSkipped) { - reSubmitBidsToWriteNode(readArgs, scanResult); - } else { - scanResult.complete(parserResult); - } - } else { - scanResult.complete(getEmptyScanResult()); - } - }); - } else { - scanResult.complete(getEmptyScanResult()); - } - }); - - return scanResult.future(); + if (isScanDisabled || readRedisNodeAPI == null || bids.getBresps().isEmpty()) { + return Future.succeededFuture(getEmptyScanResult()); } - return Future.succeededFuture(getEmptyScanResult()); + return readRedisNodeAPI.get("function_submit_bids") + .map(Response::toString) + .map(response -> List.of(response, "0", toBidsAsJson(bids), apiKey, "true")) + .compose(args -> scanBids(args, readRedisNodeAPI)) + .otherwise(ignored -> getEmptyScanResult()); + } + + private Future scanBids(List args, RedisAPI redisAPI) { + return redisAPI.evalsha(args) + .map(Response::toString) + .map(redisParser::parseBidsScanResult) + .compose(parsedResult -> parsedResult.getBidScanResults() + .stream().anyMatch(BidScanResult::isRoSkipped) + ? reSubmitBidsToWriteNode(args) + : Future.succeededFuture(parsedResult)); } - private void reSubmitBidsToWriteNode(List readArgs, Promise scanResult) { + private Future reSubmitBidsToWriteNode(List readArgs) { final RedisAPI writeRedisAPI = this.writeRedisNode.getRedisAPI(); - if (writeRedisAPI != null) { - final List writeArgs = readArgs.stream().limit(4).toList(); - writeRedisAPI.evalsha(writeArgs, response -> { - if (response.result() != null) { - final BidsScanResult parserResult = redisParser - .parseBidsScanResult(response.result().toString()); - - scanResult.complete(parserResult); - } else { - scanResult.complete(getEmptyScanResult()); - } - }); - } else { - scanResult.complete(getEmptyScanResult()); + if (writeRedisAPI == null) { + return Future.succeededFuture(getEmptyScanResult()); } + + final List writeArgs = readArgs.stream().limit(4).toList(); + return writeRedisAPI.evalsha(writeArgs) + .map(Response::toString) + .map(redisParser::parseBidsScanResult); } public Future isScanDisabledFlag() { final RedisAPI redisAPI = this.readRedisNode.getRedisAPI(); - final Promise isDisabled = Promise.promise(); - - if (redisAPI != null) { - redisAPI.get("scan-disabled", scanDisabledValue -> { - final Response scanDisabled = scanDisabledValue.result(); - isDisabled.complete(scanDisabled != null && "true".equals(scanDisabled.toString())); - }); - - return isDisabled.future(); + if (redisAPI == null) { + return Future.succeededFuture(true); } - return Future.succeededFuture(true); + return redisAPI.get("scan-disabled") + .map(Response::toString) + .map(Boolean::parseBoolean) + .otherwise(false); } private String toBidsAsJson(RedisBidsData bids) { diff --git a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisClient.java b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisClient.java index d1b424f314e..60b910c632a 100644 --- a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisClient.java +++ b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisClient.java @@ -61,7 +61,8 @@ public RedisAPI getRedisAPI() { */ private void createRedisClient(Handler> handler, boolean isReconnect) { Redis.createClient(vertx, options) - .connect(onConnect -> { + .connect() + .onComplete(onConnect -> { if (onConnect.succeeded()) { connection = onConnect.result(); connection.exceptionHandler(e -> { diff --git a/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java b/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java index d31929f111f..e70166c82c2 100644 --- a/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java +++ b/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java @@ -69,7 +69,7 @@ private String resolveEndpoint(String tenant, String origin) { } private static MultiMap headers(OptableTargetingProperties properties, List ips, String userAgent) { - final MultiMap headers = HeadersMultiMap.headers() + final MultiMap headers = HeadersMultiMap.httpHeaders() .add(HttpUtil.ACCEPT_HEADER, "application/json"); if (userAgent != null) { diff --git a/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java b/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java index 99f24ea4bc5..93738fe871f 100644 --- a/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java +++ b/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java @@ -193,7 +193,7 @@ protected Device givenDevice() { } protected HttpClientResponse givenSuccessHttpResponse(String fileName) { - final MultiMap headers = HeadersMultiMap.headers().add("Content-Type", "application/json"); + final MultiMap headers = HeadersMultiMap.httpHeaders().add("Content-Type", "application/json"); return HttpClientResponse.of(HttpStatus.SC_OK, headers, givenBodyFromFile(fileName)); } From 2b2eeb18b356d46d36fb0dcc6815d1bcfc2de7ac Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 1 Jun 2026 19:49:19 +0300 Subject: [PATCH 05/18] Bumped vert.x to 5.1.0. Fixed functional test that has been failing due to case-sensitive headers check. --- .../targeting/v1/net/APIClientImpl.java | 4 +-- .../optable/targeting/v1/BaseOptableTest.java | 4 +-- extra/pom.xml | 2 +- .../pubstack/PubstackAnalyticsReporter.java | 29 +++++++++++-------- .../server/functional/tests/SetUidSpec.groovy | 2 +- .../PubstackAnalyticsReporterTest.java | 10 +++++-- .../bidder/criteo/CriteoBidderTest.java | 3 +- .../bidder/gothamads/GothamAdsBidderTest.java | 3 +- .../server/bidder/pwbid/PwbidBidderTest.java | 3 +- 9 files changed, 34 insertions(+), 26 deletions(-) diff --git a/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java b/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java index e70166c82c2..e6e5510c40f 100644 --- a/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java +++ b/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java @@ -3,7 +3,7 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.Future; import io.vertx.core.MultiMap; -import io.vertx.core.http.impl.headers.HeadersMultiMap; +import io.vertx.core.http.HttpHeaders; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.execution.timeout.Timeout; @@ -69,7 +69,7 @@ private String resolveEndpoint(String tenant, String origin) { } private static MultiMap headers(OptableTargetingProperties properties, List ips, String userAgent) { - final MultiMap headers = HeadersMultiMap.httpHeaders() + final MultiMap headers = HttpHeaders.headers() .add(HttpUtil.ACCEPT_HEADER, "application/json"); if (userAgent != null) { diff --git a/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java b/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java index 93738fe871f..2ba8789c5c9 100644 --- a/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java +++ b/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java @@ -16,7 +16,7 @@ import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; import io.vertx.core.MultiMap; -import io.vertx.core.http.impl.headers.HeadersMultiMap; +import io.vertx.core.http.HttpHeaders; import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; import org.prebid.server.activity.infrastructure.ActivityInfrastructure; @@ -193,7 +193,7 @@ protected Device givenDevice() { } protected HttpClientResponse givenSuccessHttpResponse(String fileName) { - final MultiMap headers = HeadersMultiMap.httpHeaders().add("Content-Type", "application/json"); + final MultiMap headers = HttpHeaders.headers().add("Content-Type", "application/json"); return HttpClientResponse.of(HttpStatus.SC_OK, headers, givenBodyFromFile(fileName)); } diff --git a/extra/pom.xml b/extra/pom.xml index 26214b9d33f..57e33aac8b3 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -34,7 +34,7 @@ 3.5.10 - 5.0.10 + 5.1.0 2.0.1.Final 4.4 1.27.1 diff --git a/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java b/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java index 0bddcbe05ca..2b5c6ce6106 100644 --- a/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java +++ b/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java @@ -1,6 +1,5 @@ package org.prebid.server.analytics.reporter.pubstack; -import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Promise; import io.vertx.core.Vertx; @@ -119,19 +118,22 @@ public String name() { @Override public void initialize(Promise initializePromise) { vertx.setPeriodic(configurationRefreshDelay, id -> fetchRemoteConfig()); - fetchRemoteConfig(); - initializePromise.tryComplete(); + fetchRemoteConfig() + .onSuccess(initializePromise::succeed) + .onFailure(initializePromise::fail); } void shutdown() { eventHandlers.values().forEach(PubstackEventHandler::reportEvents); } - private void fetchRemoteConfig() { + private Future fetchRemoteConfig() { logger.info("[pubstack] Updating config: {}", pubstackConfig); - httpClient.get(makeEventEndpointUrl(pubstackConfig.getEndpoint(), pubstackConfig.getScopeId()), timeout) + return httpClient.get(makeEventEndpointUrl(pubstackConfig.getEndpoint(), pubstackConfig.getScopeId()), timeout) .map(this::processRemoteConfigurationResponse) - .onComplete(this::updateConfigsOnChange); + .map(this::updateConfigsOnChange) + .onFailure(PubstackAnalyticsReporter::logError) + .mapEmpty(); } private PubstackConfig processRemoteConfigurationResponse(HttpClientResponse response) { @@ -148,15 +150,14 @@ private PubstackConfig processRemoteConfigurationResponse(HttpClientResponse res } } - private void updateConfigsOnChange(AsyncResult asyncConfigResult) { - if (asyncConfigResult.failed()) { - logger.error("[pubstask] Fail to fetch remote configuration: {}", asyncConfigResult.cause().getMessage()); - } else if (!Objects.equals(pubstackConfig, asyncConfigResult.result())) { - final PubstackConfig pubstackConfig = asyncConfigResult.result(); + private Void updateConfigsOnChange(PubstackConfig config) { + if (!Objects.equals(pubstackConfig, config)) { eventHandlers.values().forEach(PubstackEventHandler::reportEvents); - this.pubstackConfig = pubstackConfig; + this.pubstackConfig = config; updateHandlers(pubstackConfig); } + + return null; } private void updateHandlers(PubstackConfig pubstackConfig) { @@ -187,4 +188,8 @@ private String makeEventHandlerEndpoint(String endpoint, EventType eventType) { throw new PreBidException(message); } } + + private static void logError(Throwable throwable) { + logger.error("[pubstask] Fail to fetch remote configuration: {}", throwable.getCause().getMessage()); + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy index 7e9eff9ebd3..a64bbc21086 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy @@ -514,7 +514,7 @@ class SetUidSpec extends BaseSpec { } List getSetUidsHeaders(SetuidResponse response, boolean includeEmpty = false) { - response.headers.get("Set-Cookie").findAll { cookie -> + response.headers.get("set-cookie").findAll { cookie -> includeEmpty || !(cookie =~ /\buids\d*=\s*;/) } } diff --git a/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java b/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java index ef1e488e81e..e971498c598 100644 --- a/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java +++ b/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; @@ -106,14 +107,19 @@ public void initializeShouldFetchConfigAndSetPeriodicTimerForConfigUpdate() thro @Test public void initializeShouldFailUpdateSendBuffersAndSetTimerWhenEndpointFromRemoteConfigIsNotValid() throws JsonProcessingException { + // given + final Promise promise = Promise.promise(); final PubstackConfig pubstackConfig = PubstackConfig.of("newScopeId", "invalid", Collections.singletonMap(EventType.auction, true)); given(httpClient.get(anyString(), anyLong())).willReturn( Future.succeededFuture(HttpClientResponse.of(200, null, mapper.writeValueAsString(pubstackConfig)))); - // when and then - assertThatThrownBy(() -> pubstackAnalyticsReporter.initialize(Promise.promise())) + // when + pubstackAnalyticsReporter.initialize(promise); + + // then + assertThatThrownBy(() -> promise.future().await(5, TimeUnit.SECONDS)) .hasMessage("[pubstack] Failed to create event report url for endpoint: invalid") .isInstanceOf(PreBidException.class); verify(auctionHandler).reportEvents(); diff --git a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java index ecfd0622a7a..dc2df0ee0bd 100644 --- a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java @@ -9,7 +9,6 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpMethod; -import io.vertx.core.http.impl.headers.HeadersMultiMap; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.prebid.server.VertxTest; @@ -74,7 +73,7 @@ public void makeHttpRequestsShouldEncodePassedBidRequest() { .build()); assertThat(result.getValue()).usingRecursiveComparison() - .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, HeadersMultiMap.class) + .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, MultiMap.class) .isEqualTo(expectedResult.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java b/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java index eeb982d351b..324a43a69bd 100644 --- a/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java @@ -10,7 +10,6 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpMethod; -import io.vertx.core.http.impl.headers.HeadersMultiMap; import org.junit.jupiter.api.Test; import org.prebid.server.VertxTest; import org.prebid.server.bidder.gotthamads.GothamAdsBidder; @@ -106,7 +105,7 @@ public void makeHttpRequestsShouldMakeCorrectRequest() { assertThat(result.getValue()) .usingRecursiveComparison() - .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, HeadersMultiMap.class) + .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, MultiMap.class) .isEqualTo(expectedResult.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java b/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java index ee6f8dcfe0b..ec6b07fd06e 100644 --- a/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java @@ -12,7 +12,6 @@ import com.iab.openrtb.response.SeatBid; import io.vertx.core.MultiMap; import io.vertx.core.http.HttpMethod; -import io.vertx.core.http.impl.headers.HeadersMultiMap; import org.junit.jupiter.api.Test; import org.prebid.server.VertxTest; import org.prebid.server.bidder.model.BidderBid; @@ -75,7 +74,7 @@ public void makeHttpRequestsShouldReturnExpectedHttpRequest() { assertThat(result.getValue()) .usingRecursiveComparison() - .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, HeadersMultiMap.class) + .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, MultiMap.class) .isEqualTo(expectedResults.getValue()); assertThat(result.getErrors()).isEmpty(); } From e3f97ea45c002e32e6d5f5b75f8ce05a80774175 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 20 Apr 2026 19:06:46 +0300 Subject: [PATCH 06/18] WIP --- extra/pom.xml | 13 +- pom.xml | 104 +++++++------ .../admin/LoggerControlKnobHandler.java | 87 ----------- .../prebid/server/log/LoggerControlKnob.java | 68 --------- .../spring/config/ServiceConfiguration.java | 6 - .../admin/AdminEndpointsConfiguration.java | 18 --- .../model/response/auction/ErrorType.groovy | 2 +- .../bidder/gumgum/GumgumBidderTest.java | 8 +- .../execution/file/syncer/FileSyncerTest.java | 2 +- .../handler/LoggerControlKnobHandlerTest.java | 143 ------------------ .../org/prebid/server/it/StartioTest.java | 3 - 11 files changed, 74 insertions(+), 380 deletions(-) delete mode 100644 src/main/java/org/prebid/server/handler/admin/LoggerControlKnobHandler.java delete mode 100644 src/main/java/org/prebid/server/log/LoggerControlKnob.java delete mode 100644 src/test/java/org/prebid/server/handler/LoggerControlKnobHandlerTest.java diff --git a/extra/pom.xml b/extra/pom.xml index 57e33aac8b3..13af1f7d8dd 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -33,7 +33,7 @@ 10.17.0 - 3.5.10 + 4.0.6 5.1.0 2.0.1.Final 4.4 @@ -61,9 +61,11 @@ 3.12.1 - 2.4-M6-groovy-4.0 + 2.4-groovy-5.0 5.15.0 + 2.0.4 + 6.0.0 false @@ -107,6 +109,13 @@ pom import + + io.rest-assured + rest-assured-bom + ${rest-assured-bom.version} + pom + import + org.wiremock wiremock-jetty12 diff --git a/pom.xml b/pom.xml index 8ef80350c78..15c17ccc73e 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,16 @@ org.springframework.boot spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 jakarta.annotation @@ -150,11 +160,6 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - test - com.networknt json-schema-validator @@ -163,16 +168,6 @@ com.github.java-json-tools json-patch - - com.mysql - mysql-connector-j - test - - - org.postgresql - postgresql - test - software.amazon.awssdk s3 @@ -264,6 +259,11 @@ vertx-junit5 test + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + test + org.wiremock wiremock-jetty12 @@ -289,11 +289,11 @@ xml-path test - - org.spockframework - spock-core - test - + + + + + org.apache.groovy groovy @@ -327,27 +327,37 @@ org.testcontainers - mockserver + testcontainers-mockserver test org.testcontainers - mysql + testcontainers-mysql test org.testcontainers - localstack + testcontainers-localstack test org.testcontainers - postgresql + testcontainers-postgresql test org.testcontainers - influxdb + testcontainers-influxdb + test + + + com.mysql + mysql-connector-j + test + + + org.postgresql + postgresql test @@ -596,28 +606,28 @@ - - org.codehaus.gmavenplus - gmavenplus-plugin - - true - - - ${project.basedir}/src/test/groovy - - **/*.groovy - - - - - - - - compileTests - - - - + + + + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins maven-failsafe-plugin diff --git a/src/main/java/org/prebid/server/handler/admin/LoggerControlKnobHandler.java b/src/main/java/org/prebid/server/handler/admin/LoggerControlKnobHandler.java deleted file mode 100644 index 7875c7a52dd..00000000000 --- a/src/main/java/org/prebid/server/handler/admin/LoggerControlKnobHandler.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.prebid.server.handler.admin; - -import io.netty.handler.codec.http.HttpResponseStatus; -import io.vertx.core.Handler; -import io.vertx.core.MultiMap; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.ext.web.RoutingContext; -import org.prebid.server.exception.InvalidRequestException; -import org.prebid.server.log.LoggerControlKnob; -import org.prebid.server.util.HttpUtil; - -import java.time.Duration; -import java.util.Objects; -import java.util.Set; - -public class LoggerControlKnobHandler implements Handler { - - private static final String LEVEL_PARAMETER = "level"; - private static final String DURATION_PARAMETER = "duration"; - - private static final Set ALLOWED_LEVELS = Set.of("error", "warn", "info", "debug"); - - private final long maxDurationMs; - private final LoggerControlKnob loggerControlKnob; - private final String endpoint; - - public LoggerControlKnobHandler(long maxDurationMs, LoggerControlKnob loggerControlKnob, String endpoint) { - this.maxDurationMs = maxDurationMs; - this.loggerControlKnob = Objects.requireNonNull(loggerControlKnob); - this.endpoint = Objects.requireNonNull(endpoint); - } - - @Override - public void handle(RoutingContext routingContext) { - try { - final MultiMap parameters = routingContext.request().params(); - loggerControlKnob.changeLogLevel(readLevel(parameters), readDuration(parameters)); - - HttpUtil.executeSafely(routingContext, endpoint, - HttpServerResponse::end); - } catch (InvalidRequestException e) { - HttpUtil.executeSafely(routingContext, endpoint, - response -> response - .setStatusCode(HttpResponseStatus.BAD_REQUEST.code()) - .end(e.getMessage())); - } - } - - private String readLevel(MultiMap parameters) { - final String level = parameters.get(LEVEL_PARAMETER); - - if (level == null) { - throw new InvalidRequestException("Missing required parameter '%s'".formatted(LEVEL_PARAMETER)); - } - - if (!ALLOWED_LEVELS.contains(level.toLowerCase())) { - throw new InvalidRequestException("Invalid '%s' parameter value, allowed values '%s'" - .formatted(LEVEL_PARAMETER, ALLOWED_LEVELS)); - } - - return level; - } - - private Duration readDuration(MultiMap parameters) { - final Integer duration = getIntParameter(DURATION_PARAMETER, parameters); - - if (duration == null) { - throw new InvalidRequestException("Missing required parameter '%s'".formatted(DURATION_PARAMETER)); - } - - if (duration < 1 || duration > maxDurationMs) { - throw new InvalidRequestException( - "Parameter '%s' must be between %d and %d".formatted(DURATION_PARAMETER, 0, maxDurationMs)); - } - - return Duration.ofMillis(duration); - } - - private Integer getIntParameter(String parameterName, MultiMap parameters) { - final String value = parameters.get(parameterName); - try { - return value != null ? Integer.parseInt(value) : null; - } catch (NumberFormatException e) { - throw new InvalidRequestException("Invalid '%s' parameter value".formatted(parameterName)); - } - } -} diff --git a/src/main/java/org/prebid/server/log/LoggerControlKnob.java b/src/main/java/org/prebid/server/log/LoggerControlKnob.java deleted file mode 100644 index 3c74cd36b15..00000000000 --- a/src/main/java/org/prebid/server/log/LoggerControlKnob.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.prebid.server.log; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import io.vertx.core.Vertx; -import org.slf4j.LoggerFactory; - -import java.time.Duration; -import java.util.Objects; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -public class LoggerControlKnob { - - private static final String PREBID_LOGGER = "org.prebid.server"; - - private final Vertx vertx; - private final Logger logger; - private final Level originalLevel; - - private final Lock lock = new ReentrantLock(); - private Long restoreTimerId; - - public LoggerControlKnob(Vertx vertx) { - this.vertx = Objects.requireNonNull(vertx); - - logger = getPrebidLogger(); - originalLevel = logger != null ? logger.getLevel() : null; - } - - public void changeLogLevel(String level, Duration duration) { - if (logger == null) { - return; - } - - lock.lock(); - try { - if (restoreTimerId != null) { - vertx.cancelTimer(restoreTimerId); - restoreTimerId = null; - } - - logger.setLevel(Level.toLevel(level, originalLevel)); - restoreTimerId = vertx.setTimer(duration.toMillis(), this::resetLoggerLevel); - } finally { - lock.unlock(); - } - } - - private static Logger getPrebidLogger() { - final org.slf4j.Logger prebidSlf4jLogger = LoggerFactory.getLogger(PREBID_LOGGER); - return prebidSlf4jLogger instanceof Logger ? (Logger) prebidSlf4jLogger : null; - } - - private void resetLoggerLevel(long triggeredTimerId) { - lock.lock(); - try { - if (restoreTimerId == null || triggeredTimerId != restoreTimerId) { - return; - } - - logger.setLevel(originalLevel); - restoreTimerId = null; - } finally { - lock.unlock(); - } - } -} diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java index 597feb1f1ac..2159ed9f6c8 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -105,7 +105,6 @@ import org.prebid.server.log.CriteriaLogManager; import org.prebid.server.log.CriteriaManager; import org.prebid.server.log.HttpInteractionLogger; -import org.prebid.server.log.LoggerControlKnob; import org.prebid.server.metric.Metrics; import org.prebid.server.optout.GoogleRecaptchaVerifier; import org.prebid.server.privacy.HostVendorTcfDefinerService; @@ -1275,11 +1274,6 @@ HttpInteractionLogger httpInteractionLogger(JacksonMapper mapper) { return new HttpInteractionLogger(mapper); } - @Bean - LoggerControlKnob loggerControlKnob(Vertx vertx) { - return new LoggerControlKnob(vertx); - } - @Bean DsaEnforcer dsaEnforcer(JacksonMapper mapper) { return new DsaEnforcer(mapper); diff --git a/src/main/java/org/prebid/server/spring/config/server/admin/AdminEndpointsConfiguration.java b/src/main/java/org/prebid/server/spring/config/server/admin/AdminEndpointsConfiguration.java index a5938aab396..70c13eb83bb 100644 --- a/src/main/java/org/prebid/server/spring/config/server/admin/AdminEndpointsConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/server/admin/AdminEndpointsConfiguration.java @@ -10,14 +10,12 @@ import org.prebid.server.handler.admin.CollectedMetricsHandler; import org.prebid.server.handler.admin.CurrencyRatesHandler; import org.prebid.server.handler.admin.HttpInteractionLogHandler; -import org.prebid.server.handler.admin.LoggerControlKnobHandler; import org.prebid.server.handler.admin.SettingsCacheNotificationHandler; import org.prebid.server.handler.admin.TracerLogHandler; import org.prebid.server.handler.admin.VersionHandler; import org.prebid.server.json.JacksonMapper; import org.prebid.server.log.CriteriaManager; import org.prebid.server.log.HttpInteractionLogger; -import org.prebid.server.log.LoggerControlKnob; import org.prebid.server.settings.CachingApplicationSettings; import org.prebid.server.settings.SettingsCache; import org.prebid.server.util.VersionInfo; @@ -138,22 +136,6 @@ AdminResource loggingHttpInteractionEndpoint( new HttpInteractionLogHandler(maxLimit, httpInteractionLogger, path)); } - @Bean - @ConditionalOnExpression("${admin-endpoints.logging-changelevel.enabled} == true") - AdminResource loggingChangeLevelEndpoint( - @Value("${logging.change-level.max-duration-ms}") long maxDuration, - LoggerControlKnob loggerControlKnob, - @Value("${admin-endpoints.logging-changelevel.path}") String path, - @Value("${admin-endpoints.logging-changelevel.on-application-port}") boolean isOnApplicationPort, - @Value("${admin-endpoints.logging-changelevel.protected}") boolean isProtected) { - - return new AdminResourceWrapper( - path, - isOnApplicationPort, - isProtected, - new LoggerControlKnobHandler(maxDuration, loggerControlKnob, path)); - } - @Bean @ConditionalOnProperty(prefix = "admin-endpoints.tracelog", name = "enabled", havingValue = "true") AdminResource tracerLogEndpoint( diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy index 80f504a05ec..ed4055a8a62 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy @@ -19,7 +19,7 @@ enum ErrorType { AMX_UPPER_CASE("AMX"), @JsonValue - final String value + private final String value ErrorType(String value) { this.value = value diff --git a/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java b/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java index 2b00840ed26..359ea97025c 100644 --- a/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java @@ -39,10 +39,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.tuple; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; import static org.prebid.server.proto.openrtb.ext.response.BidType.video; diff --git a/src/test/java/org/prebid/server/execution/file/syncer/FileSyncerTest.java b/src/test/java/org/prebid/server/execution/file/syncer/FileSyncerTest.java index 39a8e8ae966..454a2f6f834 100644 --- a/src/test/java/org/prebid/server/execution/file/syncer/FileSyncerTest.java +++ b/src/test/java/org/prebid/server/execution/file/syncer/FileSyncerTest.java @@ -4,6 +4,7 @@ import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.Vertx; +import org.apache.commons.lang3.NotImplementedException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -16,7 +17,6 @@ import org.prebid.server.execution.retry.FixedIntervalRetryPolicy; import org.prebid.server.execution.retry.NonRetryable; import org.prebid.server.execution.retry.RetryPolicy; -import org.testcontainers.shaded.org.apache.commons.lang3.NotImplementedException; import java.util.concurrent.Callable; diff --git a/src/test/java/org/prebid/server/handler/LoggerControlKnobHandlerTest.java b/src/test/java/org/prebid/server/handler/LoggerControlKnobHandlerTest.java deleted file mode 100644 index d3fcaae8b57..00000000000 --- a/src/test/java/org/prebid/server/handler/LoggerControlKnobHandlerTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.prebid.server.handler; - -import io.vertx.core.MultiMap; -import io.vertx.core.http.HttpServerRequest; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.ext.web.RoutingContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.prebid.server.handler.admin.LoggerControlKnobHandler; -import org.prebid.server.log.LoggerControlKnob; - -import java.time.Duration; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.startsWith; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mock.Strictness.LENIENT; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; - -@ExtendWith(MockitoExtension.class) -public class LoggerControlKnobHandlerTest { - - @Mock - private LoggerControlKnob loggerControlKnob; - - private LoggerControlKnobHandler handler; - - @Mock - private RoutingContext routingContext; - @Mock - private HttpServerRequest httpRequest; - @Mock(strictness = LENIENT) - private HttpServerResponse httpResponse; - - @BeforeEach - public void setUp() { - given(routingContext.request()).willReturn(httpRequest); - given(routingContext.response()).willReturn(httpResponse); - given(httpResponse.setStatusCode(anyInt())).willReturn(httpResponse); - - handler = new LoggerControlKnobHandler(10000L, loggerControlKnob, "endpoint"); - } - - @Test - public void shouldChangeLevelWhenAllParametersPresent() { - // given - given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() - .add("level", "info") - .add("duration", "1000")); - - // when - handler.handle(routingContext); - - // then - verify(loggerControlKnob).changeLogLevel("info", Duration.ofMillis(1000L)); - } - - @Test - public void shouldRespondWithErrorWhenLevelAbsent() { - // given - given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap()); - - // when - handler.handle(routingContext); - - // then - verify(httpResponse).setStatusCode(eq(400)); - verify(httpResponse).end(eq("Missing required parameter 'level'")); - - verifyNoInteractions(loggerControlKnob); - } - - @Test - public void shouldRespondWithErrorWhenLevelNotValid() { - // given - given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() - .add("level", "abc")); - - // when - handler.handle(routingContext); - - // then - verify(httpResponse).setStatusCode(eq(400)); - verify(httpResponse).end(startsWith("Invalid 'level' parameter value")); - - verifyNoInteractions(loggerControlKnob); - } - - @Test - public void shouldRespondWithErrorWhenDurationAbsent() { - // given - given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() - .add("level", "info")); - - // when - handler.handle(routingContext); - - // then - verify(httpResponse).setStatusCode(eq(400)); - verify(httpResponse).end(eq("Missing required parameter 'duration'")); - - verifyNoInteractions(loggerControlKnob); - } - - @Test - public void shouldRespondWithErrorWhenDurationNotInteger() { - // given - given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() - .add("level", "info") - .add("duration", "abc")); - - // when - handler.handle(routingContext); - - // then - verify(httpResponse).setStatusCode(eq(400)); - verify(httpResponse).end(eq("Invalid 'duration' parameter value")); - - verifyNoInteractions(loggerControlKnob); - } - - @Test - public void shouldRespondWithErrorWhenDurationNotValid() { - // given - given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() - .add("level", "info") - .add("duration", "20000")); - - // when - handler.handle(routingContext); - - // then - verify(httpResponse).setStatusCode(eq(400)); - verify(httpResponse).end(eq("Parameter 'duration' must be between 0 and 10000")); - - verifyNoInteractions(loggerControlKnob); - } -} diff --git a/src/test/java/org/prebid/server/it/StartioTest.java b/src/test/java/org/prebid/server/it/StartioTest.java index 217b9ebfc2b..8a1fcbea12b 100644 --- a/src/test/java/org/prebid/server/it/StartioTest.java +++ b/src/test/java/org/prebid/server/it/StartioTest.java @@ -3,9 +3,7 @@ import io.restassured.response.Response; import org.json.JSONException; import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; import org.prebid.server.model.Endpoint; -import org.springframework.test.context.junit4.SpringRunner; import java.io.IOException; @@ -15,7 +13,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static java.util.Collections.singletonList; -@RunWith(SpringRunner.class) public class StartioTest extends IntegrationTest { @Test From 173dd137f9b5ed6f9a83e4733ed541e27cfb79b3 Mon Sep 17 00:00:00 2001 From: Oleksandr Zhevedenko <720803+Net-burst@users.noreply.github.com> Date: Thu, 21 May 2026 10:23:37 -0400 Subject: [PATCH 07/18] Fix logger conflict --- extra/pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/extra/pom.xml b/extra/pom.xml index 13af1f7d8dd..8dd8cb3ec9b 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -81,6 +81,20 @@ + + org.apache.logging.log4j + log4j-bom + 2.25.4 + import + pom + + + org.slf4j + slf4j-bom + 2.0.17 + pom + import + io.vertx vertx-dependencies From 17530424d55cfe8a33a56999f8022e962140a998 Mon Sep 17 00:00:00 2001 From: Oleksandr Zhevedenko <720803+Net-burst@users.noreply.github.com> Date: Mon, 1 Jun 2026 13:36:30 -0400 Subject: [PATCH 08/18] WIP --- extra/pom.xml | 9 +++++---- pom.xml | 3 +++ .../model/request/auction/AuctionEnvironment.groovy | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/extra/pom.xml b/extra/pom.xml index 8dd8cb3ec9b..ea64f8705b5 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -23,9 +23,10 @@ ${java.version} - 3.1.1 - 3.14.1 - 3.5.3 + 3.3.1 + 3.3.1 + 3.15.0 + 3.5.6 ${maven-surefire-plugin.version} 0.8.13 0.46.0 @@ -60,7 +61,7 @@ 4.2.30 - 3.12.1 + 3.13.2 2.4-groovy-5.0 5.15.0 diff --git a/pom.xml b/pom.xml index 15c17ccc73e..a019db51079 100644 --- a/pom.xml +++ b/pom.xml @@ -392,6 +392,9 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + + spock + false diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy index 649c539e794..0148bd3dd72 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy @@ -10,7 +10,7 @@ enum AuctionEnvironment { UNKNOWN(Integer.MAX_VALUE), @JsonValue - private int value + int value AuctionEnvironment(Integer value) { this.value = value From 43d5cd7fbca70c58903be3dc1ed481df67ce9a98 Mon Sep 17 00:00:00 2001 From: Oleksandr Zhevedenko <--global> Date: Mon, 1 Jun 2026 13:59:07 -0400 Subject: [PATCH 09/18] WIP --- extra/pom.xml | 2 +- .../java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/pom.xml b/extra/pom.xml index ea64f8705b5..00986aab0c7 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -65,7 +65,6 @@ 2.4-groovy-5.0 5.15.0 - 2.0.4 6.0.0 @@ -135,6 +134,7 @@ org.wiremock wiremock-jetty12 ${wiremock.version} + test javax.validation diff --git a/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java b/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java index 359ea97025c..b0982cdd3b3 100644 --- a/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/gumgum/GumgumBidderTest.java @@ -123,7 +123,7 @@ public void testMakeHttpRequestsShouldNotSetTagIdFromZoneWhenAdUnitIdIsMissing() final Imp modifiedImp = modifiedRequest.getImp().get(0); assertNull(modifiedImp.getTagid()); - assertEquals("test-site", modifiedRequest.getSite().getId(), "zone123"); + assertEquals("zone123", modifiedRequest.getSite().getId()); } @Test From 525d6487f76eb7a055e93029a8ab52b52a903917 Mon Sep 17 00:00:00 2001 From: Oleksandr Zhevedenko <--global> Date: Mon, 1 Jun 2026 22:56:27 -0400 Subject: [PATCH 10/18] WIP --- extra/pom.xml | 9 +++++++-- pom.xml | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/extra/pom.xml b/extra/pom.xml index 00986aab0c7..2b35bce3a8a 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -40,7 +40,7 @@ 4.4 1.27.1 3.6.1 - 1.10.0 + 1.10.1 2.1 4.5.14 5.5.1 @@ -62,6 +62,7 @@ 3.13.2 + 4.2.1 2.4-groovy-5.0 5.15.0 @@ -130,11 +131,15 @@ pom import + + org.wiremock.integrations + wiremock-spring-boot + ${wiremock-spring-boot.version} + org.wiremock wiremock-jetty12 ${wiremock.version} - test javax.validation diff --git a/pom.xml b/pom.xml index a019db51079..64510527d11 100644 --- a/pom.xml +++ b/pom.xml @@ -264,6 +264,11 @@ jackson-dataformat-xml test + + org.wiremock.integrations + wiremock-spring-boot + test + org.wiremock wiremock-jetty12 From e93e6eb3186bf11a63655b26000418517ba5f741 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 2 Jun 2026 16:47:22 +0300 Subject: [PATCH 11/18] Minor refactoring. --- .../reporter/pubstack/PubstackAnalyticsReporter.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java b/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java index 2b5c6ce6106..1fd2ab7749b 100644 --- a/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java +++ b/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java @@ -118,9 +118,7 @@ public String name() { @Override public void initialize(Promise initializePromise) { vertx.setPeriodic(configurationRefreshDelay, id -> fetchRemoteConfig()); - fetchRemoteConfig() - .onSuccess(initializePromise::succeed) - .onFailure(initializePromise::fail); + fetchRemoteConfig().onComplete(initializePromise); } void shutdown() { From 0615b5bd0beac514c5ef16811d3d6f128b2169a3 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 3 Jun 2026 17:00:26 +0300 Subject: [PATCH 12/18] Refactored ContextRunner. --- src/main/java/org/prebid/server/vertx/ContextRunner.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/org/prebid/server/vertx/ContextRunner.java b/src/main/java/org/prebid/server/vertx/ContextRunner.java index 1e59fc394e9..2c4f220549e 100644 --- a/src/main/java/org/prebid/server/vertx/ContextRunner.java +++ b/src/main/java/org/prebid/server/vertx/ContextRunner.java @@ -1,7 +1,6 @@ package org.prebid.server.vertx; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.Vertx; @@ -20,10 +19,6 @@ public ContextRunner(Vertx vertx, long timeoutMs) { } public void runBlocking(Supplier> action) { - runBlocking(promise -> action.get().onComplete(promise)); - } - - public void runBlocking(Handler> action) { final CountDownLatch completionLatch = new CountDownLatch(1); final Promise promise = Promise.promise(); final Future future = promise.future(); @@ -31,7 +26,7 @@ public void runBlocking(Handler> action) { future.onComplete(ignored -> completionLatch.countDown()); vertx.runOnContext(v -> { try { - action.handle(promise); + action.get().onComplete(promise); } catch (RuntimeException e) { promise.fail(e); } From 5fd97e0e028ad113300788574363864d4ffff796 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 3 Jun 2026 17:28:03 +0300 Subject: [PATCH 13/18] Unit tests fixes. --- .../org/prebid/server/bidder/criteo/CriteoBidderTest.java | 2 +- .../prebid/server/bidder/gothamads/GothamAdsBidderTest.java | 2 +- .../java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java | 2 +- .../CircuitBreakerSecuredGeoLocationServiceTest.java | 4 +--- .../prebid/server/hooks/execution/HookStageExecutorTest.java | 4 +--- .../java/org/prebid/server/log/ConditionalLoggerTest.java | 3 +-- .../org/prebid/server/settings/S3ApplicationSettingsTest.java | 4 +--- .../server/settings/service/S3PeriodicRefreshServiceTest.java | 4 +--- .../database/CircuitBreakerSecuredDatabaseClientTest.java | 4 +--- .../vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java | 4 +--- 10 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java index dc2df0ee0bd..b1964100edd 100644 --- a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java @@ -73,7 +73,7 @@ public void makeHttpRequestsShouldEncodePassedBidRequest() { .build()); assertThat(result.getValue()).usingRecursiveComparison() - .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, MultiMap.class) + .withEqualsForType((a, b) -> a.entries().equals(b.entries()), MultiMap.class) .isEqualTo(expectedResult.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java b/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java index 324a43a69bd..f0f02e9c9ad 100644 --- a/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java @@ -105,7 +105,7 @@ public void makeHttpRequestsShouldMakeCorrectRequest() { assertThat(result.getValue()) .usingRecursiveComparison() - .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, MultiMap.class) + .withEqualsForType((a, b) -> a.entries().equals(b.entries()), MultiMap.class) .isEqualTo(expectedResult.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java b/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java index ec6b07fd06e..22f479a4836 100644 --- a/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java @@ -74,7 +74,7 @@ public void makeHttpRequestsShouldReturnExpectedHttpRequest() { assertThat(result.getValue()) .usingRecursiveComparison() - .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, MultiMap.class) + .withEqualsForType((a, b) -> a.entries().equals(b.entries()), MultiMap.class) .isEqualTo(expectedResults.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java b/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java index 6afe645cc4d..49061688f3b 100644 --- a/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java +++ b/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java @@ -16,7 +16,6 @@ import org.prebid.server.geolocation.model.GeoInfo; import org.prebid.server.metric.Metrics; -import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import static org.assertj.core.api.Assertions.assertThat; @@ -46,9 +45,8 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) throws InterruptedException { + public void tearDown(VertxTestContext context) { vertx.close().onComplete(context.succeedingThenComplete()); - context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java index 6e760120fbf..fc67149a747 100644 --- a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java +++ b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java @@ -98,7 +98,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; @@ -150,9 +149,8 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) throws InterruptedException { + public void tearDown(VertxTestContext context) { vertx.close().onComplete(context.succeedingThenComplete()); - context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java b/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java index 8fd380d9df1..3839310ce85 100644 --- a/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java +++ b/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java @@ -36,9 +36,8 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) throws InterruptedException { + public void tearDown(VertxTestContext context) { vertx.close().onComplete(context.succeedingThenComplete()); - context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java index 1ffc9052637..830017fb272 100644 --- a/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java @@ -27,7 +27,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static java.util.Collections.emptySet; @@ -74,9 +73,8 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) throws InterruptedException { + public void tearDown(VertxTestContext context) { vertx.close().onComplete(context.succeedingThenComplete()); - context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java b/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java index b340b7f0ca4..7c5ece66511 100644 --- a/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java +++ b/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java @@ -26,7 +26,6 @@ import java.time.Clock; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; @@ -96,9 +95,8 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) throws InterruptedException { + public void tearDown(VertxTestContext context) { vertx.close().onComplete(context.succeedingThenComplete()); - context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java b/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java index f91edaa1db3..f1746d438b7 100644 --- a/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java +++ b/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java @@ -22,7 +22,6 @@ import java.time.Instant; import java.time.ZoneId; import java.util.List; -import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import static java.util.Arrays.asList; @@ -59,9 +58,8 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) throws InterruptedException { + public void tearDown(VertxTestContext context) { vertx.close().onComplete(context.succeedingThenComplete()); - context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java b/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java index 629446d6a4a..4b4c3f16e67 100644 --- a/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java +++ b/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java @@ -18,7 +18,6 @@ import org.prebid.server.metric.Metrics; import org.prebid.server.vertx.httpclient.model.HttpClientResponse; -import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import java.util.function.LongSupplier; @@ -52,9 +51,8 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) throws InterruptedException { + public void tearDown(VertxTestContext context) { vertx.close().onComplete(context.succeedingThenComplete()); - context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test From 12cf9404a89a7d02b0ab99da126686523989986a Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 3 Jun 2026 17:38:29 +0300 Subject: [PATCH 14/18] Another minor refactoring. --- .../prebid/server/vertx/verticles/server/ServerVerticle.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java b/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java index 94046bfc0d5..c496b0baa85 100644 --- a/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java +++ b/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java @@ -55,7 +55,8 @@ public void start(Promise startPromise) { } server.listen(address) - .onComplete(result -> startPromise.complete(null, result.cause())) + .mapEmpty() + .onComplete(startPromise) .onSuccess(ignored -> logger.info( "Successfully started {} instance on address: {}, thread: {}", name, From e750d1030a35d6616d4a223815e9511afa6702f1 Mon Sep 17 00:00:00 2001 From: Oleksandr Zhevedenko <720803+Net-burst@users.noreply.github.com> Date: Wed, 3 Jun 2026 21:47:43 -0400 Subject: [PATCH 15/18] Fix malformed Groovy files --- .../server/functional/model/bidder/BidderName.groovy | 2 +- .../model/bidderspecific/BidderRequest.groovy | 11 ++++++++++- .../server/functional/model/privacy/Metric.groovy | 2 +- .../model/request/auction/AuctionEnvironment.groovy | 4 ++-- .../model/request/auction/BidRounding.groovy | 4 ++-- .../functional/model/request/auction/DeviceExt.groovy | 4 ++-- .../model/response/auction/ErrorType.groovy | 4 ++-- .../container/PrebidServerContainer.groovy | 5 +++-- .../server/functional/util/privacy/TcfConsent.groovy | 2 +- .../functional/util/privacy/gpp/GppConsent.groovy | 2 +- 10 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy index 702918fa886..8971403ffed 100644 --- a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy @@ -29,7 +29,7 @@ enum BidderName { MEDIANET("medianet"), AMX("amx"), AMX_CAMEL_CASE("AmX"), - AMX_UPPER_CASE("AMX"), + AMX_UPPER_CASE("AMX") @JsonValue final String value diff --git a/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderRequest.groovy index 25176aaed0f..fa81a35f2da 100644 --- a/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderRequest.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderRequest.groovy @@ -3,10 +3,19 @@ package org.prebid.server.functional.model.bidderspecific import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Imp @EqualsAndHashCode @ToString(includeNames = true, ignoreNulls = true) class BidderRequest extends BidRequest { - List imp + private List imp + + void setImp(List imp) { + this.imp = imp as List + } + + List getImp() { + return imp + } } diff --git a/src/test/groovy/org/prebid/server/functional/model/privacy/Metric.groovy b/src/test/groovy/org/prebid/server/functional/model/privacy/Metric.groovy index 80d779e069d..9b238bc322a 100644 --- a/src/test/groovy/org/prebid/server/functional/model/privacy/Metric.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/privacy/Metric.groovy @@ -11,7 +11,7 @@ enum Metric { ACCOUNT_PROCESSED_RULES_COUNT("requests.activity.processedrules.count"), TEMPLATE_ADAPTER_DISALLOWED_COUNT("adapter.{bidderName}.activity.{activityType}.disallowed.count"), TEMPLATE_ACCOUNT_DISALLOWED_COUNT("account.{accountId}.activity.{activityType}.disallowed.count"), - TEMPLATE_REQUEST_DISALLOWED_COUNT("requests.activity.{activityType}.disallowed.count"), + TEMPLATE_REQUEST_DISALLOWED_COUNT("requests.activity.{activityType}.disallowed.count") final String value diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy index 0148bd3dd72..1530e4de21d 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy @@ -7,10 +7,10 @@ enum AuctionEnvironment { NOT_SUPPORTED(0), DEVICE_ORCHESTRATED(1), SERVER_ORCHESTRATED(3), - UNKNOWN(Integer.MAX_VALUE), + UNKNOWN(Integer.MAX_VALUE) @JsonValue - int value + final int value AuctionEnvironment(Integer value) { this.value = value diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy index ba612ce9f87..c44f1100248 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy @@ -8,9 +8,9 @@ enum BidRounding { DOWN("down"), TRUE("true"), TIME_SPLIT("timesplit"), - UNKNOWN("unknown"), + UNKNOWN("unknown") - private String value + final String value BidRounding(String value) { this.value = value diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceExt.groovy index 72337563b4b..6f4b6e2f8c1 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceExt.groovy @@ -14,10 +14,10 @@ class DeviceExt { UNKNOWN(0), RESTRICTED(1), DENIED(2), - AUTHORIZED(3), + AUTHORIZED(3) @JsonValue - int value + final int value Atts(int value) { this.value = value diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy index ed4055a8a62..975aa9c710a 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy @@ -16,10 +16,10 @@ enum ErrorType { IX("ix"), OPENX("openx"), AMX("amx"), - AMX_UPPER_CASE("AMX"), + AMX_UPPER_CASE("AMX") @JsonValue - private final String value + final String value ErrorType(String value) { this.value = value diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy index 70509252af6..81cbef082fe 100644 --- a/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy @@ -11,12 +11,13 @@ import static org.prebid.server.functional.testcontainers.PbsConfig.DEFAULT_ENV class PrebidServerContainer extends GenericContainer { + public static final String ADMIN_ENDPOINT_USERNAME = "admin" + public static final String ADMIN_ENDPOINT_PASSWORD = "admin" + private static final int PROMETHEUS_PORT = 8070 private static final int DEFAULT_PORT = 8080 private static final int DEFAULT_ADMIN_PORT = 8060 private static final int DEFAULT_DEBUG_PORT = 8000 - private static final String ADMIN_ENDPOINT_USERNAME = "admin" - private static final String ADMIN_ENDPOINT_PASSWORD = "admin" private static final String APP_WORKDIR = "/app/prebid-server/" private static final int PORT = SystemProperties.getPropertyOrDefault("port", DEFAULT_PORT) private static final int ADMIN_PORT = SystemProperties.getPropertyOrDefault("admin.port", DEFAULT_ADMIN_PORT) diff --git a/src/test/groovy/org/prebid/server/functional/util/privacy/TcfConsent.groovy b/src/test/groovy/org/prebid/server/functional/util/privacy/TcfConsent.groovy index cdb6fce665b..1d5a28af2c4 100644 --- a/src/test/groovy/org/prebid/server/functional/util/privacy/TcfConsent.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/privacy/TcfConsent.groovy @@ -143,7 +143,7 @@ class TcfConsent implements ConsentString { TCF_POLICY_V2(2), TCF_POLICY_V4(4), - TCF_POLICY_V5(5), + TCF_POLICY_V5(5) final int value diff --git a/src/test/groovy/org/prebid/server/functional/util/privacy/gpp/GppConsent.groovy b/src/test/groovy/org/prebid/server/functional/util/privacy/gpp/GppConsent.groovy index e4bc73c6410..72203ca7d32 100644 --- a/src/test/groovy/org/prebid/server/functional/util/privacy/gpp/GppConsent.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/privacy/gpp/GppConsent.groovy @@ -70,7 +70,7 @@ abstract class GppConsent implements ConsentString { US_VA_V1(UsVa.NAME, UsVa.VERSION), //9 US_CO_V1(UsCo.NAME, UsCo.VERSION), //10 US_UT_V1(UsUt.NAME, UsUt.VERSION), //11 - US_CT_V1(UsCt.NAME, UsCt.VERSION), //12 + US_CT_V1(UsCt.NAME, UsCt.VERSION) //12 final String name final int version From 1dcf9110bb646d88499f34513f42829828e5ba9e Mon Sep 17 00:00:00 2001 From: Oleksandr Zhevedenko <720803+Net-burst@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:06:48 -0400 Subject: [PATCH 16/18] Revert logger control removal and fix remaining issues --- pom.xml | 67 ++++---- .../admin/LoggerControlKnobHandler.java | 87 +++++++++++ .../prebid/server/log/LoggerControlKnob.java | 68 +++++++++ .../spring/config/ServiceConfiguration.java | 6 + .../admin/AdminEndpointsConfiguration.java | 18 +++ .../handler/LoggerControlKnobHandlerTest.java | 143 ++++++++++++++++++ .../org/prebid/server/it/ApplicationTest.java | 2 +- .../org/prebid/server/it/ConnectAdTest.java | 2 +- 8 files changed, 352 insertions(+), 41 deletions(-) create mode 100644 src/main/java/org/prebid/server/handler/admin/LoggerControlKnobHandler.java create mode 100644 src/main/java/org/prebid/server/log/LoggerControlKnob.java create mode 100644 src/test/java/org/prebid/server/handler/LoggerControlKnobHandlerTest.java diff --git a/pom.xml b/pom.xml index 64510527d11..502ce85983f 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 2.0.0 9.0.1 - 4.2.1 + 4.3.1 1.7.1 3.6.0 0.6.1 @@ -35,16 +35,6 @@ org.springframework.boot spring-boot-starter - - - org.springframework.boot - spring-boot-starter-logging - - - - - org.springframework.boot - spring-boot-starter-log4j2 jakarta.annotation @@ -294,11 +284,11 @@ xml-path test - - - - - + + org.spockframework + spock-core + test + org.apache.groovy groovy @@ -580,7 +570,6 @@ ${spring.boot.version} false - true @@ -614,28 +603,28 @@ - - - - - - - - - - - - - - - - - - - - - - + + org.codehaus.gmavenplus + gmavenplus-plugin + + true + + + ${project.basedir}/src/test/groovy + + **/*.groovy + + + + + + + + compileTests + + + + org.apache.maven.plugins maven-failsafe-plugin diff --git a/src/main/java/org/prebid/server/handler/admin/LoggerControlKnobHandler.java b/src/main/java/org/prebid/server/handler/admin/LoggerControlKnobHandler.java new file mode 100644 index 00000000000..7875c7a52dd --- /dev/null +++ b/src/main/java/org/prebid/server/handler/admin/LoggerControlKnobHandler.java @@ -0,0 +1,87 @@ +package org.prebid.server.handler.admin; + +import io.netty.handler.codec.http.HttpResponseStatus; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; +import org.prebid.server.exception.InvalidRequestException; +import org.prebid.server.log.LoggerControlKnob; +import org.prebid.server.util.HttpUtil; + +import java.time.Duration; +import java.util.Objects; +import java.util.Set; + +public class LoggerControlKnobHandler implements Handler { + + private static final String LEVEL_PARAMETER = "level"; + private static final String DURATION_PARAMETER = "duration"; + + private static final Set ALLOWED_LEVELS = Set.of("error", "warn", "info", "debug"); + + private final long maxDurationMs; + private final LoggerControlKnob loggerControlKnob; + private final String endpoint; + + public LoggerControlKnobHandler(long maxDurationMs, LoggerControlKnob loggerControlKnob, String endpoint) { + this.maxDurationMs = maxDurationMs; + this.loggerControlKnob = Objects.requireNonNull(loggerControlKnob); + this.endpoint = Objects.requireNonNull(endpoint); + } + + @Override + public void handle(RoutingContext routingContext) { + try { + final MultiMap parameters = routingContext.request().params(); + loggerControlKnob.changeLogLevel(readLevel(parameters), readDuration(parameters)); + + HttpUtil.executeSafely(routingContext, endpoint, + HttpServerResponse::end); + } catch (InvalidRequestException e) { + HttpUtil.executeSafely(routingContext, endpoint, + response -> response + .setStatusCode(HttpResponseStatus.BAD_REQUEST.code()) + .end(e.getMessage())); + } + } + + private String readLevel(MultiMap parameters) { + final String level = parameters.get(LEVEL_PARAMETER); + + if (level == null) { + throw new InvalidRequestException("Missing required parameter '%s'".formatted(LEVEL_PARAMETER)); + } + + if (!ALLOWED_LEVELS.contains(level.toLowerCase())) { + throw new InvalidRequestException("Invalid '%s' parameter value, allowed values '%s'" + .formatted(LEVEL_PARAMETER, ALLOWED_LEVELS)); + } + + return level; + } + + private Duration readDuration(MultiMap parameters) { + final Integer duration = getIntParameter(DURATION_PARAMETER, parameters); + + if (duration == null) { + throw new InvalidRequestException("Missing required parameter '%s'".formatted(DURATION_PARAMETER)); + } + + if (duration < 1 || duration > maxDurationMs) { + throw new InvalidRequestException( + "Parameter '%s' must be between %d and %d".formatted(DURATION_PARAMETER, 0, maxDurationMs)); + } + + return Duration.ofMillis(duration); + } + + private Integer getIntParameter(String parameterName, MultiMap parameters) { + final String value = parameters.get(parameterName); + try { + return value != null ? Integer.parseInt(value) : null; + } catch (NumberFormatException e) { + throw new InvalidRequestException("Invalid '%s' parameter value".formatted(parameterName)); + } + } +} diff --git a/src/main/java/org/prebid/server/log/LoggerControlKnob.java b/src/main/java/org/prebid/server/log/LoggerControlKnob.java new file mode 100644 index 00000000000..3c74cd36b15 --- /dev/null +++ b/src/main/java/org/prebid/server/log/LoggerControlKnob.java @@ -0,0 +1,68 @@ +package org.prebid.server.log; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import io.vertx.core.Vertx; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.util.Objects; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class LoggerControlKnob { + + private static final String PREBID_LOGGER = "org.prebid.server"; + + private final Vertx vertx; + private final Logger logger; + private final Level originalLevel; + + private final Lock lock = new ReentrantLock(); + private Long restoreTimerId; + + public LoggerControlKnob(Vertx vertx) { + this.vertx = Objects.requireNonNull(vertx); + + logger = getPrebidLogger(); + originalLevel = logger != null ? logger.getLevel() : null; + } + + public void changeLogLevel(String level, Duration duration) { + if (logger == null) { + return; + } + + lock.lock(); + try { + if (restoreTimerId != null) { + vertx.cancelTimer(restoreTimerId); + restoreTimerId = null; + } + + logger.setLevel(Level.toLevel(level, originalLevel)); + restoreTimerId = vertx.setTimer(duration.toMillis(), this::resetLoggerLevel); + } finally { + lock.unlock(); + } + } + + private static Logger getPrebidLogger() { + final org.slf4j.Logger prebidSlf4jLogger = LoggerFactory.getLogger(PREBID_LOGGER); + return prebidSlf4jLogger instanceof Logger ? (Logger) prebidSlf4jLogger : null; + } + + private void resetLoggerLevel(long triggeredTimerId) { + lock.lock(); + try { + if (restoreTimerId == null || triggeredTimerId != restoreTimerId) { + return; + } + + logger.setLevel(originalLevel); + restoreTimerId = null; + } finally { + lock.unlock(); + } + } +} diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java index 2159ed9f6c8..597feb1f1ac 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -105,6 +105,7 @@ import org.prebid.server.log.CriteriaLogManager; import org.prebid.server.log.CriteriaManager; import org.prebid.server.log.HttpInteractionLogger; +import org.prebid.server.log.LoggerControlKnob; import org.prebid.server.metric.Metrics; import org.prebid.server.optout.GoogleRecaptchaVerifier; import org.prebid.server.privacy.HostVendorTcfDefinerService; @@ -1274,6 +1275,11 @@ HttpInteractionLogger httpInteractionLogger(JacksonMapper mapper) { return new HttpInteractionLogger(mapper); } + @Bean + LoggerControlKnob loggerControlKnob(Vertx vertx) { + return new LoggerControlKnob(vertx); + } + @Bean DsaEnforcer dsaEnforcer(JacksonMapper mapper) { return new DsaEnforcer(mapper); diff --git a/src/main/java/org/prebid/server/spring/config/server/admin/AdminEndpointsConfiguration.java b/src/main/java/org/prebid/server/spring/config/server/admin/AdminEndpointsConfiguration.java index 70c13eb83bb..a5938aab396 100644 --- a/src/main/java/org/prebid/server/spring/config/server/admin/AdminEndpointsConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/server/admin/AdminEndpointsConfiguration.java @@ -10,12 +10,14 @@ import org.prebid.server.handler.admin.CollectedMetricsHandler; import org.prebid.server.handler.admin.CurrencyRatesHandler; import org.prebid.server.handler.admin.HttpInteractionLogHandler; +import org.prebid.server.handler.admin.LoggerControlKnobHandler; import org.prebid.server.handler.admin.SettingsCacheNotificationHandler; import org.prebid.server.handler.admin.TracerLogHandler; import org.prebid.server.handler.admin.VersionHandler; import org.prebid.server.json.JacksonMapper; import org.prebid.server.log.CriteriaManager; import org.prebid.server.log.HttpInteractionLogger; +import org.prebid.server.log.LoggerControlKnob; import org.prebid.server.settings.CachingApplicationSettings; import org.prebid.server.settings.SettingsCache; import org.prebid.server.util.VersionInfo; @@ -136,6 +138,22 @@ AdminResource loggingHttpInteractionEndpoint( new HttpInteractionLogHandler(maxLimit, httpInteractionLogger, path)); } + @Bean + @ConditionalOnExpression("${admin-endpoints.logging-changelevel.enabled} == true") + AdminResource loggingChangeLevelEndpoint( + @Value("${logging.change-level.max-duration-ms}") long maxDuration, + LoggerControlKnob loggerControlKnob, + @Value("${admin-endpoints.logging-changelevel.path}") String path, + @Value("${admin-endpoints.logging-changelevel.on-application-port}") boolean isOnApplicationPort, + @Value("${admin-endpoints.logging-changelevel.protected}") boolean isProtected) { + + return new AdminResourceWrapper( + path, + isOnApplicationPort, + isProtected, + new LoggerControlKnobHandler(maxDuration, loggerControlKnob, path)); + } + @Bean @ConditionalOnProperty(prefix = "admin-endpoints.tracelog", name = "enabled", havingValue = "true") AdminResource tracerLogEndpoint( diff --git a/src/test/java/org/prebid/server/handler/LoggerControlKnobHandlerTest.java b/src/test/java/org/prebid/server/handler/LoggerControlKnobHandlerTest.java new file mode 100644 index 00000000000..d3fcaae8b57 --- /dev/null +++ b/src/test/java/org/prebid/server/handler/LoggerControlKnobHandlerTest.java @@ -0,0 +1,143 @@ +package org.prebid.server.handler; + +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.handler.admin.LoggerControlKnobHandler; +import org.prebid.server.log.LoggerControlKnob; + +import java.time.Duration; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +@ExtendWith(MockitoExtension.class) +public class LoggerControlKnobHandlerTest { + + @Mock + private LoggerControlKnob loggerControlKnob; + + private LoggerControlKnobHandler handler; + + @Mock + private RoutingContext routingContext; + @Mock + private HttpServerRequest httpRequest; + @Mock(strictness = LENIENT) + private HttpServerResponse httpResponse; + + @BeforeEach + public void setUp() { + given(routingContext.request()).willReturn(httpRequest); + given(routingContext.response()).willReturn(httpResponse); + given(httpResponse.setStatusCode(anyInt())).willReturn(httpResponse); + + handler = new LoggerControlKnobHandler(10000L, loggerControlKnob, "endpoint"); + } + + @Test + public void shouldChangeLevelWhenAllParametersPresent() { + // given + given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() + .add("level", "info") + .add("duration", "1000")); + + // when + handler.handle(routingContext); + + // then + verify(loggerControlKnob).changeLogLevel("info", Duration.ofMillis(1000L)); + } + + @Test + public void shouldRespondWithErrorWhenLevelAbsent() { + // given + given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap()); + + // when + handler.handle(routingContext); + + // then + verify(httpResponse).setStatusCode(eq(400)); + verify(httpResponse).end(eq("Missing required parameter 'level'")); + + verifyNoInteractions(loggerControlKnob); + } + + @Test + public void shouldRespondWithErrorWhenLevelNotValid() { + // given + given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() + .add("level", "abc")); + + // when + handler.handle(routingContext); + + // then + verify(httpResponse).setStatusCode(eq(400)); + verify(httpResponse).end(startsWith("Invalid 'level' parameter value")); + + verifyNoInteractions(loggerControlKnob); + } + + @Test + public void shouldRespondWithErrorWhenDurationAbsent() { + // given + given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() + .add("level", "info")); + + // when + handler.handle(routingContext); + + // then + verify(httpResponse).setStatusCode(eq(400)); + verify(httpResponse).end(eq("Missing required parameter 'duration'")); + + verifyNoInteractions(loggerControlKnob); + } + + @Test + public void shouldRespondWithErrorWhenDurationNotInteger() { + // given + given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() + .add("level", "info") + .add("duration", "abc")); + + // when + handler.handle(routingContext); + + // then + verify(httpResponse).setStatusCode(eq(400)); + verify(httpResponse).end(eq("Invalid 'duration' parameter value")); + + verifyNoInteractions(loggerControlKnob); + } + + @Test + public void shouldRespondWithErrorWhenDurationNotValid() { + // given + given(httpRequest.params()).willReturn(MultiMap.caseInsensitiveMultiMap() + .add("level", "info") + .add("duration", "20000")); + + // when + handler.handle(routingContext); + + // then + verify(httpResponse).setStatusCode(eq(400)); + verify(httpResponse).end(eq("Parameter 'duration' must be between 0 and 10000")); + + verifyNoInteractions(loggerControlKnob); + } +} diff --git a/src/test/java/org/prebid/server/it/ApplicationTest.java b/src/test/java/org/prebid/server/it/ApplicationTest.java index 3a3710c6e6e..b80221891a6 100644 --- a/src/test/java/org/prebid/server/it/ApplicationTest.java +++ b/src/test/java/org/prebid/server/it/ApplicationTest.java @@ -85,7 +85,7 @@ public void testOpenrtb2AuctionCoreFunctionality() throws IOException, JSONExcep // given WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/generic-exchange")) .withHeader("Accept", equalTo("application/json")) - .withHeader("Content-Type", equalTo("application/json;charset=UTF-8")) + .withHeader("Content-Type", equalTo("application/json;charset=utf-8")) .withRequestBody(equalToJson( jsonFrom("openrtb2/generic_core_functionality/test-generic-bid-request.json"))) .willReturn(aResponse().withBody( diff --git a/src/test/java/org/prebid/server/it/ConnectAdTest.java b/src/test/java/org/prebid/server/it/ConnectAdTest.java index 45b60d2e431..657ba1a0a19 100644 --- a/src/test/java/org/prebid/server/it/ConnectAdTest.java +++ b/src/test/java/org/prebid/server/it/ConnectAdTest.java @@ -21,7 +21,7 @@ public void openrtb2AuctionShouldRespondWithBidsFromConnectAd() throws IOExcepti // given WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/connectad-exchange")) .withHeader("Accept", equalTo("application/json")) - .withHeader("Content-Type", equalTo("application/json;charset=UTF-8")) + .withHeader("Content-Type", equalTo("application/json;charset=utf-8")) .withHeader("User-Agent", equalTo("userAgent")) .withHeader("X-Forwarded-For", equalTo("193.168.244.1")) .withHeader("DNT", equalTo("0")) From ca45f9b1bf7807014b470e59c7f0090fe0ce2d63 Mon Sep 17 00:00:00 2001 From: Oleksandr Zhevedenko <720803+Net-burst@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:33:26 -0400 Subject: [PATCH 17/18] Bump to Java 25 --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer.json | 2 +- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/docker-image-publish.yml | 2 +- .github/workflows/pr-functional-tests.yml | 2 +- .github/workflows/pr-java-ci.yml | 2 +- .../workflows/pr-module-functional-tests.yml | 2 +- .github/workflows/release-asset-publish.yml | 2 +- Dockerfile | 2 +- Dockerfile-modules | 2 +- README.md | 2 +- extra/pom.xml | 17 ++++++++++++++++- pom.xml | 8 -------- 13 files changed, 28 insertions(+), 21 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f64a14137d4..23e63602f2f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,3 +1,3 @@ # From https://github.com/microsoft/vscode-dev-containers/blob/master/containers/go/.devcontainer/Dockerfile -ARG VARIANT="21-jdk-bookworm" +ARG VARIANT="25-jdk-trixie" FROM mcr.microsoft.com/vscode/devcontainers/java:${VARIANT} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d9a309d3661..6b872b2b8d0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,7 @@ "dockerfile": "Dockerfile", "args": { // Update the VARIANT arg to pick a version of Java - "VARIANT": "21-jdk-bookworm", + "VARIANT": "25-jdk-trixie", } }, "containerEnv": { diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a6852ae7c92..ad440b85e32 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 21 + java-version: 25 - name: Cache Maven packages uses: actions/cache@v5 @@ -52,7 +52,7 @@ jobs: - name: Build with Maven if: matrix.build-mode == 'manual' - run: mvn -B package --file extra/pom.xml + run: mvn -B clean package -DskipUnitTests --file extra/pom.xml - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/docker-image-publish.yml b/.github/workflows/docker-image-publish.yml index 7f993ade73d..e3991f85a1e 100644 --- a/.github/workflows/docker-image-publish.yml +++ b/.github/workflows/docker-image-publish.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ 21 ] + java: [ 25 ] dockerfile-path: [ Dockerfile, Dockerfile-modules ] include: - dockerfile-path: Dockerfile diff --git a/.github/workflows/pr-functional-tests.yml b/.github/workflows/pr-functional-tests.yml index d512022413a..f2f8fb0c571 100644 --- a/.github/workflows/pr-functional-tests.yml +++ b/.github/workflows/pr-functional-tests.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: - java: [ 21 ] + java: [ 25 ] steps: - uses: actions/checkout@v5 diff --git a/.github/workflows/pr-java-ci.yml b/.github/workflows/pr-java-ci.yml index d69d222592f..608191c6d5f 100644 --- a/.github/workflows/pr-java-ci.yml +++ b/.github/workflows/pr-java-ci.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: - java: [ 21 ] + java: [ 25 ] steps: - uses: actions/checkout@v5 diff --git a/.github/workflows/pr-module-functional-tests.yml b/.github/workflows/pr-module-functional-tests.yml index c3b04858677..6741e45b1d6 100644 --- a/.github/workflows/pr-module-functional-tests.yml +++ b/.github/workflows/pr-module-functional-tests.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: - java: [ 21 ] + java: [ 25 ] steps: - uses: actions/checkout@v5 diff --git a/.github/workflows/release-asset-publish.yml b/.github/workflows/release-asset-publish.yml index bfa938bebe9..9b6e38c7a7c 100644 --- a/.github/workflows/release-asset-publish.yml +++ b/.github/workflows/release-asset-publish.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ 21 ] + java: [ 25 ] steps: - uses: actions/checkout@v5 - name: Set up JDK diff --git a/Dockerfile b/Dockerfile index 7de0126d535..402df0e39d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM amazoncorretto:21.0.8-al2023 +FROM amazoncorretto:25.0.3-al2023-headless WORKDIR /app/prebid-server diff --git a/Dockerfile-modules b/Dockerfile-modules index 1626999164a..f794f7c1946 100644 --- a/Dockerfile-modules +++ b/Dockerfile-modules @@ -1,4 +1,4 @@ -FROM amazoncorretto:21.0.8-al2023 +FROM amazoncorretto:25.0.3-al2023-headless WORKDIR /app/prebid-server diff --git a/README.md b/README.md index b5aa6539aae..795bb29af77 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Follow next steps to create JAR file which can be deployed locally. - Install prerequsites - Java SDK: Oracle's or Corretto. Let us know if there's a distribution PBS-Java doesn't work with. - - Java SDK Version: 21 + - Java SDK Version: 25 - Maven - Clone the project: diff --git a/extra/pom.xml b/extra/pom.xml index 2b35bce3a8a..f5d788d05d2 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -16,7 +16,7 @@ - 21 + 25 UTF-8 UTF-8 ${java.version} @@ -379,6 +379,21 @@ + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + + org.projectlombok + lombok + + + + diff --git a/pom.xml b/pom.xml index 502ce85983f..5a92c838116 100644 --- a/pom.xml +++ b/pom.xml @@ -666,14 +666,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - ${java.version} - ${java.version} - - From bea17a326c24a7514077e32eb56656228cbd7b07 Mon Sep 17 00:00:00 2001 From: Viktor Kryshtal <33136089+Lightwood13@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:02:30 +0300 Subject: [PATCH 18/18] Refactor Initializable interface to use Future (#4527) --- .../data/config/DatabaseReaderFactory.java | 8 +++--- .../reporter/agma/AgmaAnalyticsReporter.java | 5 ++-- .../pubstack/PubstackAnalyticsReporter.java | 5 ++-- .../currency/CurrencyConversionService.java | 5 ++-- .../DatabasePeriodicRefreshService.java | 5 ++-- .../service/HttpPeriodicRefreshService.java | 5 ++-- .../service/S3PeriodicRefreshService.java | 9 +++---- .../util/system/CpuLoadAverageStats.java | 6 ++--- .../prebid/server/vertx/Initializable.java | 4 +-- .../verticles/server/DaemonVerticle.java | 27 +++++++------------ .../PubstackAnalyticsReporterTest.java | 16 +++++------ .../CurrencyConversionServiceTest.java | 3 +-- .../DatabasePeriodicRefreshServiceTest.java | 3 +-- .../HttpPeriodicRefreshServiceTest.java | 3 +-- .../service/S3PeriodicRefreshServiceTest.java | 5 +--- 15 files changed, 42 insertions(+), 67 deletions(-) diff --git a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/DatabaseReaderFactory.java b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/DatabaseReaderFactory.java index 19155c86e8f..61b969eee6f 100644 --- a/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/DatabaseReaderFactory.java +++ b/extra/modules/greenbids-real-time-data/src/main/java/org/prebid/server/hooks/modules/greenbids/real/time/data/config/DatabaseReaderFactory.java @@ -4,7 +4,6 @@ import com.maxmind.geoip2.DatabaseReader; import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.core.file.FileSystem; import io.vertx.core.file.OpenOptions; @@ -45,11 +44,10 @@ public DatabaseReaderFactory(GreenbidsRealTimeDataProperties properties, Vertx v } @Override - public void initialize(Promise initializePromise) { - downloadAndExtract() + public Future initialize() { + return downloadAndExtract() .onSuccess(databaseReaderRef::set) - .mapEmpty() - .onComplete(initializePromise); + .mapEmpty(); } private Future downloadAndExtract() { diff --git a/src/main/java/org/prebid/server/analytics/reporter/agma/AgmaAnalyticsReporter.java b/src/main/java/org/prebid/server/analytics/reporter/agma/AgmaAnalyticsReporter.java index ed99c241ee5..48aa9e5d3d8 100644 --- a/src/main/java/org/prebid/server/analytics/reporter/agma/AgmaAnalyticsReporter.java +++ b/src/main/java/org/prebid/server/analytics/reporter/agma/AgmaAnalyticsReporter.java @@ -12,7 +12,6 @@ import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.MultiMap; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpMethod; @@ -97,9 +96,9 @@ public AgmaAnalyticsReporter(AgmaAnalyticsProperties agmaAnalyticsProperties, } @Override - public void initialize(Promise initializePromise) { + public Future initialize() { vertx.setPeriodic(bufferTimeoutMs, ignored -> sendEvents(buffer.pollAll())); - initializePromise.complete(); + return Future.succeededFuture(); } @Override diff --git a/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java b/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java index 1fd2ab7749b..b2c947c3a9c 100644 --- a/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java +++ b/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java @@ -1,7 +1,6 @@ package org.prebid.server.analytics.reporter.pubstack; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.BooleanUtils; @@ -116,9 +115,9 @@ public String name() { } @Override - public void initialize(Promise initializePromise) { + public Future initialize() { vertx.setPeriodic(configurationRefreshDelay, id -> fetchRemoteConfig()); - fetchRemoteConfig().onComplete(initializePromise); + return fetchRemoteConfig(); } void shutdown() { diff --git a/src/main/java/org/prebid/server/currency/CurrencyConversionService.java b/src/main/java/org/prebid/server/currency/CurrencyConversionService.java index efcaf4ad7c2..ab398f877cd 100644 --- a/src/main/java/org/prebid/server/currency/CurrencyConversionService.java +++ b/src/main/java/org/prebid/server/currency/CurrencyConversionService.java @@ -2,7 +2,6 @@ import com.iab.openrtb.request.BidRequest; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.BooleanUtils; @@ -68,7 +67,7 @@ public CurrencyConversionService(ExternalConversionProperties externalConversion * Must be called on Vertx event loop thread. */ @Override - public void initialize(Promise initializePromise) { + public Future initialize() { if (externalConversionProperties != null) { final Long refreshPeriod = externalConversionProperties.getRefreshPeriodMs(); final Long defaultTimeout = externalConversionProperties.getDefaultTimeoutMs(); @@ -84,7 +83,7 @@ public void initialize(Promise initializePromise) { externalConversionProperties.getMetrics().createCurrencyRatesGauge(this::isRatesStale); } - initializePromise.tryComplete(); + return Future.succeededFuture(); } /** diff --git a/src/main/java/org/prebid/server/settings/service/DatabasePeriodicRefreshService.java b/src/main/java/org/prebid/server/settings/service/DatabasePeriodicRefreshService.java index 7714243954d..9ace90196c2 100644 --- a/src/main/java/org/prebid/server/settings/service/DatabasePeriodicRefreshService.java +++ b/src/main/java/org/prebid/server/settings/service/DatabasePeriodicRefreshService.java @@ -1,7 +1,6 @@ package org.prebid.server.settings.service; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.apache.commons.lang3.StringUtils; import org.prebid.server.execution.timeout.Timeout; @@ -104,12 +103,12 @@ public DatabasePeriodicRefreshService(String initQuery, } @Override - public void initialize(Promise initializePromise) { + public Future initialize() { getAll(); if (refreshPeriod > 0) { vertx.setPeriodic(refreshPeriod, aLong -> refresh()); } - initializePromise.tryComplete(); + return Future.succeededFuture(); } private void getAll() { diff --git a/src/main/java/org/prebid/server/settings/service/HttpPeriodicRefreshService.java b/src/main/java/org/prebid/server/settings/service/HttpPeriodicRefreshService.java index 7669074d455..b32773eb7ce 100644 --- a/src/main/java/org/prebid/server/settings/service/HttpPeriodicRefreshService.java +++ b/src/main/java/org/prebid/server/settings/service/HttpPeriodicRefreshService.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.prebid.server.exception.PreBidException; import org.prebid.server.json.DecodeException; @@ -91,13 +90,13 @@ public HttpPeriodicRefreshService(String refreshUrl, } @Override - public void initialize(Promise initializePromise) { + public Future initialize() { getAll(); if (refreshPeriod > 0) { vertx.setPeriodic(refreshPeriod, aLong -> refresh()); } - initializePromise.tryComplete(); + return Future.succeededFuture(); } private void getAll() { diff --git a/src/main/java/org/prebid/server/settings/service/S3PeriodicRefreshService.java b/src/main/java/org/prebid/server/settings/service/S3PeriodicRefreshService.java index 35ff2a6f55a..30688dd09cf 100644 --- a/src/main/java/org/prebid/server/settings/service/S3PeriodicRefreshService.java +++ b/src/main/java/org/prebid/server/settings/service/S3PeriodicRefreshService.java @@ -2,7 +2,6 @@ import io.vertx.core.CompositeFuture; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.prebid.server.auction.model.Tuple2; import org.prebid.server.log.Logger; @@ -73,15 +72,13 @@ public S3PeriodicRefreshService(S3AsyncClient asyncClient, } @Override - public void initialize(Promise initializePromise) { - fetchStoredDataResult(clock.millis(), MetricName.initialize) - .mapEmpty() - .onComplete(initializePromise); - + public Future initialize() { if (refreshPeriod > 0) { logger.info("Starting s3 periodic refresh for " + cacheType + " every " + refreshPeriod + " s"); vertx.setPeriodic(refreshPeriod, ignored -> fetchStoredDataResult(clock.millis(), MetricName.update)); } + + return fetchStoredDataResult(clock.millis(), MetricName.initialize).mapEmpty(); } private Future> fetchStoredDataResult(long startTime, MetricName metricName) { diff --git a/src/main/java/org/prebid/server/util/system/CpuLoadAverageStats.java b/src/main/java/org/prebid/server/util/system/CpuLoadAverageStats.java index 6be897cd1d4..0ff182c4ac7 100644 --- a/src/main/java/org/prebid/server/util/system/CpuLoadAverageStats.java +++ b/src/main/java/org/prebid/server/util/system/CpuLoadAverageStats.java @@ -1,6 +1,6 @@ package org.prebid.server.util.system; -import io.vertx.core.Promise; +import io.vertx.core.Future; import io.vertx.core.Vertx; import org.prebid.server.vertx.Initializable; import oshi.SystemInfo; @@ -31,10 +31,10 @@ public CpuLoadAverageStats(Vertx vertx, long measurementIntervalMillis) { } @Override - public void initialize(Promise initializePromise) { + public Future initialize() { measureCpuLoad(); vertx.setPeriodic(measurementIntervalMillis, timerId -> measureCpuLoad()); - initializePromise.tryComplete(); + return Future.succeededFuture(); } private void measureCpuLoad() { diff --git a/src/main/java/org/prebid/server/vertx/Initializable.java b/src/main/java/org/prebid/server/vertx/Initializable.java index 5c12c30e47d..3ecfe6e8037 100644 --- a/src/main/java/org/prebid/server/vertx/Initializable.java +++ b/src/main/java/org/prebid/server/vertx/Initializable.java @@ -1,7 +1,7 @@ package org.prebid.server.vertx; +import io.vertx.core.Future; import io.vertx.core.Handler; -import io.vertx.core.Promise; /** * Denotes components requiring initialization after they have been created. @@ -12,5 +12,5 @@ @FunctionalInterface public interface Initializable { - void initialize(Promise initializePromise); + Future initialize(); } diff --git a/src/main/java/org/prebid/server/vertx/verticles/server/DaemonVerticle.java b/src/main/java/org/prebid/server/vertx/verticles/server/DaemonVerticle.java index c6175ccaafa..e64a3eab5d1 100644 --- a/src/main/java/org/prebid/server/vertx/verticles/server/DaemonVerticle.java +++ b/src/main/java/org/prebid/server/vertx/verticles/server/DaemonVerticle.java @@ -11,10 +11,8 @@ import org.prebid.server.vertx.CloseableAdapter; import org.prebid.server.vertx.Initializable; -import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.function.Consumer; import java.util.function.Function; public class DaemonVerticle extends AbstractVerticle { @@ -33,31 +31,26 @@ public DaemonVerticle(List initializables, List startPromise) { - all(initializables, initializable -> initializable::initialize).onComplete(startPromise); + all(initializables, Initializable::initialize, true).onComplete(startPromise); } @Override public void stop(Promise stopPromise) { - all(closeables, closeable -> closeable::close).onComplete(stopPromise); + all(closeables, closeable -> Future.future(closeable::close), false).onComplete(stopPromise); } - private static Future all(Collection entries, - Function>> entryToPromiseConsumerMapper) { + private static Future all( + Collection entries, + Function> entryToFutureMapper, + boolean start) { - final List> entriesFutures = new ArrayList<>(); - - for (E entry : entries) { - final Promise entryPromise = Promise.promise(); - entriesFutures.add(entryPromise.future()); - - entryToPromiseConsumerMapper.apply(entry).accept(entryPromise); - } - - return Future.all(entriesFutures) + return Future.all(entries.stream().map(entryToFutureMapper).toList()) .onSuccess(r -> logger.info( - "Successfully started {} instance on thread: {}", + "Successfully {} {} instance on thread: {}", + start ? "started" : "stopped", DaemonVerticle.class.getSimpleName(), Thread.currentThread().getName())) .mapEmpty(); } + } diff --git a/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java b/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java index e971498c598..55753587562 100644 --- a/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java +++ b/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -93,7 +92,7 @@ public void initializeShouldFetchConfigAndSetPeriodicTimerForConfigUpdate() thro Future.succeededFuture(HttpClientResponse.of(200, null, mapper.writeValueAsString(pubstackConfig)))); // when - pubstackAnalyticsReporter.initialize(Promise.promise()); + pubstackAnalyticsReporter.initialize(); // then verify(vertx).setPeriodic(anyLong(), any()); @@ -109,17 +108,16 @@ public void initializeShouldFailUpdateSendBuffersAndSetTimerWhenEndpointFromRemo throws JsonProcessingException { // given - final Promise promise = Promise.promise(); final PubstackConfig pubstackConfig = PubstackConfig.of("newScopeId", "invalid", Collections.singletonMap(EventType.auction, true)); given(httpClient.get(anyString(), anyLong())).willReturn( Future.succeededFuture(HttpClientResponse.of(200, null, mapper.writeValueAsString(pubstackConfig)))); // when - pubstackAnalyticsReporter.initialize(promise); + final Future result = pubstackAnalyticsReporter.initialize(); // then - assertThatThrownBy(() -> promise.future().await(5, TimeUnit.SECONDS)) + assertThatThrownBy(() -> result.await(5, TimeUnit.SECONDS)) .hasMessage("[pubstack] Failed to create event report url for endpoint: invalid") .isInstanceOf(PreBidException.class); verify(auctionHandler).reportEvents(); @@ -138,8 +136,8 @@ public void initializeShouldNotUpdateEventsIfFetchedConfigIsSameAsPrevious() thr Future.succeededFuture(HttpClientResponse.of(200, null, mapper.writeValueAsString(pubstackConfig)))); // when - pubstackAnalyticsReporter.initialize(Promise.promise()); - pubstackAnalyticsReporter.initialize(Promise.promise()); + pubstackAnalyticsReporter.initialize(); + pubstackAnalyticsReporter.initialize(); // then verify(httpClient, times(2)).get(anyString(), anyLong()); @@ -157,7 +155,7 @@ public void initializeShouldNotSendEventsAndUpdateConfigsWhenResponseStatusIsNot Future.succeededFuture(HttpClientResponse.of(400, null, null))); // when - pubstackAnalyticsReporter.initialize(Promise.promise()); + pubstackAnalyticsReporter.initialize(); // then verify(vertx).setPeriodic(anyLong(), any()); @@ -173,7 +171,7 @@ public void initializeShouldNotSendEventsAndUpdateConfigsWhenCantParseResponse() Future.succeededFuture(HttpClientResponse.of(200, null, "{\"endpoint\" : {}}"))); // when - pubstackAnalyticsReporter.initialize(Promise.promise()); + pubstackAnalyticsReporter.initialize(); // then verify(vertx).setPeriodic(anyLong(), any()); diff --git a/src/test/java/org/prebid/server/auction/CurrencyConversionServiceTest.java b/src/test/java/org/prebid/server/auction/CurrencyConversionServiceTest.java index 61a28789710..ed9030c493f 100644 --- a/src/test/java/org/prebid/server/auction/CurrencyConversionServiceTest.java +++ b/src/test/java/org/prebid/server/auction/CurrencyConversionServiceTest.java @@ -4,7 +4,6 @@ import com.iab.openrtb.request.BidRequest; import io.vertx.core.Future; import io.vertx.core.Handler; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -397,7 +396,7 @@ private CurrencyConversionService createInitializedService(String url, clock, jacksonMapper)); - currencyService.initialize(Promise.promise()); + currencyService.initialize(); return currencyService; } diff --git a/src/test/java/org/prebid/server/settings/service/DatabasePeriodicRefreshServiceTest.java b/src/test/java/org/prebid/server/settings/service/DatabasePeriodicRefreshServiceTest.java index bae78c111b4..626fffa58b7 100644 --- a/src/test/java/org/prebid/server/settings/service/DatabasePeriodicRefreshServiceTest.java +++ b/src/test/java/org/prebid/server/settings/service/DatabasePeriodicRefreshServiceTest.java @@ -2,7 +2,6 @@ import io.vertx.core.Future; import io.vertx.core.Handler; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -156,7 +155,7 @@ private void createAndInitService(long refresh) { metrics, clock); - databasePeriodicRefreshService.initialize(Promise.promise()); + databasePeriodicRefreshService.initialize(); } @SuppressWarnings("unchecked") diff --git a/src/test/java/org/prebid/server/settings/service/HttpPeriodicRefreshServiceTest.java b/src/test/java/org/prebid/server/settings/service/HttpPeriodicRefreshServiceTest.java index 3cb36048ffb..0cd1643fe2b 100644 --- a/src/test/java/org/prebid/server/settings/service/HttpPeriodicRefreshServiceTest.java +++ b/src/test/java/org/prebid/server/settings/service/HttpPeriodicRefreshServiceTest.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.Future; import io.vertx.core.Handler; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -174,7 +173,7 @@ private static void createAndInitService(CacheNotificationListener notif final HttpPeriodicRefreshService httpPeriodicRefreshService = new HttpPeriodicRefreshService( url, refreshPeriod, timeout, notificationListener, vertx, httpClient, jacksonMapper); - httpPeriodicRefreshService.initialize(Promise.promise()); + httpPeriodicRefreshService.initialize(); } @SuppressWarnings("unchecked") diff --git a/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java b/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java index 7c5ece66511..3b6ae88be3d 100644 --- a/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java +++ b/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java @@ -1,7 +1,6 @@ package org.prebid.server.settings.service; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; @@ -167,8 +166,6 @@ private Future createAndInitService(long refreshPeriod) { metrics, vertx); - final Promise init = Promise.promise(); - s3PeriodicRefreshService.initialize(init); - return init.future(); + return s3PeriodicRefreshService.initialize(); } }