From ba0b016847502357ced01e124067ecb86b2dd8a2 Mon Sep 17 00:00:00 2001 From: IvanBorislavovDimitrov Date: Fri, 20 Feb 2026 15:11:33 +0200 Subject: [PATCH] Stop health check executors during shutdown --- .../multiapps/controller/core/Messages.java | 2 + .../health/ApplicationHealthCalculator.java | 47 +++++++++++++------ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java index 87e44224ce..0e7a987734 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java @@ -215,6 +215,8 @@ public final class Messages { public static final String CHECKING_DATABASE_HEALTH = "Checking database health..."; public static final String CHECKING_OBJECT_STORE_HEALTH = "Checking object store health..."; public static final String CHECKING_FOR_INCREASED_LOCKS = "Checking for increased locks..."; + public static final String SHUTTING_DOWN_APPLICATION_HEALTH_CALCULATOR = "Shutting down application health calculator..."; + public static final String SKIPPING_HEALTH_STATUS_UPDATE_APPLICATION_IS_SHUTTING_DOWN = "Skipping health status update - application is shutting down"; // Audit log diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/application/health/ApplicationHealthCalculator.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/application/health/ApplicationHealthCalculator.java index ee183ad20f..5d96c616db 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/application/health/ApplicationHealthCalculator.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/application/health/ApplicationHealthCalculator.java @@ -1,19 +1,5 @@ package org.cloudfoundry.multiapps.controller.core.application.health; -import java.text.MessageFormat; -import java.time.Duration; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; import jakarta.inject.Inject; import jakarta.inject.Named; import org.cloudfoundry.multiapps.common.SLException; @@ -29,12 +15,28 @@ import org.cloudfoundry.multiapps.controller.persistence.services.FileStorage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import java.text.MessageFormat; +import java.time.Duration; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + @Named -public class ApplicationHealthCalculator { +public class ApplicationHealthCalculator implements DisposableBean { private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationHealthCalculator.class); @@ -69,6 +71,8 @@ public class ApplicationHealthCalculator { private final ResilientOperationExecutor resilientOperationExecutor = getResilienceExecutor(); + private volatile boolean isShuttingDown = false; + @Inject public ApplicationHealthCalculator(@Autowired(required = false) FileStorage objectStoreFileStorage, ApplicationConfiguration applicationConfiguration, DatabaseHealthService databaseHealthService, @@ -85,6 +89,10 @@ protected void scheduleRegularHealthUpdate() { } protected void updateHealthStatus() { + if (isShuttingDown) { + LOGGER.debug(Messages.SKIPPING_HEALTH_STATUS_UPDATE_APPLICATION_IS_SHUTTING_DOWN); + return; + } List> tasks = List.of(this::isObjectStoreFileStorageHealthy, this::isDatabaseHealthy, this::checkForIncreasedLocksWithTimeout); try { @@ -232,4 +240,13 @@ protected ResilientOperationExecutor getResilienceExecutor() { return new ResilientOperationExecutor(); } + @Override + public void destroy() { + LOGGER.info(Messages.SHUTTING_DOWN_APPLICATION_HEALTH_CALCULATOR); + isShuttingDown = true; + scheduler.shutdownNow(); + taskExecutor.shutdownNow(); + timeoutExecutor.shutdownNow(); + } + }