<

Çok Kiracılı SaaS Mimarisi: Tek Veritabanı mı, Ayrı Ayrı mı?

Ajans yıllarımdan aklımdan çıkmayan bir masa: bir İK yazılımı üreticisiyle oturmuş, sürüm geçişlerini konuşuyorduk. Üç yıl önce 12 müşteriyle başlamışlar, o gün 380 kurumsal müşterileri vardı ve her müşteri için ayrı veritabanı açıyorlardı. İlk bakışta temiz bir karar; pratikte her sürüm geçişinde migration'ların 380 kez koşması demek. Son sürüm geçişleri 5,5 saat sürmüş, üç veritabanında migration yarıda kalmış ve o üç müşteri sabahı yarı çalışan bir sistemle karşılamış. Soruları şuydu: "Baştan mı yanlış yaptık?"

Cevabımız onları şaşırtmıştı: hayır, üç yıl önce doğruydu; bugün yanlış. Multi-tenant mimaride tek doğru cevap yok, müşteri profiliyle birlikte değişen doğrular var. Bu yazıda o doğruların haritasını çıkarmaya çalışacağım.

Yelpazenin İki Ucu: Tek Havuz ve Ayrı Kaleler

Bir uçta herkesin aynı veritabanında, aynı tablolarda durduğu model var: her satırda bir tenant_id kolonu, her sorguda bir filtre. Operasyonu hafiftir — tek şema, tek migration, tek yedek. Bin kiracı da olsa işletme maliyeti neredeyse sabittir. Bedeli ise izolasyonun tamamen uygulama koduna emanet olması: filtreyi unutan tek bir sorgu, A şirketinin bordrosunu B şirketine gösterir.

Öbür uçta her kiracıya ayrı veritabanı (hatta ayrı şema) açan model: izolasyon fiziksel, "yanlışlıkla başkasının verisini görmek" sınıfı hatalar yapısal olarak zor, kiracıya özel yedek ve geri yükleme bedava geliyor. Bedeli de az önce anlattığım hikâye: kiracı sayısı büyüdükçe operasyon yükü doğrusal, hatta migration kazaları hesaba katılınca doğrusaldan beter büyüyor. Bağlantı havuzları, izleme panoları, maliyet muhasebesi — hepsi kiracı sayısıyla çarpılıyor.

Arada da melez bir alan var: paylaşımlı veritabanında şema bazlı ayrım ya da küçük kiracılar havuzda, büyükler ayrı kalede. Çoğu olgun SaaS eninde sonunda bu ara bölgeye taşınıyor.

Havuz modelinin izolasyon dışında bir derdi daha var: gürültülü komşu. Bir kiracının pazartesi sabahı çektiği devasa rapor, aynı havuzdaki diğer doksan dokuz kiracının ekranlarını da yavaşlatır. Panzehirler belli — ağır raporları read replica'ya yönlendirmek, kiracı bazında rate limit, uzun süren işleri kuyruk üzerinden eşzamansız çalıştırmak. Ama bunların hepsi baştan tasarlanırsa ucuz; "neden sistem yavaş" telefonları başladıktan sonra eklenirse pahalı.

Unutulan Tek WHERE'in Bedeli

Paylaşımlı modeli seçecekseniz, izolasyonu geliştirici disiplinine bırakmayın. "Ekip dikkatli, filtreyi unutmayız" cümlesi, ekip beş kişiyken bile riskli; işin içine kod üreten AI ajanları girince tamamen geçersiz. Ajanlar hızlı ve genelde isabetli kod yazıyor ama sizin kiracı modelinizin inceliklerini sözleşme gibi bilmiyor; korkuluk yoksa üretilen sorgunun tenant filtresi içerdiğini kimse garanti edemez.

Korkuluğun katmanları belli. ORM seviyesinde global scope: kiracı bağlamı olmadan sorgu atılamasın, atılırsa exception fırlasın. Veritabanı seviyesinde satır bazlı güvenlik (PostgreSQL'in RLS'i bu işin altın standardı): uygulama katmanı delinse bile veritabanı yanlış kiracının satırını döndürmesin. Ve test seviyesinde: her kritik endpoint için "B kiracısının token'ıyla A kiracısının kaydını iste, 404 bekle" kalıbında otomatik testler. Üç katmandan en az ikisi yoksa, paylaşımlı model bizim gözümüzde üretime çıkamaz.

O yıllarda bir projede bu üçlüyü kurduktan sonra sızıntı sınıfı hata sayısı: iki yılda sıfır. Kurulmadan önceki sayıyı nezaketen yazmıyorum.

Görünürlük tarafı da modelden bağımsız bir zorunluluk: hangi kiracı ne kadar kaynak tüketiyor? Sorgu sürelerini, depolama hacmini ve API çağrılarını kiracı etiketiyle ölçmeye ilk günden başlayın. Bu veri olmadan ne fiyatlandırmanızın kâr ettiğini bilebilirsiniz ne de "hangi kiracıyı ayrı kaleye taşıyalım" sorusuna cevap verebilirsiniz. O dönem kurduğumuz panolarda "en pahalı on kiracı" grafiği standarttı; kaç kez fiyat politikası değiştirtti, sayısını unuttum.

Hangi Müşteri Hangi Modeli Hak Ediyor?

Sorunun cevabı teknik değil ticari. Aylık 500 lira ödeyen küçük işletme için ayrı veritabanı işletmek matematiksel olarak saçma; havuza girer. Yıllık yedi haneli sözleşme imzalayan, sözleşmesine "verimiz fiziksel olarak ayrı duracak" maddesi yazdıran banka iştiraki için havuz teklif bile edilemez; ayrı kale, belki ayrı sunucu, bazen ayrı bölge gerekir.

O yüzden tavsiyemiz mimariyi tarife ile hizalamak: standart paketler paylaşımlı havuzda, enterprise paket ayrı veritabanında. Bu hem maliyeti fiyata yansıtmanın dürüst yolu hem de satış ekibine güçlü bir koz. KVKK ve sektörel regülasyonlar da bu kararda söz sahibi; sağlık ve finans verisi taşıyan kiracılarda izolasyon tartışması genelde hukuk departmanında bitiyor, mühendislikte değil.

Bir de kimsenin kuruluş gününde düşünmediği konu: ayrılan kiracı. Sözleşme bitti, müşteri verisini istiyor — havuz modelinde bunu derleyip teslim etmek ve ardından geri dönülmez biçimde silmek, üzerinde düşünülmemişse günlerce süren elle iş demek. Ayrı veritabanı modelinde ise bir dump ve bir drop. Offboarding senaryosunu mimari kararın terazisine baştan koyun; KVKK'nın silme yükümlülükleri bunu zaten er geç önünüze getirecek.

O 380 veritabanlı ekibe önerdiğimiz yol da buydu: küçük ve orta kiracıları RLS korumalı paylaşımlı havuza taşı, en büyük 25 kiracıyı ayrı tut, migration'ları kuyruklu ve geri alınabilir hale getir. Geçiş altı ay sürecekti; ama sürüm geçişi 5,5 saatten 20 dakikaya inecek, operasyon ekibi de nihayet uyuyabilecekti.

Baştan Doğru Kurmanın Tek Yolu

Yeni SaaS kuruyorsanız size tek tavsiyem şu: hangi modeli seçerseniz seçin, kiracı bağlamını ilk günden kodun her katmanına işleyin. Kiracı kimliği request'ten veritabanına kadar açık biçimde taşınsın, loglara yazılsın, kuyruğa giden her iş kaydında dursun. Model değiştirmek zor ama mümkün; kiracı bağlamı hiç düşünülmemiş bir kod tabanını sonradan çok kiracılı yapmak ise neredeyse yeniden yazmak.

Ve küçük ama etkili bir alışkanlık: geliştirme ortamınızın seed verisinde her zaman en az iki kiracı bulunsun, testler de ikisiyle birden koşsun. Tek kiracılı test ortamı, sızıntı hatasını tanım gereği yakalayamaz — her şey "doğru" görünür, çünkü karışacak ikinci bir veri yoktur. En pahalı hatalar, test ortamının yapısal olarak göremediği hatalardır.

Kiracı bağlamının veritabanı dışına da taştığını unutmayın. Redis'teki cache anahtarları kiracı önekiyle yazılmıyorsa, A kiracısı için hesaplanan rapor B'ye cache'ten servis edilebilir — ve bu sızıntıyı veritabanı denetimleriniz asla göremez. Aynı kural arama indeksleri, yüklenen dosyaların depolandığı bucket yapısı ve kuyruktaki işler için de geçerli. İzolasyon zinciri en zayıf halkası kadar sağlamdır; halkaların hepsi de SQL'de durmuyor.

Kiracı mimarisi, beş yıl sonraki faturası bugünden hesaplanması gereken kararlardan; bu kararları tartışmayı hâlâ çok severim, iletişim sayfası herkese açık.

📅 Yayınlanma:  ·  Yakup Zengin