Live data from Hacker News

Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

news.ycombinator.com

201–210 of 264 posts

Re: Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

#201
As usual, it depends. If you have a few large customers and a large number of smaller ones I'd isolate the large ones in their own DBs and put the rest in a single DB. If any of the smaller ones become dominant then you move them out as well.

If all your customers are individuals or small accounts then I'd put them all in a single DB, but I'd still build in the option to redirect to another DB if the software has applicability to enterprise level customers.

Note that it is perfectly OK to upsell the capability to isolate your customers data from each other, segregation options could be offered at multiple levels (all in one bucket, different DB, different cluster), each with their own pricepoint. Some customers will simply demand this and move elsewhere if you don't offer it (and they are typically quite insensitive to price as long as the right boxes get checked for their auditors).

Re: Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

#203
This question reminds me of some legacy system which I've seen in the past :D :D :D

In summary it was working in the following way:

There was table client(id, name).

And then dozens of other tables. Don't remember exactly the structure, so I will just use some sample names: - order_X - order_item_X - customer_X - newsletter_X

"X" being ID from the client table mentioned earlier.

Now imagine dozens of "template" tables become hundreds, once you start adding new clients. And then in the code, that beautiful logic to fetch data for given client :D

And to make things worse, sets of tables didn't have same DB schema. So imagine those conditions building selects depending on the client ID :D

Re: Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

#204
post #73
post #28

Earlier quoted context omitted.

Not a problem with MySQL, "use `tenant`" switches a connection's schema. Rails migrations work reasonably well with apartment gem. Never had a problem with inconsistent database migrations. Sometimes a migration will fail for a tenant, but ActiveRecord migrations records that, you fix the migration, and reapply, a no-op where it's already done. We don't use a single mysqld for every tenant mind, it's not like migrati…

> USE `tenant` But if the idea is to isolate accounts form each other, the different schemas would be available to different DB users. You would have to re-authenticate to get access to the other DB.

Using schemas gives you imperfect but still improved isolation. It's still possible for a database connection to cross into another tenant, but if your schema search path only includes the tenant in question, it significantly reduces the chance that cross-customer data is accidentally shared.

Re: Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

#205
post #168
post #161

Earlier quoted context omitted.

Sure, this problem can be solved as can any other -- but there's a cost to it in development time. For every feature, the dev working on it had to do it in 3 steps to ensure backwards compatibility and handle partial migration failures gracefully. Imagine doing this for a small feature - the ops effort dwarfs the actual feature! Many small features would probably be dropped.

Multi-single-tenant actually makes it easier to do transactional schema upgrades because these operations only impact one customer at a time. No fiddling with middle-of-the-migration state.

Depends on your uptime guarantees.

It becomes a non-issue if you're able to go down for a few moments. Not necessarily viable for some software though, it just depends on what you're running.

(Especially because you can apply the migration on slave and subsequently switch master, making this downtime as quick as the time your system needs to do a database switch.)

This doesn't apply to big services with millions of users, however.

Re: Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

#206
post #108

My startup currently does just this 'at scale', which is for us ~150 b2b customers with a total database footprint of ~500 GB. We are using Rails and the Apartment gem to do mutli-tenancy via unique databases per account with a single master database holding some top-level tables. This architecture decisions is one of my biggest regrets, and we are currently in the process of rebuilding into a single database model.…

Echoing this as well, I worked for Influitive and was one of the original authours of apartment (sorry!)

There are a lot of headaches involved with the "tenant per schema" approach. Certainly it was nice to never have to worry about the "customer is seeing data from another customer" bug (a death knell if you're in enterprisish B2B software), but it added so many problems:

- Migrations become a very expensive and time-consuming process, and potentially fraught with errors. Doing continious-deployment style development that involves database schema changes is close to impossible without putting a LOT of effort into having super-safe migrations.

- You'll run into weird edge cases due to the fact that you have an absolutely massive schema (since every table you have is multiplied by your number of tenants). We had to patch Rails to get around some column caching it was doing.

- Cloud DB hosting often doesn't play nice with this solution. We continually saw weird performance issues on Heroku Postgres, particularly with backup / restores (Heroku now has warnings against this approach in their docs)

- It doesn't get you any closer to horizontal scalability, since connecting to a different server is significantly different than connecting to another schema.

- It will probably push the need for a dedicated BI / DW environment earlier than you would otherwise need it, due to the inability to analyze data cross-schema.

I still think there's maybe an interesting approach using partioning rather than schemas that eliminates a lot of these problems, but apartment probably isn't the library to do it (for starters, migrations would be entirely different if partioning is used over schemas)

Re: Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

#207

We're a small company (~50 customers) delivering SaaS using Django/Postgres/uWSGI for a niche B2B market where privacy and data confidentiality is paramount. Currently we deploy one DB + unique uWSGI instances for each customer. This has some drawbacks which has made us look a bit into multi-tenancy as well. Everything is served on dedicated hardware, using common codebase, and each customer is served on a unique sub…

In such case if server gets exposed then one can have creds to all DBs. Can this be avoided somehow?

Re: Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

#208
We did this in a company long long time ago, each customer had their own Access database running an ASP website. Some larger migrations were a pain, but all upgrades were billed from the customers, so it didn't affect anything.

If you can bill the extra computing and devops work from your customers, I'd go with separate environments alltogether. You can do this easily with AWS.

On the plus side you can roll out changes gradually, upgrade the environments one user at a time.

Also if Customer X pays you to make a custom feature for them, you can sell the same to all other users if it's generic enough.

Re: Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

#209
Oracle is doing this for their cloud software I think. Benefit is that they can migrate your environment when you are ready. This way they can ensure someones environment always keeps working. Downside is that there is a lot of admin and a lot of things that can go bad.

Re: Ask HN: Has anybody shipped a web app at scale with 1 DB per account?

#210
A big healthcare company I worked for did this. It worked extremely well, though it wasn't without its drawbacks. They adopted the database-per-tenant pattern in the early '00s, and I truly think it was one of the major things that allowed them to scale to a large number of (increasingly larger in data/access patterns) clients. It also made regulatory compliance a bit easier (everyone's data is pretty firewalled off at the database-access-credentials layer) I think, but that wasn't really my department.

We ended up in the "thousands to tens of thousands" of clients range, with thousands of tables per client and a pretty hairy schema.

Each customer had their own schema on one of a few dozen giant database servers. The company pushed this idea out to other parts of their infrastructure: separate webserver/message broker/cache tiers existed for each underlying database server, so outages or brownouts in one component couldn't affect other customers' data that much.

Schema migrations, interestingly, weren't much of a problem. The practice was nailed down early of "everyone gets migrated during a release, no snowflakes". That, plus some pretty paranoid tooling and an acceptable-downtime (well, on paper it wasn't acceptable, but everyone kinda understood that it was) in seconds-to-minutes during a release made migrations roughly as traumatic as migrations anywhere I've worked (which is to say, "somewhat"), but not too much more. It did take a lot of work to get the tooling right across multiple schemas in the same database server though. Investment in tooling--up to and including dedicated teams working on a single tool without a break-up date or firm mandate other than "make this not suck and keep the lights on"--is critical here, as in most areas.

Things that were hard:

- Connection management. Others on this thread have pointed that out. Connection pooling and long-lived queue workers were essential, and the web/request tier couldn't "scale out" too far without hitting connection limits. Scheduled jobs (and this company loved cron jobs, thousands of distinct invocations per tenant) were a problem in the connection-management department. Carefully written tooling around webserver connection reuse, cron-job execution harnesses (they didn't really run as cron jobs, they got shipped to a worker already running the code with warm/spare database connections and run there--all highly custom), and asynchronous jobs was needed. That occupied a team or three for awhile.

- The "whale" problem. When an individual tenant got big enough to start crowding out others on the same database server, it caused performance problems. We eventually worked on a migration tool that moved a client's entire footprint (and remember, this isn't just databases, but webs/caches/queue worker hosts/etc.) onto another shard. Building this tool was a lot of work, but when it was done it worked surprisingly well. My advice in this area: build a good manually-initiated/semi-supervised migration system. Leverage underlying database technology (binlog based replication). Don't hesitate to get very dirty and custom with e.g. replication logfile formats, and don't assume that $off_the_shelf_data_replicator isn't gonna collapse when you want to do online per-schema replication in massive parallel from the same database (not even if that tool cost you millions of dollars). Do NOT succumb to the allure of "we can automate the bin-packing and it'll constantly rearrange clients' datasets for optimal resource usage!" Manual is just fine for data migrations that big. Worst case, part of someone's job is to initiate/supervise them.

- SPOFs sucked. Some datasets weren't per-tenant at all; sometimes client companies merged together or split up; some data arrived intended for a tenant but wasn't tagged with that tenant's ID, so it would have to go into some separate database before it found a home. These systems were, bar none, the biggest liabilities, causes of production issues, and hardest things to code around in the entire company. You'd think that having to write application code for thousands of logical databases across all the per-tenant schemas would suck, but in reality it wasn't too hard. It was making sure your code didn't accidentally talk to a SPOF that was the problem. My advice here: microservices do not help with this problem. HTTP, gRPC, or raw database wire protocol: if you have dependencies on a "tragedy of the commons"-type used-by-everyone server sneaking into your nicely sliced up per-tenant architecture, those callsites are going to be the cause of your sleepless nights. Get good visibility into where they occur. Favor "push" into per-tenant models over per-tenant code doing a blocking "pull". Even if the push approach causes massive additional complexity and work. The costs of pull are too great.

- Some database specific shit (even on polished hosted offerings from AWS, or big-budget Oracle installs) will start acting really squirrely when you're talking to thousands of identical schemas on the same database server (and thus tens or hundreds of thousands of identical tables with different data). If you double down on this route, be prepared to have a few really, really good database folks on staff. I don't mean "help me fix my slow giant reporting query" people, I mean "familiar with the internals" folks. Example: query plans can be cached based on query text, globally, across an entire database server. Different schemas have super different clients, and thus super different data distribution among 100s of GBs of data. The plan that gets cached for query X against client A is the product of running heuristics/histograms/etc. across client A's data. That plan might perform pathologically when query X runs against client B (on the same database server)'s data, and finding out how/why is really annoying. Solution: bust the cache by a) happening to know that SQL comments aren't stripped from query texts before the text is used as a plan-cache key and b) prepend each tenant's identifier to each query at the database-driver level to prevent cache pollution. Result: you have traded a spooky query performance issue for a query-plan-cache-size issue; now your queries are all predictably slow because all your tenants' different queries are thrashing the plan cache. Tradeoffs abound.

Post reply on HN