Zapisano do newslettera!

Adres został dodany do naszej listy mailingowej. Będziesz otrzymywać najnowsze artykuły i aktualizacje prosto na swoją skrzynkę.

Wiadomość wysłana!

Dziękujemy. Otrzymaliśmy Twoją wiadomość i odpowiemy w ciągu 24 godzin.

Zapytanie wysłane!

Dziękujemy. Przeanalizujemy opis projektu i odezwiemy się w ciągu 1 dnia roboczego.

Aplikacja wysłana!

Dziękujemy. Twoja aplikacja na stanowisko została przyjęta. Nasz zespół ją przejrzy i wkrótce się odezwie.

Strona główna/Blog/Architektura SaaS/Multi-tenant SaaS: błędy, które kosztują najwięcej przy 1000. kliencie
Architektura SaaS

Multi-tenant SaaS: błędy, które kosztują najwięcej przy 1000. kliencie

11 min czytania
Rzędy szaf serwerowych w centrum danych z podświetlonym okablowaniem sieciowym
Spis treści

Większość problemów architektury multi-tenant SaaS nie ujawnia się przy 20 klientach, tylko przy 300. Wtedy wdrożenie nowej wersji trwa całą noc, a import danych jednego klienta spowalnia wszystkich pozostałych. Do tego sprzedaż obiecała klientowi enterprise SSO, którego model uprawnień nie udźwignie. Decyzje z etapu MVP były rozsądne, bo pozwoliły szybko dowieźć produkt. Część z nich kosztuje jednak tydzień pracy przy 50 tenantach i kwartał przy 1000.

Poniżej opisuję dziewięć takich decyzji. Przy każdej pokazuję, jak wygląda na początku i co psuje się w skali. Podpowiadam też, co zrobić, zanim jedyną opcją będzie przepisanie systemu.

Izolacja danych w multi-tenant SaaS: decyzje, których nie cofniesz tanio

Błąd 1: model izolacji wybrany przypadkiem

Na początku: wszyscy klienci trafiają do jednej bazy z kolumną tenant_id albo, „bo bezpieczniej”, każdy dostaje własną bazę. Przy 10 klientach oba podejścia działają, więc wybór wynika z tutoriala, a nie z analizy.

Przy 1000 klientach każdy model ma swoją cenę:

  • Wspólna baza: jest najtańsza w utrzymaniu. Tabele rosną jednak do setek milionów wierszy, a jeden duży klient potrafi spowolnić zapytania wszystkich. Usunięcie danych klienta na żądanie z RODO to operacja na dziesiątkach tabel.
  • Schema-per-tenant: daje lepszą separację, ale tysiące schematów obciążają bazę, backupy i narzędzia administracyjne.
  • Database-per-tenant: zapewnia najsilniejszą izolację i prosty restore jednego klienta. Ceną jest czas migracji: 30 sekund na bazę przy 1000 bazach to ponad 8 godzin. Backup, monitoring i minimalny koszt instancji mnożą się przez tysiąc.

Co zrobić: dla większości produktów najlepszy jest model hybrydowy. Mniejsi klienci zostają na wspólnej bazie. Enterprise z wymogami umownymi (lokalizacja danych, osobne klucze, osobny backup) dostaje dedykowaną bazę, wycenioną w planie. Warunek jest jeden: logika biznesowa nie wie, gdzie leżą dane, a o wyborze bazy decyduje jedna warstwa systemu.

Jeśli dopiero zaczynasz, wybierz wspólną bazę, dodaj tenant_id do każdej tabeli z danymi klienta i używaj identyfikatorów UUID. Dzięki temu przeniesienie klienta do osobnej bazy będzie później eksportem, a nie projektem.

Błąd 2: izolacja pilnowana „na honor”

Na początku: każdy programista pamięta, żeby dodać do zapytania filtr po kliencie. Przy kilkudziesięciu ekranach code review wyłapuje braki.

Przy 1000 klientach: endpointów są setki. Dochodzą zadania w tle, raporty i eksporty pisane przez nowe osoby w zespole. Wystarczy jedno pominięte zapytanie, żeby klient A zobaczył faktury klienta B. To nie jest zwykły bug, tylko naruszenie ochrony danych osobowych. Trzeba je ocenić i, jeśli niesie ryzyko, zgłosić do UODO w ciągu 72 godzin. Przy kliencie enterprise oznacza to też realne ryzyko utraty kontraktu.

Co zrobić: izolację ma egzekwować system, a nie dyscyplina zespołu. Potrzebne są trzy niezależne warstwy:

  1. Automatyczny filtr w warstwie dostępu do danych, np. global scope w Laravelu lub filtr w Doctrine. Gdy brakuje kontekstu klienta, filtr zatrzymuje operację.
  2. Row-level security w PostgreSQL, która działa jako siatka bezpieczeństwa dla zapytań pisanych ręcznie.
  3. Automatyczne testy w CI. Tworzą dwóch klientów i sprawdzają, czy dane jednego nigdy nie pojawiają się u drugiego.

Zapytaj zespół, jak kontekst klienta trafia do zadań w kolejce. Jeśli usłyszysz „przekazujemy ręcznie”, to właśnie tam powstanie pierwszy wyciek.

Wydajność i wdrożenia: noisy neighbor i migracje przy tysiącu tenantów

Ta grupa błędów zwykle daje o sobie znać jako pierwsza. Objaw jest charakterystyczny: SaaS nie wytrzymuje wzrostu, choć serwery nie są przeciążone na stałe. Przeciążenia są okresowe i powoduje je jeden klient.

Błąd 3: wspólne zasoby bez limitów (noisy neighbor)

Na początku: jest jedna kolejka zadań, jeden cache i brak limitów API. Przy 10 klientach zasobów jest w nadmiarze.

Przy 1000 klientach: jeden klient importuje 2 mln rekordów i zapycha kolejkę. Maile z resetem hasła u pozostałych czekają 40 minut. Integracja innego klienta odpytuje API kilkadziesiąt razy na sekundę, a raport z pięciu lat danych blokuje połączenia do bazy. Dokładanie serwerów nie pomaga, bo problemem nie jest brak mocy, tylko brak granic.

Co zrobić:

  • Wprowadź rate limiting per tenant wynikający z planu. Działa jednocześnie jako ochrona i jako element oferty.
  • Rozdziel kolejki na pracę krytyczną (logowanie, płatności), standardową i masową (importy, eksporty).
  • Ustaw limit współbieżności per klient w kolejce masowej, żeby nikt nie zajął wszystkich workerów.
  • Przenieś ciężkie raporty na replikę bazy i zapisz w planach limity rozmiaru importów.

Na starcie to kilka godzin pracy. Po roku oznacza przegląd każdego zadania w systemie.

Błąd 4: wdrożenia jak w małym monolicie

Na początku: pipeline uruchamia migracje i wdraża kod na wszystkie serwery naraz. Przy małych tabelach zmiana schematu trwa sekundy.

Przy 1000 klientach: zmiana na tabeli z setkami milionów wierszy potrafi ją zablokować. Zapytania ustawiają się w kolejce i aplikacja przestaje odpowiadać. Przy bazie per klient problem wygląda inaczej: 1000 schematów × 2 minuty to około 33 godzin, przez które część klientów ma nowy schemat, a część stary. Skutek biznesowy jest taki, że wdrożenia odbywają się nocą, rzadziej i ze strachem.

Co zrobić: wymagaj migracji w modelu expand/contract. Najpierw dodajesz nowe struktury, potem przenosisz dane w tle, a stare usuwasz dopiero w kolejnym wydaniu. Kod musi działać z obecną i poprzednią wersją schematu. Feature flagi per tenant oddzielają wdrożenie od udostępnienia funkcji. Nową funkcję włączasz najpierw dla siebie, potem dla 5% klientów, a na końcu dla wszystkich.

Konfiguracja i tożsamość: dług, którego nie spłacisz ratami

Błąd 5: „jeśli to klient X, zrób inaczej”

Na początku: największy klient chce innej numeracji faktur, więc w kodzie pojawia się warunek na jego identyfikator. Potem dochodzą jego logo, domena i integracja z ERP. Przy trzech wyjątkach cały zespół wie, skąd się wzięły.

Przy 1000 klientach: wyjątków są setki i nikt nie wie, które są jeszcze aktywne. Każda zmiana grozi zepsuciem czegoś klientowi z indywidualną umową, więc refaktoryzacja staje, a rozwój produktu zwalnia.

Co zrobić: kod nie powinien znać żadnego klienta z nazwy. Zachowanie wynika z konfiguracji, a konfiguracja jest danymi. W praktyce oznacza to:

  • typowane ustawienia tenanta;
  • wymienne strategie, np. numeracji faktur, workflow akceptacji czy szablonów dokumentów;
  • integracje jako osobne konektory poza rdzeniem aplikacji;
  • własne domeny z automatycznymi certyfikatami.

Customizacja staje się wtedy produktem do sprzedania, a nie długiem technicznym.

Błąd 6: użytkownik należy do jednego tenanta

Na początku: konto jest przypisane do jednej firmy, ma jedną rolę i logowanie przez e-mail i hasło. Przy 10 klientach odpowiada to rzeczywistości.

Przy 1000 klientach rzeczywistość się rozjeżdża:

  • księgowa obsługuje 15 firm i ma 15 kont na różne adresy;
  • partner wdrożeniowy potrzebuje dostępu do wielu klientów;
  • klient enterprise wymaga SSO (SAML lub OIDC) oraz automatycznego zakładania i usuwania kont przez SCIM;
  • ktoś ma być administratorem w jednej firmie i tylko obserwatorem w drugiej.

Zmiana tego modelu dotyka każdego uprawnienia i każdego ekranu z użytkownikami. To jeden z najdroższych refaktorów w SaaS i często blokuje podpisanie dużego kontraktu.

Co zrobić od razu: zaprojektuj globalną tożsamość użytkownika i członkostwa w tenantach, z rolą przypisaną osobno w każdym z nich. SSO oprzyj na sprawdzonym dostawcy tożsamości, np. Keycloak, Auth0, Cognito czy Entra ID. Własne implementacje SAML to klasyczne źródło luk bezpieczeństwa.

Architektura multi-tenant nie psuje się nagle. Psuje się po cichu, w każdym miejscu, w którym system zakłada, że zna odpowiedź na pytanie: czyje to dane i co temu klientowi wolno?

Operacje i pieniądze: koszty, cykl życia tenanta i billing

Błąd 7: nie wiesz, ile kosztuje każdy klient

Na początku: logi nie zawierają informacji o kliencie, metryki są globalne, a rachunek z chmury przychodzi jeden. Przy 10 klientach to wystarcza, bo znasz wszystkich z nazwy.

Przy 1000 klientach: czas odpowiedzi rośnie, a Ty nie wiesz, czy problem mają wszyscy, czy jeden tenant. Support nie znajduje logów do zgłoszenia. Nie wiesz też, czy klient na najtańszym planie nie generuje 30% kosztów modeli AI. W efekcie cennik ustalasz na wyczucie.

Co zrobić: dodaj identyfikator klienta do każdego logu i trace'a. Koszty bezpośrednie, takie jak tokeny LLM, SMS-y, płatne API czy storage, przypisuj dokładnie. Koszty współdzielone (serwery, baza) rozdzielaj proporcjonalnie, np. według czasu przetwarzania. Celem jest miesięczne zestawienie kosztu i przychodu dla każdego tenanta.

Błąd 8: onboarding i offboarding robione ręcznie

Na początku: nowego klienta zakłada programista, uruchamiając skrypt. Dane klienta, który odszedł, zostają w systemie na zawsze.

Przy 1000 klientach: przez self-service przychodzi kilkudziesięciu klientów tygodniowo, a przerwane zakładanie konta zostawia osierocone zasoby. Żądanie usunięcia danych z RODO wymaga sprzątania bazy, plików, wyszukiwarki i systemów zewnętrznych. Gdy klient omyłkowo skasuje dane, przywrócenie ich z backupu całej bazy zajmuje dzień ręcznej pracy.

Co zrobić:

  • Zautomatyzuj provisioning jako powtarzalny proces ze stanami (aktywny, zawieszony, w trakcie usuwania).
  • Prowadź rejestr wszystkich miejsc, w których leżą dane klienta.
  • Zbuduj eksport danych jako funkcję produktu.
  • Usuwaj dane dwuetapowo, z okresem karencji.
  • Zaplanuj backupy: jasna retencja w umowie albo osobny klucz szyfrowania per tenant, którego usunięcie czyni dane w kopiach nieczytelnymi.

Błąd 9: billing i limity dopisane na końcu

Na początku: subskrypcja jest w Stripe, a nazwa planu w bazie. Limity są sprawdzane w kilku miejscach albo wcale.

Przy 1000 klientach: sprzedaż chce limitów użytkowników, rozliczeń za zużycie, dodatków i umów enterprise. Tymczasem warunek „jeśli plan to Pro” siedzi w dziesiątkach miejsc w kodzie, a historii zużycia nie ma. Zmiana cennika zamienia się w kilkumiesięczny projekt.

Co zrobić: rozdziel trzy pojęcia:

  • plan: produkt handlowy;
  • uprawnienia: co tenant może, czyli funkcje i limity;
  • metering: co tenant zużył.

Kod pyta wyłącznie o uprawnienia, nigdy o nazwę planu. Zużycie zapisuj jako niezmienny rejestr zdarzeń. Ten sam rejestr zasila billing i rachunek kosztu per klient.

Checklista przed 100. klientem

Jeśli planujesz budowę platformy SaaS albo masz już kilkudziesięciu klientów, potraktuj tę listę jako minimum. Każdy punkt jest dziś tani, a po przekroczeniu kilkuset tenantów robi się drogi.

  1. Każda tabela z danymi klienta ma tenant_id, a identyfikatory to UUID.
  2. O wyborze bazy decyduje jedna warstwa systemu, a logika biznesowa nie wie, gdzie leżą dane.
  3. Izolacja ma trzy warstwy: filtr w ORM, row-level security i testy wyciekowe w CI.
  4. Brak kontekstu klienta zatrzymuje operację, także w zadaniach w tle.
  5. Rate limiting działa per tenant, a limity wynikają z uprawnień planu.
  6. Kolejki są rozdzielone (krytyczna, standardowa, masowa) i mają limit współbieżności per klient.
  7. Migracje stosują expand/contract, a nowe funkcje włączasz feature flagami per tenant.
  8. W kodzie nie ma żadnego identyfikatora klienta, a customizacja jest konfiguracją.
  9. Model użytkowników opiera się na członkostwach z rolą per tenant i ma gotowy plan na SSO i SCIM.
  10. Logi zawierają identyfikator klienta, a koszty bezpośrednie są przypisane do tenantów.
  11. Masz opisany cykl życia tenanta: provisioning, eksport, usunięcie zgodne z RODO i restore jednego klienta.

Podsumowanie: multi-tenant SaaS to decyzje odroczone w czasie

Żaden z tych błędów nie wynika z niekompetencji. To rozsądne skróty z etapu MVP, których koszt rośnie razem z liczbą klientów. Większość da się naprawić ewolucyjnie, bez przepisywania systemu, pod warunkiem że zaczniesz przy 100 klientach, a nie przy 1000.

Multi-tenant SaaS, który dobrze się skaluje, ma kilka wspólnych cech. Izolację egzekwuje w nim infrastruktura, zasoby mają limity, a konfiguracja i uprawnienia są danymi. Koszt każdego klienta jest znany.

W Midapt projektujemy takie fundamenty w ramach rozwoju produktów SaaS i porządkujemy je w istniejących systemach podczas modernizacji. Jeśli czujesz, że architektura z MVP zaczyna trzeszczeć, umów bezpłatną konsultację. Przejdziemy razem przez checklistę i wskażemy, od czego zacząć.

Najczęściej zadawane pytania o multi-tenant SaaS

Tak, jeśli izolacja jest egzekwowana technicznie, a nie tylko dyscypliną programistów. Minimum to automatyczny filtr w warstwie dostępu do danych, który przy braku kontekstu klienta zatrzymuje operację, do tego row-level security w bazie i testy sprawdzające, czy dane jednego tenanta nie pojawiają się u drugiego. Dedykowana baza bywa potrzebna głównie z powodów umownych lub regulacyjnych.

Schema-per-tenant jest tańsza w infrastrukturze, ale przy setkach schematów obciąża bazę i spowalnia backupy. Database-per-tenant daje najsilniejszą izolację i łatwy restore jednego klienta, za to mnoży migracje, połączenia i koszty. Dla większości produktów najlepiej sprawdza się wspólna baza dla mniejszych klientów i dedykowane bazy dla enterprise, wycenione w planie.

To sytuacja, w której jeden tenant zużywa tyle wspólnych zasobów, że spowalnia pozostałych. Może chodzić o kolejkę zadań, połączenia do bazy, cache albo CPU. Typowe przyczyny to duże importy, intensywne integracje API i ciężkie raporty. Pomagają rate limiting per tenant, osobne kolejki dla pracy masowej, limity współbieżności i limity planów egzekwowane w systemie.

Koszty bezpośrednie, czyli tokeny LLM, SMS-y, płatne API i storage, mierz dokładnie, przypisując każde zdarzenie do klienta. Koszty współdzielone, takie jak serwery i baza, rozdzielaj proporcjonalnie według przybliżonej miary, np. czasu przetwarzania lub wolumenu danych. Zestawienie kosztu z przychodem per tenant pokazuje, którzy klienci i plany są nierentowni.

Da się, ale koszt zależy od tego, jak bardzo kod zakłada konkretny model. Najłatwiej jest, gdy każda tabela ma tenant_id, identyfikatory to UUID, a wybór bazy odbywa się w jednej warstwie. Wtedy klientów przenosi się stopniowo, pojedynczo i bez przestoju. Bez tych fundamentów zmiana zwykle oznacza wielomiesięczny refaktor.