Skip to content

Closing the context can hang when Hikari pool suspension is enabled - #52276

Open
challa wants to merge 1 commit into
spring-projects:4.0.xfrom
challa:gh-hikari-no-suspend-on-close
Open

challa wants to merge 1 commit into
spring-projects:4.0.xfrom
challa:gh-hikari-no-suspend-on-close

Conversation

@challa

@challa challa commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

HikariCheckpointRestoreLifecycle is a plain Lifecycle, so its stop() method is called whenever the application context is closed, not only before a checkpoint. With spring.datasource.hikari.allow-pool-suspension=true, which the lifecycle's warning asks for when org.crac is on the classpath, stop() suspends the pool before beans are destroyed. A bean that borrows a connection while it is being destroyed then blocks forever, as HikariCP's SuspendResumeLock uses Semaphore.acquireUninterruptibly() while the pool is suspended.

For example, with Hibernate and an embedded database, where ddl-auto defaults to create-drop, the schema is dropped when the EntityManagerFactory is closed and context.close() never returns:

java.util.concurrent.Semaphore.acquireUninterruptibly(Semaphore.java:341)
com.zaxxer.hikari.util.SuspendResumeLock.acquire(SuspendResumeLock.java:72)
com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:154)
...
org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.getIsolatedConnection(GenerationTargetToDatabase.java:57)

The same application closes normally with allow-pool-suspension=false.

This change makes stop() return early when the context is being closed, as the data source is closed during bean destruction anyway. The warning about pool suspension was already skipped in that case, so its own check is no longer needed. The new test in HikariCheckpointRestoreLifecycleTests fails without the change.

One case this doesn't cover: if the context is stopped, which suspends the pool, and then closed without being started again, the lifecycle is no longer running at close so the pool remains suspended. I wasn't sure whether that's something you'd want handled, so I've left it out.

I used an AI coding assistant for parts of the analysis; I reviewed and verified the change and tests myself.

HikariCheckpointRestoreLifecycle is a plain Lifecycle, so it is stopped
whenever the application context is closed, not only before a
checkpoint. When pool suspension is allowed, stop() suspends the pool
before beans are destroyed. A bean that borrows a connection while it
is being destroyed, for example when Hibernate drops the schema with
ddl-auto=create-drop, then waits forever as HikariCP blocks without a
timeout while the pool is suspended.

This commit leaves the pool alone when the context is being closed as
the data source is closed during bean destruction anyway.

See spring-projectsgh-52276

Signed-off-by: Challa Srinivasa Reddy <challa.srinivasareddy@gmail.com>
@challa
challa force-pushed the gh-hikari-no-suspend-on-close branch from 3d5fb20 to 3b7c803 Compare October 17, 2026 11:06
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Oct 17, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants