Multi Tenancy

Hello!

Is there a way to do Database Selection via configuring the new SDN/RX spring driver? I cannot find a property to facilitate this.

Presently the only way I can to perform database selection is to build a SessionConfig and pass that to the Driver.session(config). This solution will not work for Spring Repositories where the Driver and Session creation is handled behind Spring's Repository facade.

AND of course the real goal is to have something like a SessionConfigFactory bean which will produce a SessionConfiguration that points to the database that serves the current request's tenant.

Not there yet… As that questions comes up these days, we have created a ticket for it https://github.com/neo4j/sdn-rx/issues/124 We haven't settled on a final approach yet. Maybe a simple, static configuration option like we have introduced in OGM (cc @mike.blum_neo4j) would do for starters, but I have the feeling we will need to have a dynamic option as well.

For the time being, if you don't need the mapping, it's already prepared on the client. It does this on an adhoc base: https://github.com/neo4j/sdn-rx/blob/master/docs/neo4j-client.adoc#selecting-the-target-database

I think the "static" way its working the the latest OGM would be a good starting point for code bases that have historically relied on repositories. This is somewhat tangential but with the stream-like nature of the new SDN non/reactive clients it seems like the mapping could be done in the same stream as the session such that in a service method we:

  1. Open a session
  2. select the database we're targeting with
  3. use the client APIs to do the mapping as described here:
    https://github.com/neo4j/sdn-rx/blob/master/docs/neo4j-client.adoc#mapping-parameters

That would make the repositories extraneous but on the other hand I see the allure of just a simple repository.myMethod() call instead of what could become a very complex stream block if the parameters from the query require a lot of transformations.

These are definitely two very different ways of querying and mapping but seems like both could co-exist. One avenue I haven't gotten to explore too much is could the SDN/RX client and its mapping API replace repositories entirely or is there something that Spring Repositories do that the SDN/RX client mappings can't?

Hi Mike.

Very good points, especially regarding the complexity of reactive streams blocks.

Our approach is:

  • Repositories for the Domain type with sensible derived queries (SDN-RX support all constructs of Spring Data Common). Custom queries are fully support. What I like to see in the custom query annotation is a class attribute pointing to our cypher generator

  • Repositories Support already all standard spring data projections but no custom query results like SDN currently does

  • if you need that, then go over to the client, run your own query and run your own mapping. You get the result as driver native records and can work with them however you like. From what we learned in OGM, we cannot cater for everyone with custom mappings.

  • In between the templates are back: you get imperative and reactive of course. They know your domain entities, but you don’t need to declare a repository for each entity. This is useful in domains with a large number of entities.

All of the things have commonalities

  • all bound to spring application transactions

  • all work with native driver types, thus no more incidentally mapping

I hope that helps.

Thank for again for your input!

Michael S.

1 Like

Hi there,
I've been bending over to manage multi tenancy with Neo4j. First of all many thanks for 4.0.0 feature of having more than 1 DB per server. Second I've seen that you will support some form of switching database.

My problem is that I need server to switch database on each HTTP request. That is if user sends header X-TENANT-ID for example, some filter should detect it and set that to the driver for that particular scope of request. This should also be possible with subdomain etc.

I would love it if it was possible to somehow intercept the Neo4j template or Neo4j client or whichever is the correct one to be manipulated before queries get executed.

Most approaches use Singletons for repositories which in this case could be problematic as I need repositories to be able to execute queries against more than 1 database at the same time. I made special class for manipulating tenant and I'm testing it against the Mongo but I have no idea how to inject it to Neo4j nor is it possible at all if using SDN/RX and wanting all those mappers to work.

As an example:
2 users send HTTP requests at the same time and they execute concurrently.
User A sends X-TENANT-ID as Client1
User B sends X-TENANT-ID as Client2
Repositories call getSession() which resolves DatabaseName from the special service
For user A getSession() returns connection to Database1
For user B getSession() returns connection to Database2
Queries get executed against correct DB and results are returned

Deeply sorry about this one. I tried to work with https://github.com/neo4j/sdn-rx/blob/master/examples/multi-database/src/main/java/org/neo4j/springframework/data/examples/spring_boot/Neo4jConfig.java but it didn't work because of my mistake.

I had issues because I did not use ReactiveDatabaseSelectionProvider but non reactive one with reactive functions.

First, Thanks for the hard work on the RX libraries. They are Awesome!

I'm having issues getting Multi Tenancy to work on Spring SDN/RX.

Here are two interesting observations:

  1. An exception will be thrown if I try to access a database that does NOT exist as expected

  2. All database writes and reads are going to the neo4j (default) database.

Since I get condition #1 I can presume the database switching is working on some level, but not on another because it only ever accesses neo4j.

Here is my DatabaseConfig:

package com.wh.dashboard;

import org.neo4j.driver.Driver;
import org.neo4j.springframework.data.core.DatabaseSelection;
import org.neo4j.springframework.data.core.DatabaseSelectionProvider;
import org.neo4j.springframework.data.core.transaction.Neo4jTransactionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;

@Configuration

public class DatabaseConfig {

    @Bean
    DatabaseSelectionProvider databaseSelectionProvider() {
        return  ()-> {

System.out.println("DATABASE SELECTED: "+TenantContext.getTenant());
            return DatabaseSelection.byName(TenantContext.getTenant());
        };
    }

    @Bean
    @Primary
    @Order(Integer.MIN_VALUE)
    public Neo4jTransactionManager transactionManager(Driver driver,
                                                              DatabaseSelectionProvider databaseNameProvider) {

        return new Neo4jTransactionManager(driver, databaseNameProvider);
    }

}

Michael Simons wrote up an excellent article on configuring different databases via SDN/RX: https://medium.com/neo4j/reactive-multi-tenancy-with-neo4j-4-0-and-sdn-rx-d8ae0754c35

I got this to work!

Thanks for all the support from the neo4j community.

1 Like