RED-9496 - Implement graceful shutdown #571
@ -0,0 +1,57 @@
|
|||||||
|
package com.iqser.red.service.persistence.management.v1.processor.lifecycle;
|
||||||
|
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LifecycleAspect {
|
||||||
|
|
||||||
|
private final LifecycleManager lifecycleManager;
|
||||||
|
|
||||||
|
private final LifecycleProperties lifecycleProperties;
|
||||||
|
|
||||||
|
|
||||||
|
@Around("@annotation(org.springframework.amqp.rabbit.annotation.RabbitListener) || "
|
||||||
|
+ "@annotation(org.springframework.web.bind.annotation.GetMapping) || "
|
||||||
|
+ "@annotation(org.springframework.web.bind.annotation.PostMapping) || "
|
||||||
|
+ "@annotation(org.springframework.web.bind.annotation.PutMapping) || "
|
||||||
|
+ "@annotation(org.springframework.web.bind.annotation.DeleteMapping) || "
|
||||||
|
+ "@annotation(org.springframework.web.bind.annotation.RequestMapping))")
|
||||||
|
public Object checkLifecycle(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
|
|
||||||
|
String targetClassName = joinPoint.getTarget().getClass().getPackageName();
|
||||||
|
if (!targetClassName.startsWith(lifecycleProperties.getBasePackage())) {
|
||||||
|
return joinPoint.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (lifecycleManager) {
|
||||||
|
if (!lifecycleManager.isRunning()) {
|
||||||
|
log.info("Application is shutting down, rejecting new messages.");
|
||||||
|
throw new AmqpRejectAndDontRequeueException("Application is shutting down, rejecting new messages.");
|
||||||
|
}
|
||||||
|
lifecycleManager.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return joinPoint.proceed();
|
||||||
|
} finally {
|
||||||
|
int remainingTasks = lifecycleManager.decrementAndGet();
|
||||||
|
synchronized (lifecycleManager) {
|
||||||
|
if (remainingTasks == 0 && !lifecycleManager.isRunning()) {
|
||||||
|
lifecycleManager.countDown();
|
||||||
|
log.info("All tasks are done, ready for shutdown.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
package com.iqser.red.service.persistence.management.v1.processor.lifecycle;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.context.SmartLifecycle;
|
||||||
|
import org.springframework.context.event.ContextClosedEvent;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LifecycleManager implements SmartLifecycle, ApplicationListener<ContextClosedEvent> {
|
||||||
|
|
||||||
|
private volatile boolean running; // by default initialized as false
|
||||||
|
private volatile boolean shutdownInitiated; // by default initialized as false
|
||||||
|
private final Object shutdownMonitor = new Object();
|
||||||
|
private final AtomicInteger activeTasks = new AtomicInteger(0);
|
||||||
|
private final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
|
||||||
|
public void countDown() {
|
||||||
|
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int incrementAndGet() {
|
||||||
|
|
||||||
|
return activeTasks.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int decrementAndGet() {
|
||||||
|
|
||||||
|
return activeTasks.decrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
|
||||||
|
synchronized (shutdownMonitor) {
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
|
||||||
|
synchronized (shutdownMonitor) {
|
||||||
|
running = false;
|
||||||
|
if (activeTasks.get() == 0) {
|
||||||
|
latch.countDown(); // No active tasks, release the latch immediately
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAutoStartup() {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPhase() {
|
||||||
|
|
||||||
|
return Integer.MAX_VALUE; // Start this component last and stop it first
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(ContextClosedEvent event) {
|
||||||
|
|
||||||
|
synchronized (shutdownMonitor) {
|
||||||
|
if (shutdownInitiated) {
|
||||||
|
return; // Avoid multiple shutdown initiations
|
||||||
|
}
|
||||||
|
shutdownInitiated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop();
|
||||||
|
log.info("Context is closing, waiting for ongoing tasks to complete.");
|
||||||
|
try {
|
||||||
|
latch.await(); // Wait for the latch to count down to zero
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
log.error("Shutdown was interrupted", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.iqser.red.service.persistence.management.v1.processor.lifecycle;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@ConfigurationProperties("lifecycle")
|
||||||
|
public class LifecycleProperties {
|
||||||
|
|
||||||
|
private String basePackage;
|
||||||
|
}
|
||||||
@ -24,7 +24,6 @@ dependencies {
|
|||||||
api(project(":persistence-service-internal-api-impl-v1"))
|
api(project(":persistence-service-internal-api-impl-v1"))
|
||||||
api(project(":persistence-service-shared-mongo-v1"))
|
api(project(":persistence-service-shared-mongo-v1"))
|
||||||
api("com.iqser.red.commons:storage-commons:2.49.0")
|
api("com.iqser.red.commons:storage-commons:2.49.0")
|
||||||
api("com.knecon.fforesight:lifecycle-commons:0.6.0")
|
|
||||||
api("junit:junit:4.13.2")
|
api("junit:junit:4.13.2")
|
||||||
api("org.apache.logging.log4j:log4j-slf4j-impl:2.20.0")
|
api("org.apache.logging.log4j:log4j-slf4j-impl:2.20.0")
|
||||||
api("net.logstash.logback:logstash-logback-encoder:7.4")
|
api("net.logstash.logback:logstash-logback-encoder:7.4")
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
package com.iqser.red.service.peristence.v1.server;
|
package com.iqser.red.service.peristence.v1.server;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
|
||||||
import com.knecon.fforesight.lifecyclecommons.LifecycleAutoconfiguration;
|
|
||||||
import org.apache.catalina.Context;
|
|
||||||
import org.apache.tomcat.websocket.server.WsSci;
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
@ -23,12 +18,11 @@ import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfigurati
|
|||||||
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
|
|
||||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
|
||||||
import org.springframework.cache.annotation.EnableCaching;
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||||||
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
|
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
|
||||||
@ -47,6 +41,7 @@ import com.iqser.red.service.dictionarymerge.commons.DictionaryMergeService;
|
|||||||
import com.iqser.red.service.persistence.management.v1.processor.PersistenceServiceProcessorConfiguration;
|
import com.iqser.red.service.persistence.management.v1.processor.PersistenceServiceProcessorConfiguration;
|
||||||
import com.iqser.red.service.persistence.management.v1.processor.cache.PersistenceServiceExternalApiCacheConfiguration;
|
import com.iqser.red.service.persistence.management.v1.processor.cache.PersistenceServiceExternalApiCacheConfiguration;
|
||||||
import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
|
import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
|
||||||
|
import com.iqser.red.service.persistence.management.v1.processor.lifecycle.LifecycleProperties;
|
||||||
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
|
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
|
||||||
import com.iqser.red.service.persistence.v1.internal.api.PersistenceServiceInternalApiConfiguration;
|
import com.iqser.red.service.persistence.v1.internal.api.PersistenceServiceInternalApiConfiguration;
|
||||||
import com.iqser.red.storage.commons.StorageAutoConfiguration;
|
import com.iqser.red.storage.commons.StorageAutoConfiguration;
|
||||||
@ -72,9 +67,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@EnableRetry
|
@EnableRetry
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@EnableCaching
|
@EnableCaching
|
||||||
@EnableConfigurationProperties({FileManagementServiceSettings.class})
|
@EnableConfigurationProperties({FileManagementServiceSettings.class, LifecycleProperties.class})
|
||||||
@EnableMongoRepositories(basePackages = "com.iqser.red.service.persistence")
|
@EnableMongoRepositories(basePackages = "com.iqser.red.service.persistence")
|
||||||
@ImportAutoConfiguration({StorageAutoConfiguration.class, JobsAutoConfiguration.class, DatabaseTenantCommonsAutoConfiguration.class, MultiTenancyAutoConfiguration.class, SpringDocAutoConfiguration.class, DefaultKeyCloakCommonsAutoConfiguration.class, MongoDatabaseCommonsAutoConfiguration.class, LifecycleAutoconfiguration.class})
|
@ImportAutoConfiguration({StorageAutoConfiguration.class, JobsAutoConfiguration.class, DatabaseTenantCommonsAutoConfiguration.class, MultiTenancyAutoConfiguration.class, SpringDocAutoConfiguration.class, DefaultKeyCloakCommonsAutoConfiguration.class, MongoDatabaseCommonsAutoConfiguration.class})
|
||||||
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, CassandraAutoConfiguration.class, DataSourceAutoConfiguration.class, LiquibaseAutoConfiguration.class, MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
|
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, CassandraAutoConfiguration.class, DataSourceAutoConfiguration.class, LiquibaseAutoConfiguration.class, MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
|
||||||
@Import({PersistenceServiceExternalApiConfigurationV2.class, PersistenceServiceExternalApiConfiguration.class, PersistenceServiceInternalApiConfiguration.class, PersistenceServiceExternalApiCacheConfiguration.class, MultiTenancyWebConfiguration.class, PersistenceServiceProcessorConfiguration.class, MessagingConfiguration.class, MultiTenancyMessagingConfiguration.class})
|
@Import({PersistenceServiceExternalApiConfigurationV2.class, PersistenceServiceExternalApiConfiguration.class, PersistenceServiceInternalApiConfiguration.class, PersistenceServiceExternalApiCacheConfiguration.class, MultiTenancyWebConfiguration.class, PersistenceServiceProcessorConfiguration.class, MessagingConfiguration.class, MultiTenancyMessagingConfiguration.class})
|
||||||
@EnableAspectJAutoProxy
|
@EnableAspectJAutoProxy
|
||||||
|
|||||||
@ -22,7 +22,7 @@ server:
|
|||||||
port: 8080
|
port: 8080
|
||||||
|
|
||||||
lifecycle:
|
lifecycle:
|
||||||
base-package: com.iqser.red.service.peristence
|
base-package: com.iqser.red.service.persistence
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
main:
|
main:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user