ebean: Datasource connection deadlock
Expected behavior
It is hoped that the new query caused by the previous query process should use the same jdbc connection.
Actual behavior
When the previous jdbc connection is not closed, hashcode triggers lazy loading and re-acquires a new jdbc connection, which may cause mutual exclusion of connections and deadlock in concurrent situations. For example, the maximum available connection pool is 2, when two threads trigger lazy loading at the same time, because the previous two connections are not released and get new connections through the connection pool, the connection pool finds that there is no available connection and suspends the thread to wait for other threads to release the connection, and both threads are stuck in getting new connections, resulting in deadlock.
Steps to reproduce
java.lang.RuntimeException
at test.system.Ebean$TConnection.invoke(Ebean.java:369)
at jdk.proxy2/jdk.proxy2.$Proxy161.getAutoCommit(Unknown Source)
at io.ebeaninternal.server.transaction.JdbcTransaction.checkAutoCommit(JdbcTransaction.java:282)
at io.ebeaninternal.server.transaction.JdbcTransaction.<init>(JdbcTransaction.java:223)
at io.ebeaninternal.server.transaction.TransactionManager.createTransaction(TransactionManager.java:397)
at io.ebeaninternal.server.transaction.TransactionFactoryBasic.create(TransactionFactoryBasic.java:55)
at io.ebeaninternal.server.transaction.TransactionFactoryBasic.createReadOnlyTransaction(TransactionFactoryBasic.java:29)
at io.ebeaninternal.server.transaction.TransactionManager.createReadOnlyTransaction(TransactionManager.java:390)
at io.ebeaninternal.server.core.DefaultServer.createReadOnlyTransaction(DefaultServer.java:2231)
at io.ebeaninternal.server.core.OrmQueryRequest.initTransIfRequired(OrmQueryRequest.java:278)
at io.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.java:1535)
at io.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.java:1515)
at io.ebeaninternal.server.core.DefaultBeanLoader.executeQuery(DefaultBeanLoader.java:180)
at io.ebeaninternal.server.core.DefaultBeanLoader.loadBean(DefaultBeanLoader.java:163)
at io.ebeaninternal.server.core.DefaultServer.loadBean(DefaultServer.java:561)
at io.ebeaninternal.server.loadcontext.DLoadBeanContext$LoadBuffer.loadBean(DLoadBeanContext.java:208)
at io.ebean.bean.EntityBeanIntercept.loadBeanInternal(EntityBeanIntercept.java:852)
at io.ebean.bean.EntityBeanIntercept.loadBean(EntityBeanIntercept.java:829)
at io.ebean.bean.EntityBeanIntercept.preGetter(EntityBeanIntercept.java:923)
at test.domain.entity.Root._ebean_get_reference(Root.java:6)
at test.domain.entity.Root.hashCode(Root.java:657)
at java.base/java.util.HashMap.hash(HashMap.java:338)
at java.base/java.util.HashMap.put(HashMap.java:610)
at java.base/java.util.HashSet.add(HashSet.java:221)
at io.ebean.common.BeanSet.internalAdd(BeanSet.java:84)
at io.ebean.common.BeanSet.internalAddWithCheck(BeanSet.java:74)
at io.ebeaninternal.server.deploy.BaseCollectionHelp.add(BaseCollectionHelp.java:39)
at io.ebeaninternal.server.deploy.BeanSetHelp.add(BeanSetHelp.java:19)
at io.ebeaninternal.server.deploy.BeanPropertyAssocMany.addBeanToCollectionWithCreate(BeanPropertyAssocMany.java:271)
at io.ebeaninternal.server.query.CQuery.setLazyLoadedChildBean(CQuery.java:417)
at io.ebeaninternal.server.query.SqlTreeNodeBean$Load.complete(SqlTreeNodeBean.java:421)
at io.ebeaninternal.server.query.SqlTreeNodeBean$Load.perform(SqlTreeNodeBean.java:447)
at io.ebeaninternal.server.query.SqlTreeNodeBean.load(SqlTreeNodeBean.java:464)
at io.ebeaninternal.server.query.SqlTreeNodeRoot.load(SqlTreeNodeRoot.java:45)
at io.ebeaninternal.server.query.CQuery.readNextBean(CQuery.java:447)
at io.ebeaninternal.server.query.CQuery.hasNext(CQuery.java:531)
at io.ebeaninternal.server.query.CQuery.readCollection(CQuery.java:564)
at io.ebeaninternal.server.query.CQueryEngine.findMany(CQueryEngine.java:379)
at io.ebeaninternal.server.query.DefaultOrmQueryEngine.findMany(DefaultOrmQueryEngine.java:131)
at io.ebeaninternal.server.core.OrmQueryRequest.findList(OrmQueryRequest.java:470)
at io.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.java:1536)
at io.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.java:1515)
at io.ebeaninternal.server.core.DefaultBeanLoader.executeQuery(DefaultBeanLoader.java:180)
at io.ebeaninternal.server.core.DefaultBeanLoader.loadMany(DefaultBeanLoader.java:45)
at io.ebeaninternal.server.core.DefaultServer.loadMany(DefaultServer.java:546)
at io.ebeaninternal.server.loadcontext.DLoadManyContext$LoadBuffer.loadMany(DLoadManyContext.java:228)
at io.ebean.common.AbstractBeanCollection.lazyLoadCollection(AbstractBeanCollection.java:101)
at io.ebean.common.BeanSet.init(BeanSet.java:137)
at io.ebean.common.BeanSet.iterator(BeanSet.java:283)
at java.base/java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1865)
at java.base/java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:414)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:508)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at test.domain.entity.Student.getCuDetail(Student.java:634)
at test.domain.entity.Student.getCuDetail(Student.java:627)
at test.dto.response.StudentRespDTO.<init>(StudentRespDTO.java:79)
at test.repository.StudentUnavailabilityTimeRepositoryTest.lambda$test$1(StudentUnavailabilityTimeRepositoryTest.java:145)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
About this issue
- Original URL
- State: closed
- Created 10 months ago
- Comments: 19 (9 by maintainers)
Commits related to this issue
- #3173 - BeanSet lazy loading / Too many DataSource connections used - The error reported was that a DataSource connection pool maxed out - The symptom was that there was an additional lazy loading qu... — committed to ebean-orm/ebean by rbygrave 10 months ago
- Merge pull request #3203 from ebean-orm/feature/3173-BeanSet #3173 - BeanSet lazy loading / Too many DataSource connections used — committed to ebean-orm/ebean by rbygrave 10 months ago
- Follow up #3173 - Add test for BeanSet lazy loading OneToMany with hashCode/equals — committed to ebean-orm/ebean by rbygrave 10 months ago
- Follow up #3173 - Add test for BeanSet clear() when hashCode/equals used This shows the extra lazy loading that is occurring when clear() is called on the [Bean]Set DUE to the hashcode/equals impleme... — committed to ebean-orm/ebean by rbygrave 10 months ago
- Merge pull request #3204 from ebean-orm/feature/3173-beanSet-clear Follow up #3173 - Add test for BeanSet clear() when hashCode/equals used — committed to ebean-orm/ebean by rbygrave 10 months ago
- Follow up #3173 - Change BeanSet clear() to lazy load ALL properties To avoid the 1+N lazy loading queries that are invoked due to the clear() when the entity bean in the Set has a hashCode/equals im... — committed to ebean-orm/ebean by rbygrave 10 months ago
- Merge pull request #3205 from ebean-orm/feature/3173-beanSet-clear Follow up #3173 - Change BeanSet clear() to lazy load ALL properties — committed to ebean-orm/ebean by rbygrave 10 months ago
- [12x] Backport of fix for #3173 BeanSet init(), initClear() and BeanMap - BeanSet init() and initClear() load with onlyIds false because the expectation is that with BeanSet the equals/hashCode imple... — committed to ebean-orm/ebean by rbygrave 10 months ago
- Merge pull request #3218 from ebean-orm/backport-12x/3173-BeanSet-init-loading [12x] Backport of fix for #3173 BeanSet init(), initClear() and BeanMap — committed to ebean-orm/ebean by rbygrave 10 months ago
ebean version
12.16.2has been released with the back ported patch https://github.com/ebean-orm/ebean/pull/3218Note: The branch
maintain-v12is the maintenance branch for 12.x releases going forward.Thank you, I have submitted the test code to this address: https://github.com/lbsoft-lwsoft/example-minimal/blob/test-case-3173/src/test/java/org/example/domain/ConnectionLeakTest.java
I think we posted at the same time there.
Yes, it looks like we can fix it in
OrmQueryRequestininitTransIfRequired()andendTransIfRequired()for the read-only case.