Skip to content

Deadlock on JPA EntityListener instantiation #22997

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
hgarus opened this issue Aug 18, 2020 · 8 comments
Closed

Deadlock on JPA EntityListener instantiation #22997

hgarus opened this issue Aug 18, 2020 · 8 comments
Labels
for: external-project For an external project and not something we can fix status: duplicate A duplicate of another issue

Comments

@hgarus
Copy link

hgarus commented Aug 18, 2020

I've tried to build a minimal reproduction of the deadlock issue I mentioned in #16230, this is what I've come up with so far.

Using spring-boot-parent 2.3.3, spring-boot-starter-jpa and h2 this reliably deadlocks on startup on my machine:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.stereotype.Component;

import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.Id;
import javax.persistence.PrePersist;

@SpringBootApplication
@EnableJpaAuditing
public class DemoApplication {


	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@Component
	public static class MyBean { }

}


@Entity
@EntityListeners({MyEntityListener.class})
class MyEntity {

	@Id
	private Long id;
}

class MyEntityListener {

	private final DemoApplication.MyBean myBean;

	// It's my understanding that constructor injection on entity listeners is supported, even though it's not
	// JPA compliant. Changing this to setter Injection still results in a deadlock
	public MyEntityListener(DemoApplication.MyBean myBean) {
		this.myBean = myBean;
	}

	@PrePersist
	public void prePersist(MyEntity myEntity) {
		// needs to have one annotated method otherwise it's ignored
	}
}

Suspending the thread with a break point (ie in AbstractEntityManagerFactoryBean.buildNativeEntityManager()) until the main thread is waiting on the Future should always produce it.

In my actual Service I have an EntityListener with an injected dependency as well. Wrapping the dependency in an ObjectFactory seems to remove the Deadlock there as well.

Thread Dump:

"main@1" prio=5 tid=0x1 nid=NA waiting
  java.lang.Thread.State: WAITING
	 blocks task-1@4636
	  at jdk.internal.misc.Unsafe.park(Unsafe.java:-1)
	  at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
	  at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:447)
	  at java.util.concurrent.FutureTask.get(FutureTask.java:190)
	  at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.getNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:540)
	  at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.invokeProxyMethod(AbstractEntityManagerFactoryBean.java:497)
	  at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean$ManagedEntityManagerFactoryInvocationHandler.invoke(AbstractEntityManagerFactoryBean.java:680)
	  at com.sun.proxy.$Proxy55.getMetamodel(Unknown Source:-1)
	  at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean$$Lambda$424.1048842522.apply(Unknown Source:-1)
	  at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
	  at java.util.Iterator.forEachRemaining(Iterator.java:133)
	  at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
	  at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	  at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	  at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
	  at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	  at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
	  at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.getMetamodels(JpaMetamodelMappingContextFactoryBean.java:106)
	  at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:80)
	  at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:44)
	  at org.springframework.beans.factory.config.AbstractFactoryBean.afterPropertiesSet(AbstractFactoryBean.java:142)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1790)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
	  at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$227.873827336.getObject(Unknown Source:-1)
	  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
	  - locked <0x16d7> (a java.util.concurrent.ConcurrentHashMap)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	  at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
	  at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
	  at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:690)
	  at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:196)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1356)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1203)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
	  at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$227.873827336.getObject(Unknown Source:-1)
	  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	  at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897)
	  at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879)
	  at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
	  - locked <0x16d8> (a java.lang.Object)
	  at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
	  at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
	  at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
	  at com.example.demo.DemoApplication.main(DemoApplication.java:19)

"task-1@4636" prio=5 tid=0x19 nid=NA waiting for monitor entry
  java.lang.Thread.State: BLOCKED
	 waiting for main@1 to release lock on <0x16d7> (a java.util.concurrent.ConcurrentHashMap)
	  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:183)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:523)
	  at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:530)
	  at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:503)
	  at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:265)
	  at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1473)
	  at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1270)
	  at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227)
	  at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:884)
	  at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:788)
	  at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:227)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1356)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1203)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:353)
	  at org.springframework.orm.hibernate5.SpringBeanContainer.createBean(SpringBeanContainer.java:147)
	  at org.springframework.orm.hibernate5.SpringBeanContainer.getBean(SpringBeanContainer.java:105)
	  at org.hibernate.resource.beans.internal.ManagedBeanRegistryImpl.getBean(ManagedBeanRegistryImpl.java:61)
	  at org.hibernate.jpa.event.internal.CallbackBuilderLegacyImpl.resolveEntityCallbacks(CallbackBuilderLegacyImpl.java:200)
	  at org.hibernate.jpa.event.internal.CallbackBuilderLegacyImpl.buildCallbacksForEntity(CallbackBuilderLegacyImpl.java:74)
	  at org.hibernate.event.service.internal.EventListenerRegistryImpl.prepare(EventListenerRegistryImpl.java:146)
	  at org.hibernate.boot.internal.MetadataImpl.initSessionFactory(MetadataImpl.java:379)
	  at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:213)
	  at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:469)
	  at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1259)
	  at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
	  at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
	  at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:391)
	  at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean$$Lambda$423.69160933.call(Unknown Source:-1)
	  at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
	  at java.util.concurrent.FutureTask.run(FutureTask.java:-1)
	  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
	  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
	  at java.lang.Thread.run(Thread.java:832)
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Aug 18, 2020
@snicoll
Copy link
Member

snicoll commented Aug 18, 2020

Thank you for the report. As I was indicated in the related issue, can you please turn that code in text into an actual project we can run?

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label Aug 18, 2020
@hgarus
Copy link
Author

hgarus commented Aug 18, 2020

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Aug 18, 2020
@knoobie
Copy link

knoobie commented Aug 18, 2020

Could this be related to spring-projects/spring-framework#25111?

@snicoll
Copy link
Member

snicoll commented Aug 19, 2020

Sounds about right @knoobie, thank you.

@hgarus thank you for sharing a sample we can run. I'll add a note to the related issue to try out your sample once we have a fix for the framework issue.

@snicoll snicoll closed this as completed Aug 19, 2020
@snicoll snicoll added for: external-project For an external project and not something we can fix status: duplicate A duplicate of another issue and removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged labels Aug 19, 2020
@steklopod

This comment has been minimized.

@dc-up42
Copy link

dc-up42 commented May 18, 2021

Have this problem on 2.4.0.

@snicoll
Copy link
Member

snicoll commented May 19, 2021

@dc-up42 this issue is closed. If you believe you found a bug in Spring Boot, please create a separate issue. Before doing so, we'll need a small sample that reproduces the problem. You can do that by attaching a zip to the issue or sharing a link to a GitHub repository. Thank you.

@girtsivans
Copy link

girtsivans commented Feb 4, 2022

@hgarus You can add Lazy annotation

public MyEntityListener(@Lazy DemoApplication.MyBean myBean) {
	this.myBean = myBean;
}

Found this solution here

humaolin pushed a commit to humaolin/spring-boot that referenced this issue May 7, 2022
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
for: external-project For an external project and not something we can fix status: duplicate A duplicate of another issue
Projects
None yet
Development

No branches or pull requests

7 participants