Uploaded image for project: 'Spring Data Commons'
  1. Spring Data Commons
  2. DATACMNS-901

Potential deadlock in AbstractMappingContext when creating bean while application event is being fired

    XMLWordPrintable

    Details

      Description

      A deadlock occurs in my application while one thread performs a query on a mongo db, causing an application event to be fired, while another thread attempts to create a bean with an AutowireCapableBeanFactory.

      This happens in the constructor of a @Service and prevents the startup of the entire application as the service is created on startup.

      Pseudocode:

      @Service
      public class MyService {
      
          @Autowired
          public MyService(AutowireCapableBeanFactory factory, TaskScheduler scheduler) {
              MyFirstCommand command1 = factory.getBean(MyFirstCommand.class);
              scheduler.schedule(command1, new Date());
      
              MySecondCommand command2 = factory.getBean(MySecondCommand.class);
              scheduler.schedule(command2, new Date());
          }
      }
      
      public class MyFirstCommand extends Runnable {
      
          @Autowired MongoOperations mongo;
      
          @Override
          public void run() {
             mongo.findById("some-id", MyData.class); 
          }
      }
      

      Note that i have some AbstractMongoEventListeners registered as @Components.

      The deadlock occurs while one thread in MyFirstCommand.run() performs mongo.findById(). The other thread is in the constructor of MyService performing factory.getBean(MySecondCommand.class):

      Java stack information for the threads listed above:
      ===================================================
      "pool-1-thread-1":
      at org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners(AbstractApplicationEventMulticaster.java:179)
      - waiting to lock <0x00000000816d0970> (a java.util.concurrent.ConcurrentHashMap)
      at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
      at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:381)
      at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:335)
      at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:326)
      at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:179)
      at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:139)
      at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:66)
      at org.springframework.data.mongodb.core.MongoTemplate.determineCollectionName(MongoTemplate.java:1976)
      at org.springframework.data.mongodb.core.MongoTemplate.findById(MongoTemplate.java:607)
      
      
      "main":
      at sun.misc.Unsafe.park(Native Method)
      - parking to wait for  <0x0000000081ed8468> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
      at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared(AbstractQueuedSynchronizer.java:967)
      at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(AbstractQueuedSynchronizer.java:1283)
      at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.lock(ReentrantReadWriteLock.java:727)
      at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:160)
      at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:139)
      at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:66)
      at org.springframework.data.mongodb.repository.support.MongoRepositoryFactory.getEntityInformation(MongoRepositoryFactory.java:118)
      at org.springframework.data.mongodb.repository.support.MongoRepositoryFactory.getTargetRepository(MongoRepositoryFactory.java:92)
      at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:185)
      at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:251)
      at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:237)
      at org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean.afterPropertiesSet(MongoRepositoryFactoryBean.java:108)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) 
      

      The application has changed recently so that creating and scheduling the commands now happens in the constructor of MyService. Before, they were triggered in a public method of MyService which was called by a @PostConstruct _ method of the _@Configuration class. I didn't get the deadlock then. Even now, i get the deadlock on one host every single time, on another host, i haven't encountered it.

      This might be a very dangerous deadlock if it could occur anytime one thread fires an application event while another creates a bean. I can work around this for now by first creating all runnable beans before scheduling them, but there is really no way for me to prevent these two things happening simultaneously.

      This was first posted on StackOverflow.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              olivergierke Oliver Drotbohm
              Reporter:
              johannes.dorn@codetrails.com Johannes Dorn
              Last updater:
              Mark Paluch
              Votes:
              2 Vote for this issue
              Watchers:
              5 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: