Spring sessions in a Separate Database

Storing session details in Redis or database is usually a good idea. However, the default implementation of spring-session-jdbc uses the primary data source to store and retrieve data from the session related tables. This can be a problem when there is a large amount of session related operations to the database.

To avoid these situations, it is a good idea to move the session related schema to a separate database. In this example we will see how we can use two separate databases, one for the application and one for storing sessions.

Before going further I would like you to take a look at Spring session using JDBC for a little background on how to implement JDBC session store.

The easiest way we can supply data source to the sessions related auto-configuration is by defining a DataSource bean with @SpringSessionDataSource annotation. At first, it may seem easy, but it is not. When you define your own DataSource bean, It will mess up the default autowiring. To safely provide a second data source, you need to follow these instructions.

Storing sessions in a different Database

First, create two sets of properties for each database. In my case, I’m using two separate in-memory h2 databases for the demo.

debug=true

# Properties for primary database
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=root
spring.datasource.password=root
spring.h2.console.enabled=true

# Properties for session database
session.datasource.url=jdbc:h2:mem:sessions
session.datasource.username=sessions
session.datasource.password=sessions

Then manually create both the data source using DataSourceProperties properties. The spring.datasource configurations will be used to create the primary data source. The session.datasource properties will be used for creating the session data source.

@Configuration
public class DataSourceConfig {
    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource")
    @Qualifier("dataSource")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    public DataSource primaryDataSource(@Qualifier("dataSource") DataSourceProperties dataSourceProperties) {
        return dataSourceProperties
                .initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
    }

    @Bean
    @ConfigurationProperties("session.datasource")
    @Qualifier("sessionDataSourceProperties")
    public DataSourceProperties sessionDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Qualifier("sessionDataSource")
    @SpringSessionDataSource
    public DataSource sessionDataSource(@Qualifier("sessionDataSourceProperties") DataSourceProperties sessionDataSourceProperties) {
        return sessionDataSourceProperties
                .initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
    }
}

In the above configuration class, notice how I marked sessionDataSource as @SpringSessionDataSource. Also, I have made sure both the data sources and their properties are distinguishable by their names using @Qualifier.

Initializing session schema

If you are using a database like Oracle or MySQL, You are already done. Create the session related tables (SPRING_SESSION and SPRING_SESSION_ATTRIBUTES) in the database pointed by sessionDataSource and you are good to go. But if you want to use an embedded data source like in this demo or if you set the spring.session.jdbc.initialize-schema to always, then you have a problem.

The default auto-configuration does not know which dataSource to autowire for JdbcSessionDataSourceInitializer. So it may end up creating session tables in the primary data source. To avoid this you need to provide your own JdbcSessionDataSourceInitializer bean as shown below.

@Configuration
@EnableConfigurationProperties(JdbcSessionProperties.class)
public class JdbcSessionConfig {
    @Bean
    public JdbcSessionDataSourceInitializer
    jdbcSessionDataSourceInitializer(@Qualifier("sessionDataSource") DataSource sessionDataSource,
                                     ResourceLoader resourceLoader,
                                     JdbcSessionProperties properties) {
        return new JdbcSessionDataSourceInitializer(sessionDataSource, resourceLoader, properties);
    }
}

In the above configuration note how I have used @Qualifier to load the sessionDataSource. Also, note that you need to EnableConfigurationProperties for autowiring JdbcSessionProperties.

If we do all of this right, You can see that the session database having two tables, and the primary database will be used by the application.

You can find this example in this GitHub Repository. Feel free to take a look.

Leave a Comment