Když stavíš aplikaci, kterou používá víc firem (typicky každá SaaS appka), obsluhuje jeden a tentýž systém spoustu zákazníků zároveň. A data firmy A nesmí nikdy vidět firma B. Tomu, jak jeden systém bezpečně sdílet mezi víc zákazníky, se říká multitenancy. Je to klíčové téma pro každého, kdo dělá SaaS — a snadné místo, kde udělat vážnou bezpečnostní chybu.
Pár pojmů na úvod
- Tenant (nájemník) = jeden zákazník systému (typicky firma a všichni její uživatelé).
- Multitenancy = jeden běžící systém slouží více tenantům, jejichž data jsou oddělená.
- Izolace = jak moc jsou data jednotlivých tenantů od sebe oddělená.
Analogie: bytový dům (jeden systém), kde má každý nájemník svůj byt (svoje data) a do cizího se nedostane.
Tři způsoby, jak oddělit data tenantů
Liší se mírou izolace, cenou a složitostí. Od nejjednoduššího po nejizolovanější:
| Model | Jak | Výhody | Nevýhody |
|---|---|---|---|
| Sdílená databáze, sdílené tabulky | Všichni v jedné tabulce, navíc sloupec tenant_id | Nejlevnější, nejjednodušší | Nejtenčí hranice — stačí zapomenout filtr a uniknou data |
| Sdílená databáze, schéma na tenanta | Jedna databáze, ale každý tenant má vlastní sadu tabulek | Lepší oddělení | Složitější správa, migrace na X schémat |
| Databáze na tenanta | Každý tenant má vlastní databázi | Nejsilnější izolace | Nejdražší a nejnáročnější na provoz |
Pro start je obvykle správně sdílená databáze se sloupcem
tenant_id— nejjednodušší a uveze hodně. Na silnější izolaci (vlastní databáze) přejdeš, až to vyžaduje regulace nebo velký zákazník.
Největší riziko: únik dat mezi tenanty
U nejčastějšího modelu (sdílené tabulky se sloupcem tenant_id) drží hranici mezi zákazníky jediná
věc: filtr WHERE tenant_id = …. Když ho na jednom dotazu zapomeneš, zákazník uvidí cizí data — a
to je v SaaS jedna z nejzávažnějších možných chyb (únik dat jiné firmy).
Proto se to nenechává na paměti vývojáře. Filtrování podle tenanta se vynutí systémově:
- centrálně ve vrstvě přístupu k datům (každý dotaz automaticky dostane
tenant_id), - nebo přímo v databázi (Postgres umí Row-Level Security — řádek je vidět jen „svému" tenantovi).
Pravidlo: tenant nikdy nesmí záviset jen na tom, že vývojář nezapomněl přidat filtr. Vynuť to.
⚠️ Ani Row-Level Security není kouzlo zdarma. Aplikace musí na začátku každého requestu správně nastavit, kdo je tenant (
SET app.tenant_id = …) — a pozor na connection pooling: když se spojení recykluje mezi requesty a tahle proměnná „přetrvá", uvidíš data cizího tenanta. Navíc RLS obchází vlastník tabulky a superuser (BYPASSRLS), takže běž pod účtem s minimem práv.
Co bývá společné a co per-tenant
I ve sdíleném systému mívá každý tenant vlastní konfiguraci — vlastní logo a nastavení, někdy
vlastní subdoménu (firma.tvojeapp.cz), vlastní limity (kolik uživatelů, kolik místa). Tenant se
obvykle určí z přihlášení uživatele nebo právě z domény, a tahle informace se pak protáhne celým
zpracováním requestu.
Dvě operační témata, na která senior u SaaS narazí:
- Offboarding / smazání dat tenanta. Když zákazník odejde (nebo dle GDPR požádá o výmaz), musíš
umět spolehlivě smazat všechna jeho data — u sdílených tabulek to znamená projít všechny tabulky
s
tenant_id. Vlastní databáze na tenanta to naopak zjednoduší (zahodíš celou DB). - Data residency a per-tenant šifrování. Velký/regulovaný zákazník může chtít data v konkrétním regionu nebo šifrovaná vlastním klíčem — další osa izolace nad RLS.
V praxi je nejčastější cílový stav hybrid: většina (malých) tenantů sdílí tabulky, a pár velkých nebo regulovaných se vyčlení do vlastní databáze. Není to „buď, anebo".
Failure modes — jak to v praxi praská
- Zapomenutý
tenant_idfiltr → zákazník uvidí data jiné firmy. Nejzávažnější chyba multitenancy. - Tenant jen v aplikační vrstvě → jediná díra v kódu = únik; lepší vynutit i v databázi.
- „Hlučný soused" → jeden velký tenant ve sdílené databázi zpomalí všem ostatním.
- Migrace na mnoho schémat/databází → změna struktury se musí spolehlivě provést pro všechny tenanty.
🛠️ Cvičení
- Vyber model. Pro malou SaaS appku se stovkami malých firem vyber model izolace a zdůvodni. Kdy bys volil databázi na tenanta?
- Najdi únik. Máš dotaz „vrať všechny faktury" bez
tenant_id. Popiš, co se stane, a oprav to. - Vynuť izolaci. Navrhni, jak zajistíš, aby se na
tenant_idfiltr nedalo zapomenout (centrální vrstva nebo Row-Level Security). - Hlučný soused. Jeden tenant generuje 80 % provozu a zpomaluje ostatní ve sdílené databázi. Navrhni dvě řešení.
- Určení tenanta. Appka běží na subdoménách
firma.app.cz. Popiš, jak z requestu zjistíš, o kterého tenanta jde, a protáhneš to dál.
Náčrt řešení — rozbal, až si cvičení zkusíš sám
- Vyber model — pointa: pro stovky malých firem zvol sdílenou databázi se sloupcem
tenant_id— je nejlevnější, nejjednodušší a uveze hodně. Pozor: databázi na tenanta volíš až když to vyžaduje regulace, velký zákazník nebo silnější izolace; cílový stav bývá hybrid. - Najdi únik — pointa: dotaz „vrať všechny faktury" bez
tenant_idvrátí faktury všech firem, takže zákazník uvidí cizí data; oprava je přidat filtrWHERE tenant_id = …. Pozor: tohle je nejzávažnější chyba multitenancy — proto se na filtr nesmí jen spoléhat, že vývojář nezapomene. - Vynuť izolaci — pointa: filtr nenech na paměti vývojáře, ale vynuť ho systémově — centrální datovou vrstvou (každý dotaz automaticky dostane
tenant_id) nebo Row-Level Security v Postgresu. Pozor: u RLS musíš na začátku requestu správně nastavit tenanta (SET app.tenant_id), hlídat connection pooling (přetrvalá proměnná = cizí data) a běžet pod účtem bezBYPASSRLS. - Hlučný soused — pointa: jeden tenant s 80 % provozu spotřebuje většinu zdrojů a zpomalí ostatním; řeš to limity na tenanta nebo vyčleněním velkého tenanta do vlastní databáze. Pozor: je to daň za sdílení jednoho systému — proto se velcí zákazníci často oddělují.
- Určení tenanta — pointa: ze subdomény requestu (
firma.app.cz) zjistíš tenanta, ověříš, že na něj přihlášený uživatel opravdu patří (jinak by si cizí subdoménou sáhl na cizí data), a tuhle informaci protáhneš celým zpracováním requestu, aby ji dostal každý dotaz. Pozor: tenant se dá určit i z přihlášení uživatele — důležité je, aby ho měl k dispozici filtr na datech.
🧠 Otázky & odpovědi
Co je multitenancy a tenant?
Tenant je jeden zákazník systému (typicky firma a její uživatelé). Multitenancy znamená, že jeden běžící systém obsluhuje víc tenantů zároveň a jejich data jsou oddělená — jako bytový dům, kde má každý nájemník svůj byt a do cizího se nedostane. Je to základní model SaaS aplikací.
Jaké jsou tři modely izolace tenantů a jak se liší?
Sdílená databáze a tabulky (sloupec tenant_id u každého řádku) — nejlevnější a nejjednodušší, ale
nejtenčí hranice. Sdílená databáze, schéma na tenanta — lepší oddělení, složitější správa.
Databáze na tenanta — nejsilnější izolace, ale nejdražší a nejnáročnější na provoz. Liší se mírou
izolace versus cenou a složitostí.
Proč je zapomenutý tenant_id filtr tak nebezpečný?
U sdílených tabulek drží hranici mezi zákazníky jediná věc — filtr WHERE tenant_id = …. Když ho na
jednom dotazu zapomeneš, zákazník uvidí data jiné firmy, což je v SaaS jeden z nejzávažnějších možných
úniků. Proto se filtrování nenechává na paměti vývojáře, ale vynutí systémově (centrální datová
vrstva nebo Row-Level Security v databázi).
Který model izolace zvolit na start?
Obvykle sdílenou databázi se sloupcem tenant_id — je nejjednodušší a uveze hodně. Na silnější
izolaci (vlastní schéma nebo dokonce vlastní databáze na tenanta) přejdeš, až to vyžaduje regulace,
velký zákazník, nebo když ti jeden „hlučný soused" zpomaluje ostatní.
Co je problém hlučného souseda v multitenancy?
Ve sdílené databázi může jeden velký nebo aktivní tenant spotřebovat většinu zdrojů a tím zpomalit všem ostatním. Řeší se to limity na tenanta, oddělením velkých tenantů do vlastní databáze, nebo celkově silnější izolací. Je to daň za sdílení jednoho systému mezi víc zákazníků.
