Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collection based multitenancy with Spring Data MongoDB

Our Spring Boot 1.3.3 application persists data on MongoDB (2.6 ou 3.2) using Spring Data MongoDB 1.8.4.

We need to support multitenancy. We chose to use "collection based" multitenancy, i.e. each tenant has its own set of collection. For example for the Article entity, the collections are "{tenantName}_articles".

Oliver Gierke kindly explained an implementation in Making spring-data-mongodb multi-tenant using for example :

@Document(collectionName = "#{tenantProvider.getTenantId()}_articles")

This is very nice on paper, but does not seem applicable for real life applications as I found two issues, one being major:

Issue 1 (I could live with that): at application startup Spring Boot makes the database build the indexes for entities that have custom indexes (such as @Indexed attributes). But at startup, there is no "current tenant" so Spring Data creates irrelevant collections such as "_articles". How can we prevent this?

Issue 2 (major probleme here): at runtime the multitenant collections such as "{tenantName}_articles" are created and used without the custom indexes (apart from the default MongoDB index on "_id"). I suspect Spring ignores indexes at runtime because it thinks it already did the job at startup. This is a major performance problem. How can we fix this?

Thank you for your time.

like image 320
Florian Beaufumé Avatar asked May 26 '16 11:05

Florian Beaufumé


1 Answers

Found a way to recreate the indexes for a given tenant:

String tenantName = ...;

MongoMappingContext mappingContext = (MongoMappingContext) mongoTemplate.getConverter().getMappingContext();
MongoPersistentEntityIndexResolver resolver = new MongoPersistentEntityIndexResolver(mappingContext);

for (BasicMongoPersistentEntity entity : mappingContext.getPersistentEntities()) {
    if (entity.findAnnotation(Document.class) == null) {
        // Keep only collection roots
        continue;
    }

    String collectionName = entity.getCollection();
    if (!collectionName.startsWith(tenantName)) {
        // Keep only dynamic entities
        continue;
    }

    IndexOperations indexOperations = mongoTemplate.indexOps(collectionName);
    for (MongoPersistentEntityIndexResolver.IndexDefinitionHolder holder : resolver.resolveIndexForEntity(entity)) {
        indexOperations.ensureIndex(holder.getIndexDefinition());
    }
}

Took me some time to figure this out. Hope this will help. Improvements welcome.

like image 110
Florian Beaufumé Avatar answered Nov 15 '22 05:11

Florian Beaufumé