Skip to content

Publishing MongoMappingEvent invokes unrelated event listeners [DATAMONGO-2129] #2998

Open
@spring-projects-issues

Description

@spring-projects-issues

lnevaril opened DATAMONGO-2129 and commented

Framework spring-data-mongodb provides possibility to register own listeners for the lifecycle events through extending generic AbstractMongoEventListener<T>. All lifecycle events are processed by SimpleApplicationEventMulticaster and corresponding listeners are invoked. For each lifecycle event all listeners extending AbstractMongoEventListener are invoked even though the generic type is different and the filtering is done in AbstractMongoEventListener instances method onApplicationEvent but it could be done in the higher level in the event multicaster through existing facility. This fact has significant performance impact on the database queries (in our application by order of magnitude for findAll operations). Spring framework offers possibility to determine the type of generic events correctly by implementing ResolvableTypeProvider, for example org.springframework.context.PayloadApplicationEvent implements it. Is there any reason to not to implement the ResolvableTypeProvider interface in the MongoMappingEvent to reduce the number of invoked listeners for all lifecycle events only to the desired ones? For example during the findAll operation on collection it would reduce the overhead of calling all listeners for each collection's object.

 

Consider following example:

 
There are three simple classes User, Category and Item and for each domain object there is a repository. Moreover, there are AbstractMongoEventListeners for each domain object.
 

@Component
public class UserMongoEventListener extends AbstractMongoEventListener<User> {

   @Override
   public void onAfterLoad(AfterLoadEvent<User> event) {
      super.onAfterLoad(event);
   }

   @Override
   public void onAfterConvert(AfterConvertEvent<User> event) {
      super.onAfterConvert(event);
   }

   @Override
   public void onBeforeConvert(BeforeConvertEvent<User> event) {
      super.onBeforeConvert(event);
   }

}

 

@Component
public class CategoryMongoEventListener extends AbstractMongoEventListener<Category> {

   @Override
   public void onAfterLoad(AfterLoadEvent<Category> event) {
      super.onAfterLoad(event);
   }

   @Override
   public void onAfterConvert(AfterConvertEvent<Category> event) {
      super.onAfterConvert(event);
   }

   @Override
   public void onBeforeConvert(BeforeConvertEvent<Category> event) {
      super.onBeforeConvert(event);
   }

}

  

@Component
public class ItemMongoEventListener extends AbstractMongoEventListener<Item> {

   @Override
   public void onAfterLoad(AfterLoadEvent<Item> event) {
      super.onAfterLoad(event);
   }

   @Override
   public void onAfterConvert(AfterConvertEvent<Item> event) {
      super.onAfterConvert(event);
   }

   @Override
   public void onBeforeConvert(BeforeConvertEvent<Item> event) {
      super.onBeforeConvert(event);
   }

}

 

Consider there are 1000 Item objects in the database and findAll method is called on this collection. Events AfterLoadEvent and AfterConvertEvent are published for each Item document.

 
By default event multicasting is done by SimpleApplicationEventMulticaster. ResolvableType of MongoMappingEvent is org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent<?>, therefore, getApplicationListeners(event, type) returns all defined AbstractMongoEventListener instances (in fact it returns all listeners registered for Spring's ApplicationEvent).
 

org.springframework.context.event.SimpleApplicationEventMulticaster


@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      Executor executor = getTaskExecutor();
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         invokeListener(listener, event);
      }
   }
}

  

This would result in 4000 unnecessary calls to listeners defined for different domain type since ResolvableType of MongoMappingEvent's does not provide information of the exact generic parameter type.

 

Is there any reason for this behavior that I do not understand? Possibly I could contribute with the pull request to extend MongoMappingEvent with the ResolvableTypeProvider.

 

Thank you


No further details from DATAMONGO-2129

Metadata

Metadata

Assignees

Labels

in: mappingMapping and conversion infrastructuretype: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions