Multi-tenant web applications
Many companies with web applications cater not to end users, but to other businesses with customers of their own. There are many ways to architect a web application to meet such needs. Sometimes, a separate instance of the application is installed for each customer (for example, when the customer base is small but has diverse needs). This is known as a "single-tenant" setup. On the other hand, when many clients with similar needs are expected, it might make sense to run a single instance of the application that handles all of the clients.
Throughout this discussion, I'll try to use the following terms pretty consistently:
- Tenant: a business client served by an instance of a web application. Think, as is natural, of tenants living in rented homes; some homes are standalone and rented to a single tenant, while others homes (e.g. apartments) house many tenants.
- Single-tenant application: an application, intended for multiple client businesses, that's installed on a separate instance (or pool of instances) for each client. (Thus each instance of the application serves a single tenant.)
- Multi-tenant application: an application, intended for multiple client businesses, that's installed on a single instance/pool and serves all clients from that instance. (Thus the single instance serves multiple tenants.)
Pros and Cons of multi-tenancy
Architecting an application for multi-tenancy isn't an easy process. Here are some guidelines to help determine if an application should be multi-tenant:
Reduced maintenance costs
A multi-tenant application is a single codebase, installed in a single location (a single server or pool of servers), and potentially relying on a single instance of each backing service (cache, database, log, etc.). When an end-user reports an error, developers don't need to determine first on which instance the error occurred before replicating and debugging. A single source of application logs helps here, too. And when a fix is ready, it gets committed into the (only) repository, and re-deployed to the (only) server/pool – so all tenants benefit from the fix immediately and simultaneously.
Imagine a situation where an application is set up for two clients, and each client generates about 1.5 server's worth of load. If the application is deployed separately for each client, it takes four servers (two each) to handle the total load. On the other hand, a multi-tenant application could be installed on just three instances to handle the same load. With more tenants, the advantages of a multi-tenant architecture grow even further.
A single-tenant application is simpler than the equivalent multi-tenant app. There doesn't need to be any code to protect against data leakage between tenants, or to detect which tenant a web request is intended for. Its configuration can be essentially static. With a separate instance of the application for each client, logs are segregated, so the log format can be simpler.
Reduced distribution of risk
By running on a single server (or pool), backed by a single database, a multi-tenant app has fewer points of failure – but those failure points have a greater effect. When a single-tenant application instance breaks (say, the database goes down), it brings down a single tenant; other instances are able to continue unaffected. When the database for a multi-tenant app is unavailable, all tenants experience loss of service.
Reduced flexibility across tenants
A single-tenant, multi-instance app has greater flexibility to set low-level configurations (of the server hardware or OS, or of the web server, or the application itself) than a multi-tenant app. This won't be a problem for many applications, but if the application needs a high degree of customization for each new tenant, a multi-tenant app might not be the best solution.
Concerns of a multi-tenant application
A multi-tenant application needs to be carefully designed in order to avoid major problems. It:
- needs to detect the client (or tenant) that any individual web request is for;
- must separate persistent data for each tenant and its users;
- has to segregate configuration for each tenant;
- can not allow cached data (e.g. views) to leak between tenants;
- requires separate background tasks for each tenant;
- must identify to which tenant each line of log output belongs.
Over the next few weeks, I'll be writing several more blog posts to deal with each of the concerns identified above. We'll take an in-depth look at each problem and how Quick Left solved it. Stay tuned – we'll start off with tenant detection and database separation soon! As each post goes up, we'll add links here so that it's easy to keep track of the whole series.