Multi tenancy and Compare Exchange values

38 views
Skip to first unread message

Alexandru Puiu

unread,
May 3, 2021, 11:14:05 AM5/3/21
to RavenDB - an awesome database
I'm building a multi-tenant application using a RavenDB identity store that connects to a different database for each tenant's users.

I register one store, and then use dependency injection to create an IAsyncSession for each tenant:

services.AddScoped(sp => sp.GetRequiredService<IDocumentStore>().OpenAsyncSession(sp.GetService<IOptions<UserStoreOptions>>()?.Value?.DatabaseName));

The library I'm using creates a Compare Exchange Value for each user to reserve the username, but because it's doing the operation against the document store instead of the session, it doesn't know the name of the database I intend, so it's executing it against the default database. Compare exchange values get created in the default database, while users are created in the tenant's database

I'm happy to submit PRs to the library to fix this behavior, just need some guidance.

Solutions and issued:
1. Call .ForDatabase here: DbSession.Advanced.DocumentStore.Operations.SendAsync
- I haven't found a way to get the current database the session is targetting, since Database is an internal property. I can get it if I parse the identifier, but that's too hacky

2. Add an explicit configurable IOptions, and put the database name in there, then modify all the code to use that value if it's set.
- Not sure if the dev of the library would be in favor of it, but likely best option

3. Use session.Advanced.ClusterTransaction.CreateCompareExchangeValue(compareExchangeKey, user.Id);
- Since the IAsyncSession is injected, I can't specify the ClusterWide option in the session constructor, and I can't make every IAsyncSession I inject be cluster-wide since other things depend on it

4. Get fancy with injection and have it figure out the right session to customize based on who's asking for it
- This just sounds like trouble in the long-run, I don't like it

5. Create a separate IDocumentStore for each tenant
- I'm concerned about unnecessary memory usage, extra connections to the ravendb server for things like subscriptions, duplicate cache? Having IAsyncSessions injected sounds a lot lighter than IDocumentStore objects. Let me know if not the case


Igal Merhavia

unread,
May 5, 2021, 2:19:39 AM5/5/21
to rav...@googlegroups.com

Solutions and issued:

  1. Call .ForDatabase here: DbSession.Advanced.DocumentStore.Operations.SendAsync
  • I haven’t found a way to get the current database the session is targetting, since Database is an internal property. I can get it if I parse the identifier, but that’s too hacky
  • var store = DbSession.Advanced.DocumentStore;
    store.Operations.ForDatabase(store.Database).SendAsync();
    
  1. Create a separate IDocumentStore for each tenant
  • I’m concerned about unnecessary memory usage, extra connections to the ravendb server for things like subscriptions, duplicate cache? Having IAsyncSessions injected sounds a lot lighter than IDocumentStore objects. Let me know if not the case

Best regards,
Igal


--
You received this message because you are subscribed to the Google Groups "RavenDB - an awesome database" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ravendb+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ravendb/74df970d-30da-47ba-8b5b-a8fae2fd8e17n%40googlegroups.com.

Alexandru Puiu

unread,
May 5, 2021, 12:36:15 PM5/5/21
to RavenDB - an awesome database
Hi Igal,
Thanks for the reply, I agree with having only one IDocumentStore.
DbSession.Advanced.DocumentStore.Database points to the default database, not the one used by DbSession. I found that the AsyncDocumentSession class has the DatabaseName property it's just not exposed in the interface, and if I cast IAsyncDocumentStore to AsyncDocumentStore I can access the correct value.

Igal Merhavia

unread,
May 6, 2021, 1:19:16 AM5/6/21
to rav...@googlegroups.com
Hi,

Even though the chance we will remove the database name from the `AsyncDocumentSession` is very low, I don't recommend using our internal objects.
I highly recommend you to stick with the interfaces.

Best regards,
Igal

Reply all
Reply to author
Forward
0 new messages