Is This A Good Approach For Multi-Tenancy Or Not?

Here’s the situation: we have an application which is used by multiple customers. The application consists of various functional modules. Each customer can use one or many (or obviously all) of these modules. In the past we used to deploy this application for each customer. The configuration file contained various settings that could differ from customer to customer and obviously, each deployed version had its specific configuration file depending on the settings required for each customer. This approach worked, but it was not really ideal.

Multi-tenancy to the rescue! Not sure if this counts as the official definition of multi-tenancy, but wikipedia defines it likes this:

Multitenancy refers to a principle in software architecture where a single instance of the software runs on a software-as-a-service (SaaS) vendor’s servers, serving multiple client organizations (tenants). Multitenancy is contrasted with a multi-instance architecture where separate software instances (or hardware systems) are set up for different client organizations. With a multitenant architecture, a software application is designed to virtually partition its data and configuration so that each client organization works with a customized virtual application instance.

Sounds like this is exactly what we’re looking for. So i’ve recently been working on changing the application to support this, and i came up with the approach i will outline in the rest of this post. The approach does not strictly comply with the definition above, but it does seem to comply with Ayende’s definition of it. I’d like to get some feedback from you guys as to whether you believe this approach is good or not, what could be better, what we need to keep in mind, etc…

First of all, i would like to point out that this application already exists. We already have a database with about 200 tables in it and there are about 150 pages (it’s a web app obviously). So obviously, we can’t just change everything to make it fully compliant with ‘the definition’.

Let’s start with the database. Instead of trying to use one huge database to keep all of the tenants’ data, which would require modifications in a large amount of the existing tables, we’ve decided to go with a separate database for each tenant. Each database will have an identical structure, regardless of whether some functional modules are used or not by a particular tenant. There is also one ‘master’ database which contains tenant-specific data. Basically it contains all of the configuration settings for each tenant, including connection strings to each tenants’ specific database. The connection strings do not contain user names and passwords as we will use Windows’ Integrated Security to connect to the specific databases (more on that in a bit).

Now for the application itself. Our first idea was to have one actual instance of the application, and the application would be able to determine the current tenant for each request based on the URL of the incoming request. Tenant A would have an URL like tenanta.ourproduct.com, tenant B would have tenantb.ourproduct.com etc. The application would basically use the URL to get the correct tenant-info from the master database (note that the connection string to the master database would be the only connection string we’d have in the application’s configuration file) and it would then use the tenant-specific database for each request with the tenant’s application URL.

The idea was to use an NHibernate SessionFactory for each specific tenant. You obviously can’t use just one because you’re using multiple databases. But we also use NHibernate’s 2nd Level Cache, which is problematic when you’re using multiple SessionFactories. The 2nd Level Cache is great, but it doesn’t differentiate between multiple SessionFactories. So if you have the result of a specific query cached, you could get that data back for a different tenant than the one the data actually belongs to if the query’s parameters happen to be identical. Btw, if i’m wrong about this please let me know.

So then we figured we could still use one physical deployment (as in: one physical folder where the application is located), and then we’d use multiple virtual directories in IIS which all point to the same physical folder. We’d basically have one virtual directory (and one instance of the application) per tenant. We still have the benefit of one physical deployment, and because each tenant’s ‘virtual’ application runs in its own AppDomain, the caching problem is no longer an issue. Each virtual directory is configured for the URL of its tenant and the running instance of the application can still retrieve the actual tenant’s data from the master database based on the URL. Each virtual directory can run in its own Application Pool which can be set up to run under a Windows account which is specific to the current tenant. This allows us to use Integrated Security when connecting to the tenant-specific database. So each tenant would only have access to the master database and its own specific database.

Obviously, this approach would require a bit more effort in our ‘management module’ (which is yet to be written). Whenever we need to add a tenant, we not only have to create a specific database for the tenant, we’d also have to create a new windows account for the tenant, set up a new virtual directory, and an application pool if each tenant indeed runs in its own application pool under it’s own Windows account.

Apart from the management module, i’ve modified the application to work with this approach in just a few days work. But, nothing is final just yet… so now i’d like to hear from you guys whether this is a good approach or not. What are the possible problems we need to take into account? Is this really still multi-tenancy? I guess the opinions on this will be divided, but it does largely solve the issue of multiple deployment and multiple configuration files. True, the configuration is now mostly in the master database and those settings need to be maintained as well. However, they could now be maintained without having to redeploy the application which is a plus. Some settings could even be modified by the tenant itself (at least, the users who have the proper privileges to do so).

So anyways, i’m awaiting your feedback 🙂

6 thoughts on “Is This A Good Approach For Multi-Tenancy Or Not?

  1. I have a multi-tenant app with a db for each database and I have a web stie for each customer.

    I use memcached as my cache provider

    I just use specify a different cache.region for each web site.

    Here is an example from one of my Windsor.boo files.

    cache.region_prefix=’continutiy2′

    Words for me.

  2. hah… i can’t believe i didn’t think of that. I’d keep the cache region out of the config file though… seeing as how i’m already providing the tenant’s specific connectionstring to the SessionFactory in code, i can just as easily use a tenant-specific cache region there as well

    thanks for the tip 🙂

    still not sure if i’d be better off with with separate application instances then, or to try to serve all the tenants with one instance

  3. Just another data point and not advocating for/against anything here, but… I have a client who has just recently migrated from a multitenancy situation very similar to what you describe back to a “single” db / “single” web app model.* The maintenance headaches of 200 very complicated databases and application web sites grew into a crushing burden. Even trivial changes became marathons of change management.

    This does have the tradeoff of making the application code more complicated.

    * for certain values of “single” as they use a web farm, load balancing tricks, etc. to manage the physical demands.

  4. >> still not sure if i’d be better off with with separate application instances

    This approach really works for me. As long as all code bases are the same and you don’t start tinkering with individual instances then it really is the least friction.

    I’ve been going with my code base for over a year and I just think my problems would multiply everytime the app. scales.

    Anyway just my opinion.

  5. I am doing almost what you are doing.
    We have a master and instance sites DBs.
    However I do not require each tenant to have its own VDir (AppDomain). Instead, I use a multisite provider to map the “tenant code” in the URL (www.site.com/tenant1/default.aspx) to the actual physical file.
    The end result is that I have one single appDomain. This saves memory.
    The tricky part is not to use static variables to hold site state but use caching instead (Enterprise library) with the site id as part of every key.
    Another big plus is the way we manage localized strings (in the DBs) for each site where we optimize which strings to load for each DB. Since 99% of the strings are the same for all site we can really optimize what strings to cache. This is a long story I cannot describe here.

Comments are closed.

Proudly powered by WordPress | Theme: Code Blog by Crimson Themes.