Multi Tenant Databases
This is a farily age-old problem faced by most Spring-Boot applications. The default functionality associated with the
spring-boot-starter-jdbc and associated RDBMS related
spring-data modules auto configures a single datasource.
The Spring Framework provides the AbstractRoutingDataSource which allows configuration of one or more
javax.sql.DataSourceinstances that are keyed on an identifier. The implementation of the method
AbstractRoutingDataSource.determineCurrentLookupKey() will provide a result of the key to retrieve the tentant
To provide a consistent schema, and upgrade path as the application evolves, the package Flyway can be used to consistently apply database migrations and track the changes within the database.
This article Multitenancy Applications with Spring Boot and Flyway provides a rock solid foundation. There are, however, a few discrepancies in the article itself that make the described implementation unworkable. However, the provided GitHub repository has a proper implementation that does work.
What the exemplar implementation provides are the following:
- A method for configuring multiple datasources
- A method for applying Flyway migrations on each datasource
- A method for resolving the tenant database to use based on HTTP header associated with the request
Thoughts on Migration Application
In a production environment, assuming this is a containerized application, it would be ideal to provide rolling updates to the application.
Adding a new Tenant
To add a new tentant, the database to contain the tenant would need to be established by an administrator. Subsequently, the configuration for the application would then be updated with the appropriate connection string and credentials, then the application containers could be restarted in a rolling fashion.
The exemplar article assumes credentials of the database connection have permission to modify the database schema. In a production environment, this would not be recommended.
With this in mind, the code base could be extended to run the application in a fashion that would allow the database to be accessed and allow for Flyway to apply migrations but not remain resident. Then the application can be started normally utilizing a RW connection for data but not schema modification. A method of achieving this would be utilizing Spring Bean Definition Profiles.
Deployed in a framework like Kubernetes, an
InitContainer could be declared that would run the application, as described above, in a mode where it will apply the Flyway migrations, then the regular operating application would be declared as a
Container of the deployment. The semantics of a Kubernetes
Deployment require that all declared
InitContainer elements run to completion before any
Container instances are started.
Configuration data could be externalized via a
ConfigMap providing a much broader mechanism of providing configuration to the application.