Spring Boot JPA; JpaRepository return Stream<T>; logAbandoned: true shows that Connection is abandoned.
See original GitHub issueI did a small proof of concept with Spring Boot JPA, that has JpaRepository which has one custom method, that returns a Stream of entities. I turned on a bit of logging and currently it seems that though the Stream reading works well, then the database connection is abandoned. That concerns me a bit. Should I be worried and is this maybe a leakage?
application.yml:
spring:
datasource:
removeAbandonedTimeout: 30
remove-abandoned: true
log-abandoned: true
Code:
package my.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.stream.Stream;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@SpringBootApplication
@EnableScheduling
public class StreamErrorAppRunner {
public static void main(String[] args) {
SpringApplication.run(StreamErrorAppRunner.class);
}
@Bean
public CommandLineRunner demo(TestEntityRepository testEntityRepository) {
return (args) -> {
testEntityRepository.save(new TestEntity("test_one"));
testEntityRepository.save(new TestEntity("test_two"));
testEntityRepository.save(new TestEntity("test_three"));
testEntityRepository.save(new TestEntity("test_four"));
testEntityRepository.save(new TestEntity("test_five"));
testEntityRepository.save(new TestEntity("test_six"));
testEntityRepository.save(new TestEntity("test_seven"));
};
}
}
@Component
class TestEntityLogger {
private static final Logger logger = LoggerFactory.getLogger(TestEntityLogger.class);
private final TestEntityRepository testEntityRepository;
@Autowired
TestEntityLogger(TestEntityRepository testEntityRepository) {
this.testEntityRepository = testEntityRepository;
}
@Scheduled(fixedDelay = 60000, initialDelay = 5000)
public void logAllTestEntities() {
try (Stream<TestEntity> entities = testEntityRepository.findEntities()) {
entities.forEach(e -> logger.info(e.toString()));
}
logger.info("Finished reading the stream. It should be closed by now.");
}
}
@Entity
class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
public TestEntity() {
}
public TestEntity(String name) {
this.name = name;
}
@Override
public String toString() {
return "TestEntity{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
interface TestEntityRepository extends JpaRepository<TestEntity, Long> {
@Query("select t from TestEntity t")
Stream<TestEntity> findEntities();
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>sb-jpa-stream-bug-poc</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<groupId>my.test.app</groupId>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.192</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.3.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Relevant logs:
2016-06-20 10:46:18.115 INFO 10436 --- [ main] my.test.StreamErrorAppRunner : Started StreamErrorAppRunner in 2.401 seconds (JVM running for 2.683)
2016-06-20 10:46:23.090 INFO 10436 --- [pool-2-thread-1] my.test.TestEntityLogger : TestEntity{id=1, name='test_one'}
2016-06-20 10:46:23.090 INFO 10436 --- [pool-2-thread-1] my.test.TestEntityLogger : TestEntity{id=2, name='test_two'}
2016-06-20 10:46:23.090 INFO 10436 --- [pool-2-thread-1] my.test.TestEntityLogger : TestEntity{id=3, name='test_three'}
2016-06-20 10:46:23.091 INFO 10436 --- [pool-2-thread-1] my.test.TestEntityLogger : TestEntity{id=4, name='test_four'}
2016-06-20 10:46:23.091 INFO 10436 --- [pool-2-thread-1] my.test.TestEntityLogger : TestEntity{id=5, name='test_five'}
2016-06-20 10:46:23.091 INFO 10436 --- [pool-2-thread-1] my.test.TestEntityLogger : TestEntity{id=6, name='test_six'}
2016-06-20 10:46:23.091 INFO 10436 --- [pool-2-thread-1] my.test.TestEntityLogger : TestEntity{id=7, name='test_seven'}
2016-06-20 10:46:23.092 INFO 10436 --- [pool-2-thread-1] my.test.TestEntityLogger : Finished reading the stream. It should be closed by now.
2016-06-20 10:47:01.998 WARN 10436 --- [:1466408776991]] o.a.tomcat.jdbc.pool.ConnectionPool : Connection has been abandoned PooledConnection[conn9: url=jdbc:h2:mem:testdb user=SA]:java.lang.Exception
at org.apache.tomcat.jdbc.pool.ConnectionPool.getThreadDump(ConnectionPool.java:1061)
at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:777)
at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:626)
at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:185)
at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:127)
at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:139)
at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:380)
at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:228)
at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:171)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:63)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:162)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:186)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:160)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1885)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1862)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1839)
at org.hibernate.loader.Loader.scroll(Loader.java:2627)
at org.hibernate.loader.hql.QueryLoader.scroll(QueryLoader.java:561)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.scroll(QueryTranslatorImpl.java:439)
at org.hibernate.engine.query.spi.HQLQueryPlan.performScroll(HQLQueryPlan.java:355)
at org.hibernate.internal.SessionImpl.scroll(SessionImpl.java:1381)
at org.hibernate.internal.QueryImpl.scroll(QueryImpl.java:91)
at org.springframework.data.jpa.provider.PersistenceProvider$HibernateScrollableResultsIterator.<init>(PersistenceProvider.java:414)
at org.springframework.data.jpa.provider.PersistenceProvider$1.executeQueryWithResultStream(PersistenceProvider.java:118)
at org.springframework.data.jpa.repository.query.JpaQueryExecution$StreamExecution.doExecute(JpaQueryExecution.java:323)
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:78)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:100)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:91)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:462)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:440)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:131)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at my.test.$Proxy55.findEntities(Unknown Source)
at my.test.TestEntityLogger.logAllTestEntities(StreamErrorAppRunner.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Issue Analytics
- State:
- Created 7 years ago
- Reactions:1
- Comments:6 (1 by maintainers)
Top Results From Across the Web
Spring JpaRepository findAll, Java 8 Stream, Connection has ...
Just this nasty abandoned connection error that I do not like in my logs. This is just a minimal example. And if you...
Read more >Spring Data JPA - Reference Documentation
Spring Data JPA provides repository support for the Jakarta Persistence API (JPA). It eases development of applications that need to access ...
Read more >Connection Pooling and Data Sources - Developer
Advantages of this approach: Uses Tomcat JDBC connection pooling; Logging: Server will log and close abandoned connections; Helps identify application design ...
Read more >Spring Data Java 8 Support - Baeldung
Spring Data now supports core Java 8 features – such as Optional, Stream API and CompletableFuture. In this quick article, we'll go through ......
Read more >Spring Boot: Detecting Connection Leak - YouTube
In this video we will see how to detect connection leak in a Spring Boot application using HikariCP, the default database connection pool ......
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@TarmoKalling since there is no JIRA issue yet I’m replying here.
if you add
@Transactional(readOnly = true)
to thelogAllTestEntities()
method then the connection should be released properly - according to a quick test.cc @olivergierke FYI
Cheers, Thomas
@TarmoKalling if you can use Hibernate 5.2, here is a workaround for you 😉 (tested with spring-boot 1.4.1 and hibernate 5.2.3.Final )
@Transactional(readOnly = true)
annotation needs to be on the method that process the Stream, on in your case on thelogAllTestEntities
Method, otherwise it closes the stream after method call.I run this on H2 & Oracle DB with 500K records, works perfect.