Arsitektur Multi-Tenant SaaS B2B dengan PostgreSQL: Row-Level Security vs Multi-Schema
Dalam rekayasa platform SaaS (Software as a Service) enterprise dan aplikasi portal B2B, pemilihan strategi multi-tenancy pada lapisan basis data merupakan keputusan arsitektural paling mendasar. Kesalahan desain pada tahap ini dapat mengakibatkan kebocoran isolasi data antar klien korporat (cross-tenant data breach), lonjakan biaya infrastruktur cloud yang tidak terkendali, atau kebuntuan proses migrasi skema ketika basis data mencapai jutaan baris data.
Ketika melayani puluhan hingga ribuan entitas bisnis dalam satu klaster server, rekayasawan dihadapkan pada dua pendekatan dominan dalam ekosistem PostgreSQL: Row-Level Security (RLS) dengan skema tunggal bersama (shared-schema shared-database), atau Multi-Schema Isolation di mana setiap penyewa (tenant) memiliki ruang nama skema terpisah di dalam basis data yang sama.
Artikel ini membedah perbandingan teknis, keamanan, kompleksitas connection pooling, serta strategi operasional zero-downtime untuk kedua model tersebut.
1. Tiga Paradigma Isolasi Data dalam Multi-Tenancy #
Sebelum membedah implementasi PostgreSQL, penting untuk memahami spektrum isolasi data dari yang paling terisolasi hingga yang paling efisien:
+-----------------------------------------------------------------------------------------+
| SPEKTRUM ISOLASI BASIS DATA MULTI-TENANT B2B |
+-----------------------------------------------------------------------------------------+
| |
| [ MODEL 1: Database Per Tenant ] |
| Isolasi: Maksimal | Biaya RAM: Sangat Tinggi | Kompleksitas Migrasi: Ekstrem |
| Tenant A DB <---> Tenant B DB <---> Tenant C DB |
| |
| [ MODEL 2: Schema Per Tenant (Multi-Schema) ] |
| Isolasi: Tinggi | Biaya RAM: Moderat | Kompleksitas Migrasi: Tinggi |
| Database Tunggal -> Schema "tenant_pewali", Schema "tenant_acme" |
| |
| [ MODEL 3: Shared Schema + Row-Level Security (RLS) ] |
| Isolasi: Kuat (Kernel DB) | Biaya RAM: Terendah | Kompleksitas Migrasi: Rendah |
| Database Tunggal -> Tabel Bersama dengan Kolom `tenant_id` + Kebijakan RLS |
| |
+-----------------------------------------------------------------------------------------+
Untuk perusahaan rintisan B2B hingga level korporat dengan ratusan klien aktif, Database per Tenant jarang sekali efisien karena setiap instance basis data mengonsumsi alokasi RAM statis (shared buffers, work_mem), serta menyulitkan agregasi analitik platform secara agregat. Oleh karena itu, diskursus rekayasa modern berpusat pada perbandingan antara Multi-Schema dan Shared-Schema dengan RLS.
2. Pendekatan Multi-Schema: Kelebihan dan Titik Kegagalan #
Pada model Multi-Schema, setiap kali penyewa baru mendaftar, sistem menjalankan perintah DDL seperti CREATE SCHEMA tenant_abc dan menduplikasi seluruh tabel ke dalam skema tersebut. Saat aplikasi menangani request HTTP, koneksi basis data menetapkan search path:
SET search_path TO tenant_abc, public;
SELECT * FROM orders WHERE status = 'COMPLETED';
Kelebihan Multi-Schema #
- Pemisahan Logis yang Kasar: Mengurangi risiko ketidaksengajaan query SQL manual dari staf internal yang lupa menambahkan klausa
WHERE tenant_id = 'xxx'. - Kustomisasi Kolom Parsial: Memberi ruang jika ada klien B2B tier enterprise yang membutuhkan tabel khusus atau relasi tambahan tanpa mengubah klien lain.
- Backup dan Restore Per-Tenant: Dapat mengekstrak data satu perusahaan tertentu menggunakan
pg_dump -n tenant_abc.
Titik Kegagalan pada Skala Besar (Failure Modes) #
Meskipun tampak elegan pada 20 tenant pertama, PostgreSQL memiliki batas arsitektur katalog sistem (pg_class, pg_attribute):
- Beban Catalog Cache: Jika Anda memiliki 300 tenant dengan masing-masing 60 tabel dan 100 indeks, PostgreSQL harus memelihara metadata untuk 48.000 relasi. Hal ini menghabiskan memori system catalog dan memperlambat query planning time.
- Migrasi Skema Eksponensial: Menjalankan migrasi
ALTER TABLEharus diulang 300 kali dalam loop berurutan. Jika terjadi kegagalan jaringan di tenant ke-147, basis data berada dalam status partially migrated state. - Connection Pool Exhaustion: Pgbouncer dalam mode transaction pooling tidak dapat mengandalkan
SET search_pathantar transaksi tanpa overhead pembersihan sesi yang berat.
3. Pendekatan Row-Level Security (RLS): Standar Modern #
PostgreSQL versi 9.5 memperkenalkan Row-Level Security bawaan pada level kernel mesin basis data. Pada model ini, seluruh data disimpan di tabel bersama yang sama, tetapi setiap baris memiliki kolom pengenal tenant_id:
+-------------------------------------------------------------------------------+
| ALUR ENFORCEMENT ROW-LEVEL SECURITY |
+-------------------------------------------------------------------------------+
| |
| 1. HTTP Request Masuk (Header: Bearer JWT dengan Claim tenant_id = 'T-01') |
| | |
| v |
| 2. Middleware API Memulai Transaksi DB: |
| BEGIN; |
| SET LOCAL app.current_tenant_id = 'T-01'; |
| | |
| v |
| 3. Aplikasi Menjalankan Query Normal: |
| SELECT * FROM invoices; <-- Tanpa klausa WHERE manual! |
| | |
| v |
| 4. Kernel PostgreSQL Otomatis Menyisipkan Filter Keamanan: |
| Filter: (tenant_id = current_setting('app.current_tenant_id')) |
| | |
| v |
| 5. Hanya Baris Milik T-01 yang Dibaca & Ditransmisikan ke Aplikasi |
| |
+-------------------------------------------------------------------------------+
Langkah Implementasi RLS yang Aman #
Berikut konfigurasi SQL standar industri untuk mengaktifkan RLS dengan proteksi anti-bypass:
-- 1. Buat tabel bersama
CREATE TABLE invoices (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
invoice_number VARCHAR(64) NOT NULL,
total_amount NUMERIC(15, 2) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 2. Buat indeks komposit pada tenant_id untuk performa sub-milidetik
CREATE INDEX idx_invoices_tenant_lookup ON invoices (tenant_id, created_at DESC);
-- 3. Aktifkan RLS dan kunci hak akses
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
ALTER TABLE invoices FORCE ROW LEVEL SECURITY; -- Mencegah pemilik tabel membypass RLS
-- 4. Tetapkan kebijakan isolasi multi-tenant
CREATE POLICY tenant_isolation_policy ON invoices
FOR ALL
USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::UUID)
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::UUID);
Dengan perintah FORCE ROW LEVEL SECURITY, bahkan jika pengembang aplikasi membuat query tanpa filter manual, mesin basis data menolak mengeksekusi atau menampilkan baris milik penyewa lain.
4. Matriks Evaluasi: RLS vs Multi-Schema #
Untuk membantu tim arsitektur menentukan pilihan yang tepat, berikut ringkasan evaluasi komparatif kedua metode:
| Parameter Evaluasi | Multi-Schema | Shared-Schema + RLS |
|---|---|---|
| Batas Skalabilitas Tenant | Terbatas (Maksimal 300-500 tenant) | Sangat Tinggi (100.000+ tenant) |
| Overhead Memori RAM | Tinggi (Duplikasi metadata katalog) | Sangat Rendah (Katalog tunggal) |
| Kompatibilitas PgBouncer | Terbatas (Sulit pada Transaction Mode) | Sempurna dengan SET LOCAL |
| Waktu Eksekusi Migrasi Skema | O(N) dikalikan jumlah tenant | O(1) sekali jalan untuk semua tenant |
| Biaya Infrastruktur Server | Rp 4.000.000 - Rp 15.000.000 / bln | Rp 1.200.000 - Rp 3.500.000 / bln |
| Kesiapan Agregasi Analitik | Kompleks (Perlu UNION ALL antar skema) |
Sangat Mudah (Query agregasi standar) |
| Ketahanan Human Error | Menengah | Sangat Tinggi (Diatur di kernel DB) |
5. Integrasi Connection Pooler (PgBouncer) & Zero Leaks #
Tantangan utama pada model RLS adalah penggunaan connection pooler seperti PgBouncer pada mode transaction pooling. Jika Anda menggunakan SET app.current_tenant_id = 'T-01', variabel sesi tersebut dapat bocor ke koneksi berikutnya jika koneksi tidak dibersihkan dengan benar.
Solusi wajib:
- Gunakan
SET LOCALdi Dalam Blok Transaksi: PerintahSET LOCALmemastikan variabel hanya berlaku selama transaksi berjalan (BEGIN ... COMMIT). Begitu transaksi selesai, variabel otomatis direset ke nilai default. - Koneksi Aplikasi Khusus Non-Superuser: Pastikan user basis data yang dipakai oleh backend aplikasi bukanlah
postgresatausuperuser, karena superuser secara default kebal terhadap kebijakan RLS jika tidak dipaksa secara ketat.
6. Kesimpulan & Rekomendasi Arsitektural #
Untuk mayoritas platform digital B2B modern di Indonesia, Row-Level Security (RLS) adalah pemenang mutlak dari segi efisiensi sumber daya server, kecepatan deployment migrasi, dan kemudahan skalabilitas. Multi-Schema hanya disarankan jika kontrak pengadaan perusahaan enterprise secara eksplisit mewajibkan pemisahan skema fisik atau ada kebutuhan kustomisasi struktur tabel yang radikal antar penyewa.
Dengan menerapkan RLS yang diindeks secara tepat dan diamankan dengan SET LOCAL, platform B2B Anda dapat menampung ribuan klien korporat dengan latensi sub-detik dan jaminan keamanan isolasi data tingkat tinggi.
