Collection based multitenancy with Spring Data MongoDB
Asked Answered
M

1

5

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.

Macrae answered 26/5, 2016 at 11:38 Comment(0)
M
9

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.

Macrae answered 5/7, 2016 at 9:43 Comment(2)
Where to write this code ? Or Give location for write this code .Medamedal
If your tenants can be registered, this code can be in the registration process for example.Johnstone

© 2022 - 2024 — McMap. All rights reserved.