Search code examples
springsessiontomcatspring-boot

Spring boot embedded tomcat application session does not invalidate


Recently we ported our application from the web application running in tomcat to spring boot application with embedded tomcat.

After running the app for several days, memory and cpu usage have reached 100%. In heap dump analysis it comes out that there was a bunch of http session objects which where not destroyed.

I can see in the debug that sessions created with configured timeout value, lets say, 5 minutes. But after this time the invalidation is not triggered. It invoked only if I do request again after the timeout period.

I have compared this behavior with app running in tomcat and I can see that session invalidation is triggered by ContainerBackgroungProcessor thread [StandardManager(ManagerBase).processExpires()]

I do not see this background thread in spring boot application.

What was done following some suggestions found:

  1. session timeout set in application.properties: server.session.timout=300 or in EmbeddedServletContainerCustomizer @Bean: factory.setSessionTimout(5, TimeUnit.MINUTES)

  2. Added HttpSessionEventPublisher and SessionRegistry beans

Nothing helps, sessions just not invalidated at the expiration time.

Some clue about this?


Solution

  • After some more debugging and documentation reading this is the reason and solution:

    In tomcat, there is a thread spawned on behalf of the root container which scans periodically container and its child containers session pools and invalidates them. Each container/child container may be configured to have its own background processor to do the job or to rely on its host's background processor. This controlled by context.backgroundProcessorDelay

    Apache Tomcat 8 Configuration Reference

    backgroundProcessorDelay -
    This value represents the delay in seconds between the invocation of the backgroundProcess method on this engine and its child containers, including all hosts and contexts. Child containers will not be invoked if their delay value is not negative (which would mean they are using their own processing thread). Setting this to a positive value will cause a thread to be spawn. After waiting the specified amount of time, the thread will invoke the backgroundProcess method on this engine and all its child containers. If not specified, the default value for this attribute is 10, which represent a 10 seconds delay.

    In spring boot application with embedded tomcat there is TomcatEmbeddedServletContainerFactory.configureEngine() which sets this property -1 for the StandardEngine[Tomcat], which is the root container in tomcat hierarchy, as I understand. All the child containers including web app also have this parameter set to -1. And this means they all rely on someone else to do the job. Spring do not do it, no-one do it.

    The solution for me was to set this parameter for the app context:

    @Bean
    public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
        return new EmbeddedServletContainerCustomizer() {
    
            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {
                if (container instanceof TomcatEmbeddedServletContainerFactory) {
                    TomcatEmbeddedServletContainerFactory factory = (TomcatEmbeddedServletContainerFactory) container;
                    TomcatContextCustomizer contextCustomizer = new TomcatContextCustomizer() {
                        
                        @Override
                        public void customize(Context context) {
                            context.setBackgroundProcessorDelay(10);
                        }
                    };
                    List<TomcatContextCustomizer> contextCustomizers = new ArrayList<TomcatContextCustomizer>();
                    contextCustomizers.add(contextCustomizer);
                    factory.setTomcatContextCustomizers(contextCustomizers);
                    customizeTomcat(factory);
                }
            }