Merge pull request #42937 from deki
* pr/42937: Polish "Add logger warning if Hikari datasource doesn't have pool suspension configured" Add logger warning if Hikari datasource doesn't have pool suspension configured Closes gh-42937
This commit is contained in:
commit
5a7e632210
|
@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnCheckpointR
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.jdbc.HikariCheckpointRestoreLifecycle;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
@ -44,8 +45,9 @@ class DataSourceCheckpointRestoreConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HikariCheckpointRestoreLifecycle hikariCheckpointRestoreLifecycle(DataSource dataSource) {
|
||||
return new HikariCheckpointRestoreLifecycle(dataSource);
|
||||
HikariCheckpointRestoreLifecycle hikariCheckpointRestoreLifecycle(DataSource dataSource,
|
||||
ConfigurableApplicationContext applicationContext) {
|
||||
return new HikariCheckpointRestoreLifecycle(dataSource, applicationContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -34,6 +34,7 @@ import com.zaxxer.hikari.pool.HikariPool;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -49,6 +50,7 @@ import org.springframework.util.ReflectionUtils;
|
|||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public class HikariCheckpointRestoreLifecycle implements Lifecycle {
|
||||
|
@ -71,15 +73,34 @@ public class HikariCheckpointRestoreLifecycle implements Lifecycle {
|
|||
|
||||
private final HikariDataSource dataSource;
|
||||
|
||||
private final ConfigurableApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* Creates a new {@code HikariCheckpointRestoreLifecycle} that will allow the given
|
||||
* {@code dataSource} to participate in checkpoint-restore. The {@code dataSource} is
|
||||
* {@link DataSourceUnwrapper#unwrap unwrapped} to a {@link HikariDataSource}. If such
|
||||
* unwrapping is not possible, the lifecycle will have no effect.
|
||||
* @param dataSource the checkpoint-restore participant
|
||||
* @deprecated since 3.4.0 for removal in 3.6.0 in favor of
|
||||
* {@link #HikariCheckpointRestoreLifecycle(DataSource, ConfigurableApplicationContext)}
|
||||
*/
|
||||
@Deprecated(since = "3.4.0", forRemoval = true)
|
||||
public HikariCheckpointRestoreLifecycle(DataSource dataSource) {
|
||||
this(dataSource, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code HikariCheckpointRestoreLifecycle} that will allow the given
|
||||
* {@code dataSource} to participate in checkpoint-restore. The {@code dataSource} is
|
||||
* {@link DataSourceUnwrapper#unwrap unwrapped} to a {@link HikariDataSource}. If such
|
||||
* unwrapping is not possible, the lifecycle will have no effect.
|
||||
* @param dataSource the checkpoint-restore participant
|
||||
* @param applicationContext the application context
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public HikariCheckpointRestoreLifecycle(DataSource dataSource, ConfigurableApplicationContext applicationContext) {
|
||||
this.dataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class, HikariDataSource.class);
|
||||
this.applicationContext = applicationContext;
|
||||
this.hasOpenConnections = (pool) -> {
|
||||
ThreadPoolExecutor closeConnectionExecutor = (ThreadPoolExecutor) ReflectionUtils
|
||||
.getField(CLOSE_CONNECTION_EXECUTOR, pool);
|
||||
|
@ -109,13 +130,21 @@ public class HikariCheckpointRestoreLifecycle implements Lifecycle {
|
|||
logger.info("Suspending Hikari pool");
|
||||
this.dataSource.getHikariPoolMXBean().suspendPool();
|
||||
}
|
||||
else {
|
||||
if (this.applicationContext != null && !this.applicationContext.isClosed()) {
|
||||
logger.warn(this.dataSource + " is not configured to allow pool suspension. "
|
||||
+ "This will cause problems when the application is checkpointed. "
|
||||
+ "Please configure allow-pool-suspension to fix this!");
|
||||
}
|
||||
}
|
||||
closeConnections(Duration.ofMillis(this.dataSource.getConnectionTimeout() + 250));
|
||||
}
|
||||
|
||||
private void closeConnections(Duration shutdownTimeout) {
|
||||
logger.info("Evicting Hikari connections");
|
||||
this.dataSource.getHikariPoolMXBean().softEvictConnections();
|
||||
logger.debug("Waiting for Hikari connections to be closed");
|
||||
logger.debug(LogMessage.format("Waiting %d seconds for Hikari connections to be closed",
|
||||
shutdownTimeout.toSeconds()));
|
||||
CompletableFuture<Void> allConnectionsClosed = CompletableFuture.runAsync(this::waitForConnectionsToClose);
|
||||
try {
|
||||
allConnectionsClosed.get(shutdownTimeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
|
|
|
@ -24,6 +24,8 @@ import com.zaxxer.hikari.HikariConfig;
|
|||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
|
@ -47,7 +49,8 @@ class HikariCheckpointRestoreLifecycleTests {
|
|||
config.setJdbcUrl("jdbc:hsqldb:mem:test-" + UUID.randomUUID());
|
||||
config.setPoolName("lifecycle-tests");
|
||||
this.dataSource = new HikariDataSource(config);
|
||||
this.lifecycle = new HikariCheckpointRestoreLifecycle(this.dataSource);
|
||||
this.lifecycle = new HikariCheckpointRestoreLifecycle(this.dataSource,
|
||||
mock(ConfigurableApplicationContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -88,7 +91,7 @@ class HikariCheckpointRestoreLifecycleTests {
|
|||
@Test
|
||||
void startHasNoEffectWhenDataSourceIsNotAHikariDataSource() {
|
||||
HikariCheckpointRestoreLifecycle nonHikariLifecycle = new HikariCheckpointRestoreLifecycle(
|
||||
mock(DataSource.class));
|
||||
mock(DataSource.class), mock(ConfigurableApplicationContext.class));
|
||||
assertThat(nonHikariLifecycle.isRunning()).isFalse();
|
||||
nonHikariLifecycle.start();
|
||||
assertThat(nonHikariLifecycle.isRunning()).isFalse();
|
||||
|
@ -97,7 +100,7 @@ class HikariCheckpointRestoreLifecycleTests {
|
|||
@Test
|
||||
void stopHasNoEffectWhenDataSourceIsNotAHikariDataSource() {
|
||||
HikariCheckpointRestoreLifecycle nonHikariLifecycle = new HikariCheckpointRestoreLifecycle(
|
||||
mock(DataSource.class));
|
||||
mock(DataSource.class), mock(ConfigurableApplicationContext.class));
|
||||
assertThat(nonHikariLifecycle.isRunning()).isFalse();
|
||||
nonHikariLifecycle.stop();
|
||||
assertThat(nonHikariLifecycle.isRunning()).isFalse();
|
||||
|
|
Loading…
Reference in New Issue