-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Description
Bug description
By registering a JVM exit callback using JobExecutionShutdownHook, and then using kill to shut down the JVM program during task execution, you will see an exception indicating that the database operation failed.
Environment
openjdk 25.0.2 2026-01-20 LTS
spring batch 6.0.2
spring boot 4.0.3
Steps to reproduce
registering a JVM exit callback, and execute command kill 62989
val hook = JobExecutionShutdownHook(jobExecution, jobOperator)
Runtime.getRuntime().addShutdownHook(hook)I also tried SpringApplication.getShutdownHandlers().add(hook), but it still couldn't save the work to the DB.
The exception log shows that this line threw a database operation exception:
org.springframework.batch.core.launch.support.SimpleJobOperator.stop(SimpleJobOperator.java:349)
full log:
2026-03-01T15:19:45.071+08:00 INFO 34906 --- [sast-web3-consumer] [ Thread-12] o.s.b.c.l.s.JobExecutionShutdownHook : Received JVM shutdown signal
2026-03-01T15:19:45.071+08:00 INFO 34906 --- [sast-web3-consumer] [ Thread-12] o.s.b.c.l.s.JobExecutionShutdownHook : Attempting to gracefully stop job execution 228
2026-03-01T15:19:45.072+08:00 INFO 34906 --- [sast-web3-consumer] [ Thread-12] o.s.b.c.l.s.TaskExecutorJobOperator : Stopping job execution: JobExecution: id=228, version=1, startTime=2026-03-01T15:19:09.782276, endTime=null, lastUpdated=2026-03-01T15:19:09.782424, status=STARTED, exitStatus=exitCode=UNKNOWN;exitDescription=, job=[JobInstance: id=225, version=0, Job=[contractAnalysisJob]], jobParameters=[{JobParameter{name='task', value=ContractScanTask(analyzer=Semgrep, analysisId=1190, filePath=null, objKey=adds, extra=null, contractAddress=x faf88wf aaw a]odwdded 9aw d, chainType=ETH, dataType=SRC), type=class io.github.xhstormr.sast.model.contract.ContractScanTask, identifying=true}}]
2026-03-01T15:19:45.578+08:00 INFO 34906 --- [sast-web3-consumer] [ionShutdownHook] i.g.x.s.d.c.handler.ApplicationLogger : Application is destroyed!
2026-03-01T15:19:45.589+08:00 WARN 34906 --- [sast-web3-consumer] [ Thread-12] com.zaxxer.hikari.pool.ProxyConnection : HikariPool-1 - Connection org.postgresql.jdbc.PgConnection@665f7826 marked as broken because of SQLSTATE(08006), ErrorCode(0)
org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:456)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:387)
at org.postgresql.jdbc.PgConnection.executeTransactionCommand(PgConnection.java:998)
at org.postgresql.jdbc.PgConnection.commit(PgConnection.java:1020)
at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)
at com.zaxxer.hikari.pool.HikariProxyConnection.commit(HikariProxyConnection.java)
at org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.commit(AbstractLogicalConnectionImplementor.java:84)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commitNoRollbackOnly(JdbcResourceLocalTransactionCoordinatorImpl.java:249)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:242)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:89)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:553)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:794)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:757)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:687)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:408)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:130)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:222)
at jdk.proxy2/jdk.proxy2.$Proxy248.update(Unknown Source)
at org.springframework.batch.core.launch.support.SimpleJobOperator.stop(SimpleJobOperator.java:349)
at org.springframework.batch.core.launch.support.TaskExecutorJobOperator.stop(TaskExecutorJobOperator.java:139)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:565)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:158)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:133)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:371)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:130)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:222)
at jdk.proxy2/jdk.proxy2.$Proxy266.stop(Unknown Source)
at org.springframework.batch.core.launch.support.JobExecutionShutdownHook.run(JobExecutionShutdownHook.java:55)
Caused by: java.net.SocketException: Socket closed
at java.base/sun.nio.ch.NioSocketImpl.endRead(NioSocketImpl.java:242)
at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:321)
at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:354)
at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:798)
at java.base/java.net.Socket$SocketInputStream.implRead(Socket.java:974)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:964)
at org.postgresql.core.VisibleBufferedInputStream.readMore(VisibleBufferedInputStream.java:192)
at org.postgresql.core.VisibleBufferedInputStream.ensureBytes(VisibleBufferedInputStream.java:159)
at org.postgresql.core.VisibleBufferedInputStream.ensureBytes(VisibleBufferedInputStream.java:144)
at org.postgresql.core.VisibleBufferedInputStream.read(VisibleBufferedInputStream.java:76)
at org.postgresql.core.PGStream.receiveChar(PGStream.java:477)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2314)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:429)
... 32 common frames omitted
Exception in thread "Thread-12" java.lang.RuntimeException: Unable to gracefully stop job execution 228
at org.springframework.batch.core.launch.support.JobExecutionShutdownHook.run(JobExecutionShutdownHook.java:62)
Caused by: org.springframework.dao.DataAccessResourceFailureException: Hibernate transaction: Unable to commit against JDBC Connection; An I/O error occurred while sending to the backend.
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:152)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:102)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:111)
at org.springframework.orm.jpa.hibernate.HibernateExceptionTranslator.convertHibernateAccessException(HibernateExceptionTranslator.java:144)
at org.springframework.orm.jpa.hibernate.HibernateExceptionTranslator.convertHibernateAccessException(HibernateExceptionTranslator.java:131)
at org.springframework.orm.jpa.hibernate.HibernateExceptionTranslator.translateExceptionIfPossible(HibernateExceptionTranslator.java:105)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:557)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:794)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:757)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:687)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:408)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:130)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:222)
at jdk.proxy2/jdk.proxy2.$Proxy248.update(Unknown Source)
at org.springframework.batch.core.launch.support.SimpleJobOperator.stop(SimpleJobOperator.java:349)
at org.springframework.batch.core.launch.support.TaskExecutorJobOperator.stop(TaskExecutorJobOperator.java:139)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:565)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:158)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:133)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:371)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:130)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:222)
at jdk.proxy2/jdk.proxy2.$Proxy266.stop(Unknown Source)
at org.springframework.batch.core.launch.support.JobExecutionShutdownHook.run(JobExecutionShutdownHook.java:55)
Caused by: org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:456)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:387)
at org.postgresql.jdbc.PgConnection.executeTransactionCommand(PgConnection.java:998)
at org.postgresql.jdbc.PgConnection.commit(PgConnection.java:1020)
at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:378)
at com.zaxxer.hikari.pool.HikariProxyConnection.commit(HikariProxyConnection.java)
at org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.commit(AbstractLogicalConnectionImplementor.java:84)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commitNoRollbackOnly(JdbcResourceLocalTransactionCoordinatorImpl.java:249)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:242)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:89)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:553)
... 22 more
Caused by: java.net.SocketException: Socket closed
at java.base/sun.nio.ch.NioSocketImpl.endRead(NioSocketImpl.java:242)
at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:321)
at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:354)
at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:798)
at java.base/java.net.Socket$SocketInputStream.implRead(Socket.java:974)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:964)
at org.postgresql.core.VisibleBufferedInputStream.readMore(VisibleBufferedInputStream.java:192)
at org.postgresql.core.VisibleBufferedInputStream.ensureBytes(VisibleBufferedInputStream.java:159)
at org.postgresql.core.VisibleBufferedInputStream.ensureBytes(VisibleBufferedInputStream.java:144)
at org.postgresql.core.VisibleBufferedInputStream.read(VisibleBufferedInputStream.java:76)
at org.postgresql.core.PGStream.receiveChar(PGStream.java:477)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2314)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:429)
... 32 more
Expected behavior
Able to save task metadata and gracefully exit, no exception occurs.
Minimal Complete Reproducible example
Please provide a failing test or a minimal complete verifiable example that reproduces the issue.
Bug reports that are reproducible will take priority in resolution over reports that are not reproducible.