Spring application events lifecycle and earlyApplicationEvents

Not long ago I learned the hard way the way spring handles earlyApplication events when we started having problems in one of our projects

Basically we were using a MongoDB database as a cache layer, so when the application would startup we would pull most of the data from our RDBMS do some processing and denormalisation then fill the MongoDB with data

The application had been running mostly fine for some time but we saw some memory issues as the data kept growing within the application and I suspected a Memory leak issue

So I decided to do a java heap dump to try to see what was happening, and I found the culprit; way down the piping of spring the org.springframework.context.support.AbstractApplicationContext class had a private property earlyApplicationEvents that was keeping references to more than 3 million instances of BasicDBObject that were not Garbage collected even though they were no longer in scope and were already written to the database

After scouring the internet a fellow developer pointed out to me that the property : earlyApplicationEvents is :

basically holds onto events during startup until the listeners can be registered, at which point it will get set to null

So with this information I could simple push my heavy duty task later on the spring application events lifecycle by using spring's handy application events mechanism.

For those who don't know what application events are; application events are available since the very beginning of the Spring framework as a mean for loosely coupled components to exchange information; the main application events are :

  1. ContextRefreshedEvent : published when the ApplicationContext is either initialized or refreshed
  2. ContextStartedEvent : when the ApplicationContext is started using the start() method on the ConfigurableApplicationContext interface. You can poll your database or you can restart any stopped application after receiving this event
  3. ContextStoppedEvent : published when the ApplicationContext is stopped using the stop() method on the ConfigurableApplicationContext interface. You can do required housekeep work after receiving this event.
  4. ContextClosedEvent : published when the ApplicationContext is closed using the close() method on the ConfigurableApplicationContext interface. A closed context reaches its end of life; it cannot be refreshed or restarted.

in my case after the context has been refreshed like so :


@Configuration
public class MyConfigClass implements ApplicationListener<ContextRefreshedEvent>{


    //Misc code
    
    private final InitializationService initializationService;

    
    @Autowired
    public MyConfigClass(InitializationService initializationService){
       this. initializationService = initializationService;
    }

 @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        initializationService.init();
    }
}

So if I can leave you with a parting "word of wisdom" : Be careful WHEN you are running certain operations in respect to the Spring Application events lifecycle and the possible side-effects that this can cause

for more info on application events please read the spring official documentation

Working with stateless session in hibernate and jpa

When doing heavy read-only operations in with the database it can be a good idea to take advantage of using Hibernate's Stateless Session

As it name indicates this session does not keep state of the entities that it retrieves from the database and so it bypasses the L1 and L2 cache

This can have significant performance impact on heavy DBA read operations (since the state of the entities is not bet tracked)



@Component
@Scope("prototype")
public class SomeJpaService {

public static final int SCROLL_STEP = 1000;

     @PersistenceContext
     private EntityManager entityManager;


    
     public void doInStatelessSession(){

       LOGGER.info("ABOUT to perform Heavy read operation on DB");

       StatelessSession statelessSession = entityManager.unwrap(Session.class).getSessionFactory().openStatelessSession();

        try (ScrollableResults r = statelessSession
                .createQuery("SELECT c FROM Order c")
                .setReadOnly(true)
                .setCacheable(false)
                .scroll()) {// create the scrollable results with a try/close resources block

            while (r.scroll(SCROLL_STEP)) {
                 Order o = (Order) r.get(0);
                //perform some operation on object
              
            }
        }      



        LOGGER.info("FINISHED heavy read operation");

        statelessSession.close();
   }


}

There's however a few caveats with this approach :

  • Collections cannot be fetched on stateless sessions
  • Because of the stateless nature of the session this can only be used on read-only operations

docker compose on multiple environments

docker-compose is a pretty cool tool that allows you to bootstrap and run multiple docker containers with 1 configuration file Below is a...