Skip to content

Commit 5a7e632

Browse files
committed
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
2 parents 4ab80d2 + 70d5756 commit 5a7e632

File tree

3 files changed

+41
-7
lines changed

3 files changed

+41
-7
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2626
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2727
import org.springframework.boot.jdbc.HikariCheckpointRestoreLifecycle;
28+
import org.springframework.context.ConfigurableApplicationContext;
2829
import org.springframework.context.annotation.Bean;
2930
import org.springframework.context.annotation.Configuration;
3031

@@ -44,8 +45,9 @@ static class Hikari {
4445

4546
@Bean
4647
@ConditionalOnMissingBean
47-
HikariCheckpointRestoreLifecycle hikariCheckpointRestoreLifecycle(DataSource dataSource) {
48-
return new HikariCheckpointRestoreLifecycle(dataSource);
48+
HikariCheckpointRestoreLifecycle hikariCheckpointRestoreLifecycle(DataSource dataSource,
49+
ConfigurableApplicationContext applicationContext) {
50+
return new HikariCheckpointRestoreLifecycle(dataSource, applicationContext);
4951
}
5052

5153
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/HikariCheckpointRestoreLifecycle.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
3434
import org.apache.commons.logging.Log;
3535
import org.apache.commons.logging.LogFactory;
3636

37+
import org.springframework.context.ConfigurableApplicationContext;
3738
import org.springframework.context.Lifecycle;
3839
import org.springframework.core.log.LogMessage;
3940
import org.springframework.util.Assert;
@@ -49,6 +50,7 @@
4950
*
5051
* @author Christoph Strobl
5152
* @author Andy Wilkinson
53+
* @author Moritz Halbritter
5254
* @since 3.2.0
5355
*/
5456
public class HikariCheckpointRestoreLifecycle implements Lifecycle {
@@ -71,15 +73,34 @@ public class HikariCheckpointRestoreLifecycle implements Lifecycle {
7173

7274
private final HikariDataSource dataSource;
7375

76+
private final ConfigurableApplicationContext applicationContext;
77+
7478
/**
7579
* Creates a new {@code HikariCheckpointRestoreLifecycle} that will allow the given
7680
* {@code dataSource} to participate in checkpoint-restore. The {@code dataSource} is
7781
* {@link DataSourceUnwrapper#unwrap unwrapped} to a {@link HikariDataSource}. If such
7882
* unwrapping is not possible, the lifecycle will have no effect.
7983
* @param dataSource the checkpoint-restore participant
84+
* @deprecated since 3.4.0 for removal in 3.6.0 in favor of
85+
* {@link #HikariCheckpointRestoreLifecycle(DataSource, ConfigurableApplicationContext)}
8086
*/
87+
@Deprecated(since = "3.4.0", forRemoval = true)
8188
public HikariCheckpointRestoreLifecycle(DataSource dataSource) {
89+
this(dataSource, null);
90+
}
91+
92+
/**
93+
* Creates a new {@code HikariCheckpointRestoreLifecycle} that will allow the given
94+
* {@code dataSource} to participate in checkpoint-restore. The {@code dataSource} is
95+
* {@link DataSourceUnwrapper#unwrap unwrapped} to a {@link HikariDataSource}. If such
96+
* unwrapping is not possible, the lifecycle will have no effect.
97+
* @param dataSource the checkpoint-restore participant
98+
* @param applicationContext the application context
99+
* @since 3.4.0
100+
*/
101+
public HikariCheckpointRestoreLifecycle(DataSource dataSource, ConfigurableApplicationContext applicationContext) {
82102
this.dataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class, HikariDataSource.class);
103+
this.applicationContext = applicationContext;
83104
this.hasOpenConnections = (pool) -> {
84105
ThreadPoolExecutor closeConnectionExecutor = (ThreadPoolExecutor) ReflectionUtils
85106
.getField(CLOSE_CONNECTION_EXECUTOR, pool);
@@ -109,13 +130,21 @@ public void stop() {
109130
logger.info("Suspending Hikari pool");
110131
this.dataSource.getHikariPoolMXBean().suspendPool();
111132
}
133+
else {
134+
if (this.applicationContext != null && !this.applicationContext.isClosed()) {
135+
logger.warn(this.dataSource + " is not configured to allow pool suspension. "
136+
+ "This will cause problems when the application is checkpointed. "
137+
+ "Please configure allow-pool-suspension to fix this!");
138+
}
139+
}
112140
closeConnections(Duration.ofMillis(this.dataSource.getConnectionTimeout() + 250));
113141
}
114142

115143
private void closeConnections(Duration shutdownTimeout) {
116144
logger.info("Evicting Hikari connections");
117145
this.dataSource.getHikariPoolMXBean().softEvictConnections();
118-
logger.debug("Waiting for Hikari connections to be closed");
146+
logger.debug(LogMessage.format("Waiting %d seconds for Hikari connections to be closed",
147+
shutdownTimeout.toSeconds()));
119148
CompletableFuture<Void> allConnectionsClosed = CompletableFuture.runAsync(this::waitForConnectionsToClose);
120149
try {
121150
allConnectionsClosed.get(shutdownTimeout.toMillis(), TimeUnit.MILLISECONDS);

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/jdbc/HikariCheckpointRestoreLifecycleTests.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.zaxxer.hikari.HikariDataSource;
2525
import org.junit.jupiter.api.Test;
2626

27+
import org.springframework.context.ConfigurableApplicationContext;
28+
2729
import static org.assertj.core.api.Assertions.assertThat;
2830
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2931
import static org.assertj.core.api.Assertions.assertThatNoException;
@@ -47,7 +49,8 @@ class HikariCheckpointRestoreLifecycleTests {
4749
config.setJdbcUrl("jdbc:hsqldb:mem:test-" + UUID.randomUUID());
4850
config.setPoolName("lifecycle-tests");
4951
this.dataSource = new HikariDataSource(config);
50-
this.lifecycle = new HikariCheckpointRestoreLifecycle(this.dataSource);
52+
this.lifecycle = new HikariCheckpointRestoreLifecycle(this.dataSource,
53+
mock(ConfigurableApplicationContext.class));
5154
}
5255

5356
@Test
@@ -88,7 +91,7 @@ void whenDataSourceIsClosedThenStartShouldThrow() {
8891
@Test
8992
void startHasNoEffectWhenDataSourceIsNotAHikariDataSource() {
9093
HikariCheckpointRestoreLifecycle nonHikariLifecycle = new HikariCheckpointRestoreLifecycle(
91-
mock(DataSource.class));
94+
mock(DataSource.class), mock(ConfigurableApplicationContext.class));
9295
assertThat(nonHikariLifecycle.isRunning()).isFalse();
9396
nonHikariLifecycle.start();
9497
assertThat(nonHikariLifecycle.isRunning()).isFalse();
@@ -97,7 +100,7 @@ void startHasNoEffectWhenDataSourceIsNotAHikariDataSource() {
97100
@Test
98101
void stopHasNoEffectWhenDataSourceIsNotAHikariDataSource() {
99102
HikariCheckpointRestoreLifecycle nonHikariLifecycle = new HikariCheckpointRestoreLifecycle(
100-
mock(DataSource.class));
103+
mock(DataSource.class), mock(ConfigurableApplicationContext.class));
101104
assertThat(nonHikariLifecycle.isRunning()).isFalse();
102105
nonHikariLifecycle.stop();
103106
assertThat(nonHikariLifecycle.isRunning()).isFalse();

0 commit comments

Comments
 (0)