Multi-Tenancy

in Java applications



@ladislavGazo
gazo@seges.sk

The application


user

|

bunch of JavaScript

|

service

|

domain model

Implication


user1 ... userN

|

same bunch of JavaScript

|

service

|

ONE database

One application to rule them all


The term multi-tenancy in general is applied to software development to indicate an architecture in which a single running instance of an application simultaneously serves multiple clients (tenants). 

This is highly common in SaaS solutions. 

Isolating information (data, customizations, etc) pertaining to the various tenants is a particular challenge in these systems.

Split me now!


user1 -> BA, user2 -> BA, user 3 -> KE, user 4 -> ZA

|

again the same JavaScript

|

(almost) the same service

|

DB (BA), DB (KE), DB (ZA)



  • a lot of "tenant" dependent data - high volumes
  • performance
  • separation -> easier backup and restore
  • scalability of front-end and service layer





Hibernate is the easiest


Multi-tenant configuration

<property name="hibernate.multiTenancy" value="DATABASE" />

<property name="hibernate.tenant_identifier_resolver" value="sk.seges.hroddelenie.configuration.MultiTenantIdentifierResolver" />
<property name="hibernate.multi_tenant_connection_provider" value="org.hibernate.service.jdbc.connections.spi.DataSourceBasedMultiTenantConnectionProviderImpl" />

<property name="hibernate.connection.datasource" value="java:comp/env/jdbc/hr" />

<property name="hibernate.multi_tenant.datasource.identifier_for_any" value="default" />

Switch it ON







hibernate.multiTenancy

Configure where to look for the current tenant identifier





hibernate.tenant_identifier_resolver

Where are my connections?





hibernate.multi_tenant_connection_provider




Hibernate's DataSourceBasedMultiTenantConnectionProviderImpl utilizes JNDI lookups

Point to JNDI root for datasources






hibernate.connection.datasource

... and the default






hibernate.multi_tenant.datasource.identifier_for_any





So in the end = java:comp/env/jdbc/hr/default

But there are things that don't work


  • EHCache configuration from previous version is different
  • Hibernate's internal schema update does not work
    • NPE !!! ........ who would have said that





Migration




Hibernate exporter

is not helpful




Do you have Envers?


then you need custom exporter

It is easy to write


but don't forget the AuditConfiguration


Ejb3Configuration jpaConfiguration = new Ejb3Configuration().configure(				unitName, null);
Configuration hibernateConfiguration = jpaConfiguration
				.getHibernateConfiguration();
		
hibernateConfiguration.buildMappings();
AuditConfiguration auditConfiguration = AuditConfiguration.getFor(hibernateConfiguration);

and use EnversSchemaGenerator at last

Get rid of NPE bug





 { "hibernate.multiTenancy",
   "hibernate.tenant_identifier_resolver",
   "hibernate.multi_tenant_connection_provider",
   "hibernate.connection.datasource",
   "hibernate.multi_tenant.datasource.identifier_for_any" }





Run the exporter from Maven

Combine it with Liquibase



version 3.0.6 contains feature to populate all tenant databases

                 MultiTenantSpringLiquibase



Don't forget...


it is maintenable but generated SQL needs to be checked


existing databases must be moved to "point 0"

A bit of logging

Importance of logging - using MDC to track requests

                           MDC.put("tenant", clientOrgz); 

 log4j.appender.stdout.layout.ConversionPattern = APP [%d{ISO8601}] - %-5p [%t|%X{tenant}|%X{requestId}] %F:%M %-30.30c{1} - %m%n

And the most boring...





Design


your app to be multi-tenant

Send tenant identification

  • in HTTP Header
    • use Filter to get it out
  • in Service parameters
  • using ThreadLocal variable
  • via tenant-URL-based service calls
    • rmi://localhost:12345/<tenant>/<service>
  • etc...

Thanks for listening


@ladislavGazo
gazo@seges.sk

Multi-tenant applications in Java

By lgazo

Multi-tenant applications in Java

  • 1,327