This is an automated email from the ASF dual-hosted git repository. shuber pushed a commit to branch unomi-3-dev in repository https://gitbox.apache.org/repos/asf/unomi.git
commit 6f90cbb5c883b14412931435135309aeec1c46ea Author: Jérôme Blanchard <[email protected]> AuthorDate: Tue Dec 30 11:31:22 2025 +0100 test: add healthcheck test for concurrency. --- .../org/apache/unomi/itests/HealthCheckIT.java | 102 ++++++++------------- 1 file changed, 39 insertions(+), 63 deletions(-) diff --git a/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java b/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java index 1db256917..fcf4551ef 100644 --- a/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java +++ b/itests/src/test/java/org/apache/unomi/itests/HealthCheckIT.java @@ -33,9 +33,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import static org.junit.Assert.fail; @@ -55,79 +56,54 @@ public class HealthCheckIT extends BaseIT { @Test public void testHealthCheck() { try { - // Retry health check until all required providers are LIVE - List<HealthCheckResponse> response = waitForHealthCheckReady(); + List<HealthCheckResponse> response = get(HEALTHCHECK_ENDPOINT, new TypeReference<>() {}); LOGGER.info("health check response: {}", response); Assert.assertNotNull(response); - // Health check may return 4 or 5 providers depending on configuration (persistence may be included) - Assert.assertTrue("Health check should return at least 4 providers", response.size() >= 4); - Assert.assertTrue("Karaf health check should be LIVE", - response.stream().anyMatch(r -> r.getName().equals("karaf") && r.getStatus() == HealthCheckResponse.Status.LIVE)); - Assert.assertTrue("Search engine (" + searchEngine + ") health check should be LIVE", - response.stream().anyMatch(r -> r.getName().equals(searchEngine) && r.getStatus() == HealthCheckResponse.Status.LIVE)); - Assert.assertTrue("Unomi health check should be LIVE", - response.stream().anyMatch(r -> r.getName().equals("unomi") && r.getStatus() == HealthCheckResponse.Status.LIVE)); - Assert.assertTrue("Cluster health check should be LIVE", - response.stream().anyMatch(r -> r.getName().equals("cluster") && r.getStatus() == HealthCheckResponse.Status.LIVE)); + Assert.assertEquals(4, response.size()); + Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("karaf") && r.getStatus() == HealthCheckResponse.Status.LIVE)); + Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals(searchEngine) && r.getStatus() == HealthCheckResponse.Status.LIVE)); + Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("unomi") && r.getStatus() == HealthCheckResponse.Status.LIVE)); + Assert.assertTrue(response.stream().anyMatch(r -> r.getName().equals("cluster") && r.getStatus() == HealthCheckResponse.Status.LIVE)); } catch (Exception e) { LOGGER.error("Error while executing health check", e); fail("Error while executing health check" + e.getMessage()); } } - /** - * Waits for health check to be ready by retrying until all required providers are LIVE. - * This method retries the health check with exponential backoff up to a maximum timeout. - * - * @return List of health check responses when all required providers are LIVE - * @throws InterruptedException if the maximum timeout is reached - */ - private List<HealthCheckResponse> waitForHealthCheckReady() throws InterruptedException { - final long maxWaitTime = TimeUnit.SECONDS.toMillis(30); // Maximum 30 seconds - final long initialRetryInterval = 500; // Start with 500ms - final long maxRetryInterval = 2000; // Max 2 seconds between retries - final long startTime = System.currentTimeMillis(); - long retryInterval = initialRetryInterval; - int attemptCount = 0; - - while (System.currentTimeMillis() - startTime < maxWaitTime) { - attemptCount++; - try { - List<HealthCheckResponse> response = get(HEALTHCHECK_ENDPOINT, new TypeReference<>() {}); - - if (response != null && response.size() >= 4) { - // Check if all required providers are LIVE - boolean karafLive = response.stream().anyMatch(r -> r.getName().equals("karaf") && r.getStatus() == HealthCheckResponse.Status.LIVE); - boolean searchEngineLive = response.stream().anyMatch(r -> r.getName().equals(searchEngine) && r.getStatus() == HealthCheckResponse.Status.LIVE); - boolean unomiLive = response.stream().anyMatch(r -> r.getName().equals("unomi") && r.getStatus() == HealthCheckResponse.Status.LIVE); - boolean clusterLive = response.stream().anyMatch(r -> r.getName().equals("cluster") && r.getStatus() == HealthCheckResponse.Status.LIVE); - - if (karafLive && searchEngineLive && unomiLive && clusterLive) { - LOGGER.info("All health checks are LIVE after {} attempts ({} ms)", attemptCount, System.currentTimeMillis() - startTime); - return response; - } else { - LOGGER.debug("Health check attempt {}: karaf={}, {}={}, unomi={}, cluster={}", - attemptCount, karafLive, searchEngine, searchEngineLive, unomiLive, clusterLive); - } - } else { - LOGGER.debug("Health check attempt {}: response is null or has insufficient providers (size: {})", - attemptCount, response != null ? response.size() : 0); + @Test + public void testConcurrentHealthCheck() { + final int NB_THREADS = 10; + final int NB_ITERATIONS = 20; + + ExecutorService executorService = null; + try { + executorService = Executors.newFixedThreadPool(NB_THREADS); + List<Future<List<HealthCheckResponse>>> futures = new ArrayList<>(); + for (int i = 0; i < NB_ITERATIONS; i++) { + for (int j = 0; j < NB_THREADS; j++) { + Future<List<HealthCheckResponse>> future = executorService.submit(() -> get(HEALTHCHECK_ENDPOINT, new TypeReference<>() {})); + futures.add(future); } - } catch (Exception e) { - LOGGER.debug("Health check attempt {} failed: {}", attemptCount, e.getMessage()); + for (Future<List<HealthCheckResponse>> future : futures) { + List<HealthCheckResponse> health = future.get(10, TimeUnit.SECONDS); + Assert.assertEquals(4, health.size()); + Assert.assertTrue(health.stream().anyMatch(r -> r.getName().equals("karaf") && r.getStatus() == HealthCheckResponse.Status.LIVE)); + Assert.assertTrue(health.stream().anyMatch(r -> r.getName().equals(searchEngine) && r.getStatus() == HealthCheckResponse.Status.LIVE)); + Assert.assertTrue(health.stream().anyMatch(r -> r.getName().equals("unomi") && r.getStatus() == HealthCheckResponse.Status.LIVE)); + Assert.assertTrue(health.stream().anyMatch(r -> r.getName().equals("cluster") && r.getStatus() == HealthCheckResponse.Status.LIVE)); + } + Thread.sleep(10); } - - // Wait before retrying with exponential backoff - Thread.sleep(retryInterval); - retryInterval = Math.min(retryInterval * 2, maxRetryInterval); - } - - // Final attempt without waiting - List<HealthCheckResponse> finalResponse = get(HEALTHCHECK_ENDPOINT, new TypeReference<>() {}); - if (finalResponse == null) { - throw new InterruptedException("Health check did not become ready within " + maxWaitTime + " ms after " + attemptCount + " attempts"); + executorService.shutdown(); + Assert.assertTrue(executorService.awaitTermination(10, TimeUnit.SECONDS)); + } catch (Exception e) { + LOGGER.error("Error while executing concurrent health check", e); + fail("Error while executing concurrent health check: " + e.getMessage()); + } finally { + if ( executorService != null ) { + executorService.shutdownNow(); + } } - return finalResponse; } protected <T> T get(final String url, TypeReference<T> typeReference) {
