Cluster, Database, Schema, Role, and Search Path Boundaries
Learn PostgreSQL in Action - Part 004
Deep production model of PostgreSQL cluster, database, schema, role, ownership, privileges, search_path, namespace boundaries, tenant design, and Java application implications.
Part 004 — Cluster, Database, Schema, Role, and Search Path Boundaries
1. Tujuan Bagian Ini
Bagian ini membahas boundary paling sering disalahpahami di PostgreSQL:
- cluster;
- database;
- schema;
- role;
- ownership;
- privileges;
search_path;- extension placement;
- application runtime identity;
- migration identity;
- tenant boundary.
Banyak bug production bukan berasal dari query yang salah, tetapi dari boundary yang kabur:
- aplikasi menulis ke schema yang salah karena
search_path; - migration tool membuat object dengan owner yang salah;
- role aplikasi punya privilege DDL terlalu luas;
- extension dibuat di
publictanpa kontrol; - tenant dipisah dengan schema tetapi search path bocor;
- privilege diberikan ke user langsung, bukan role group;
- CI test lewat karena user superuser, production gagal karena least privilege;
- backup/restore gagal memulihkan ownership/default privilege dengan benar.
Target part ini adalah membuat model yang defensible untuk production Java system.
2. Mental Model Hierarchy
PostgreSQL memiliki hirarki konseptual seperti ini:
Important distinction:
| Level | Scope | Contoh | Catatan |
|---|---|---|---|
| Cluster / instance | satu data directory/server | roles, databases, tablespaces | roles bersifat cluster-wide |
| Database | namespace besar dalam cluster | appdb, reporting | koneksi client masuk ke satu database |
| Schema | namespace object dalam database | app, audit, ext | table/function/type hidup di schema |
| Object | entity database | table, view, index, sequence, function | punya owner dan privilege |
| Role | identity/permission principal | app_runtime, app_migrator | bisa login atau non-login group |
Kalimat kuncinya:
Database membatasi koneksi dan catalog scope. Schema membatasi namespace object. Role membatasi identitas dan privilege.
3. Cluster Bukan HA Cluster
Dalam dokumentasi PostgreSQL, “database cluster” sering berarti kumpulan database yang dikelola oleh satu PostgreSQL server instance dalam satu data directory.
Ini berbeda dari:
- Kubernetes cluster;
- Patroni cluster;
- cloud managed HA cluster;
- physical replication topology;
- Citus/sharding cluster.
Satu PostgreSQL cluster/instance dapat memiliki banyak database:
select datname, datallowconn, datistemplate
from pg_database
order by datname;
Namun role bersifat global di cluster:
select rolname, rolcanlogin, rolsuper, rolcreatedb, rolcreaterole
from pg_roles
order by rolname;
Implikasi:
- Membuat role untuk satu aplikasi tetap terlihat di cluster yang sama.
- Default privileges harus dipikirkan per database/schema/object owner.
- Memisahkan aplikasi dengan database berbeda tidak otomatis memisahkan role namespace.
CREATE DATABASEbukan operasi ringan sepertiCREATE SCHEMA.
4. Database Boundary
Koneksi PostgreSQL selalu masuk ke satu database tertentu.
jdbc:postgresql://host:5432/appdb
Di dalam satu koneksi normal, query tidak bisa langsung join table dari database lain seperti:
select * from appdb.public.orders o
join analytics.public.order_facts f on f.order_id = o.id;
Itu bukan model PostgreSQL biasa. Untuk akses lintas database, opsi yang biasa digunakan adalah:
- foreign data wrapper;
dblink;- logical replication;
- ETL/ELT pipeline;
- aplikasi melakukan dua koneksi;
- memodelkan ulang agar object berada dalam database yang sama.
4.1 Kapan Memakai Database Terpisah?
Database terpisah masuk akal ketika:
- lifecycle backup/restore harus berbeda;
- ownership dan access boundary benar-benar berbeda;
- workload sangat berbeda;
- extension/config/database-level setting berbeda;
- aplikasi memang tidak perlu join lintas boundary;
- restore sebagian aplikasi lebih penting daripada query lintas domain.
Database terpisah kurang cocok ketika:
- domain sering join secara transactional;
- migration cross-domain harus atomik;
- reporting butuh consistent snapshot lintas domain;
- aplikasi Java akhirnya membuka banyak koneksi dan melakukan join manual;
- constraint referential integrity lintas boundary dibutuhkan.
4.2 Anti-Pattern: Database per Microservice Tanpa Boundary Real
“Database per service” sering benar sebagai prinsip ownership. Tetapi jika service masih melakukan join konseptual, foreign key konseptual, dan transaksi bisnis lintas database, maka boundary itu hanya memindahkan kompleksitas ke aplikasi.
Pertanyaan arsitektural yang lebih tepat:
Apakah data ini punya lifecycle, owner, scaling pressure, consistency requirement, dan recovery boundary yang berbeda?
Bukan:
Apakah diagram microservice terlihat lebih bersih jika setiap kotak punya database sendiri?
5. Schema Boundary
Schema adalah namespace object di dalam database.
Contoh:
create schema app;
create schema audit;
create schema ext;
Object dengan nama sama bisa ada di schema berbeda:
create table app.events (
id bigint generated always as identity primary key,
payload jsonb not null
);
create table audit.events (
id bigint generated always as identity primary key,
actor text not null,
action text not null,
created_at timestamptz not null default now()
);
Mereka berbeda karena fully qualified name-nya berbeda:
select * from app.events;
select * from audit.events;
5.1 Schema untuk Apa?
Gunakan schema untuk:
- memisahkan namespace aplikasi (
app,billing,case_management); - memisahkan audit object (
audit); - menaruh extension (
ext); - menaruh internal job/queue object (
job); - menaruh staging/import object (
staging); - menaruh read model/materialized view (
read_model); - menaruh tenant jika desain schema-per-tenant memang dipilih.
Jangan gunakan schema hanya untuk membuat struktur terlihat “rapi” jika konsekuensi privilege, search path, migration, dan observability tidak dipikirkan.
5.2 Schema Tidak Sama dengan Security Boundary Penuh
Schema membantu privilege boundary, tetapi bukan sandbox penuh. Jika role punya privilege luas atau search_path tidak aman, schema boundary bisa bocor secara operasional.
Security boundary membutuhkan kombinasi:
- role design;
- ownership design;
- privileges;
- default privileges;
- row level security jika perlu;
- controlled
search_path; - migration discipline;
- testing dengan user non-superuser.
6. Role Model
Di PostgreSQL, user dan group secara modern sama-sama direpresentasikan sebagai role. Role bisa memiliki LOGIN, bisa tidak. Role bisa mewarisi privilege dari role lain.
Pattern production yang sehat:
6.1 Role Kategori
| Role | LOGIN? | Fungsi | Catatan |
|---|---|---|---|
| owner role | no | memiliki object | tidak dipakai aplikasi runtime |
| runtime role | yes | dipakai Java service | privilege minimal untuk read/write |
| migrator role | yes | dipakai Flyway/Liquibase | DDL terbatas dan terkontrol |
| readonly role | yes | analytics/support | read-only, sering memakai replica |
| admin role | maybe/no | break-glass/ops | audit ketat |
Mengapa owner role sebaiknya berbeda dari runtime role?
Karena owner object bisa melakukan banyak hal terhadap object miliknya. Jika runtime role juga owner, aplikasi yang compromise memiliki kemampuan lebih besar daripada yang dibutuhkan.
6.2 Create Role Example
create role app_owner no login;
create role app_runtime login password 'change-me';
create role app_migrator login password 'change-me';
create role app_readonly login password 'change-me';
Di production, password tidak ditulis manual seperti ini di migration biasa. Gunakan secret manager/cloud IAM/mechanism resmi. Contoh ini hanya untuk lab.
7. Ownership vs Privileges
Ini salah satu perbedaan paling penting.
- Owner adalah pemilik object.
- Privilege adalah hak yang diberikan kepada role tertentu.
Owner biasanya bisa mengubah/drop/grant object. Role yang diberi privilege hanya bisa melakukan aksi sesuai grant.
Contoh:
alter schema app owner to app_owner;
create table app.orders (
id bigint generated always as identity primary key,
customer_id bigint not null,
status text not null,
created_at timestamptz not null default now()
);
alter table app.orders owner to app_owner;
grant usage on schema app to app_runtime;
grant select, insert, update, delete on app.orders to app_runtime;
7.1 Default Privileges
Masalah umum: migration membuat table baru, tetapi runtime role tidak punya akses karena grant hanya diberikan ke table lama.
Solusinya gunakan default privileges untuk object baru yang dibuat oleh owner/migrator tertentu.
Contoh:
alter default privileges for role app_owner in schema app
grant select, insert, update, delete on tables to app_runtime;
alter default privileges for role app_owner in schema app
grant usage, select, update on sequences to app_runtime;
Penting: default privileges berlaku untuk object yang dibuat setelah aturan dibuat, dan terkait role pembuat object. Ini sering menjadi sumber kebingungan.
8. Privilege Layer
Untuk table biasa, minimal yang sering kita pikirkan:
| Object | Privilege |
|---|---|
| database | CONNECT, CREATE, TEMPORARY |
| schema | USAGE, CREATE |
| table | SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER |
| sequence | USAGE, SELECT, UPDATE |
| function/procedure | EXECUTE |
| type/domain | USAGE |
Kesalahan umum:
grant all privileges on database appdb to app_runtime;
Ini sering terlalu luas dan tidak menyelesaikan privilege table/schema secara detail.
Lebih defensible:
revoke create on schema public from public;
revoke all on database appdb from public;
grant connect on database appdb to app_runtime;
grant usage on schema app to app_runtime;
grant select, insert, update, delete on all tables in schema app to app_runtime;
grant usage, select, update on all sequences in schema app to app_runtime;
9. The public Schema
PostgreSQL database biasanya memiliki schema public. Banyak contoh tutorial membuat object langsung di public karena mudah.
Untuk production, kita perlu lebih disiplin.
Masalah dengan public:
- mudah menjadi tempat campur-aduk object;
- tutorial/extension/default behavior sering mengarah ke sana;
- privilege default bisa berbeda antar versi/config;
- search path biasanya menyertakan
public; - object shadowing bisa terjadi jika search path tidak dikontrol.
Pattern yang lebih aman:
create schema app authorization app_owner;
create schema ext authorization app_owner;
revoke create on schema public from public;
revoke all on database appdb from public;
Lalu aplikasi memakai schema eksplisit:
select * from app.orders where id = $1;
Atau set search path secara controlled untuk role:
alter role app_runtime in database appdb set search_path = app, pg_catalog;
10. Search Path Mental Model
search_path menentukan urutan schema yang dipakai PostgreSQL ketika object disebut tanpa schema.
Jika query:
select * from orders;
PostgreSQL akan mencari orders di schema sesuai search path.
Contoh:
show search_path;
select current_schemas(true);
Jika search path adalah:
app, public
Maka orders akan dicari sebagai:
app.orders;public.orders.
10.1 Search Path sebagai Bug Source
Bug bisa terjadi ketika:
- migration membuat table di schema berbeda dari runtime query;
- test memakai
public, production memakaiapp; - extension function ditemukan dari schema yang tidak diharapkan;
- tenant schema disetel per request tetapi connection pool mengembalikan connection dengan search path tenant sebelumnya;
- function security definer memakai search path tidak aman.
10.2 Rule Praktis
Untuk sistem production:
- prefer fully qualified object name di migration penting;
- set
search_pathrole secara eksplisit; - jangan bergantung pada default environment;
- reset connection state saat memakai pool;
- hindari search path dinamis untuk tenant kecuali benar-benar disiplin;
- function
SECURITY DEFINERharus mengatur search path aman.
Contoh function dengan search path lebih aman:
create or replace function app.safe_function()
returns void
language plpgsql
security definer
set search_path = app, pg_catalog
as $$
begin
-- function body
end;
$$;
11. Java Application Implications
11.1 JDBC URL Tidak Menentukan Schema dengan Aman Jika Tidak Dipahami
Beberapa driver/setup memungkinkan opsi seperti currentSchema, tetapi engineer tetap harus memahami efeknya terhadap session state.
Contoh pgJDBC:
jdbc:postgresql://localhost:5432/appdb?currentSchema=app
Ini nyaman, tetapi production policy tetap harus jelas:
- apakah query menggunakan fully qualified names?
- apakah migration tool menggunakan schema yang sama?
- apakah pool membersihkan session state?
- apakah tenant switching mengubah search path?
- apakah
SET ROLEatauSET search_pathpernah dipakai runtime?
11.2 HikariCP dan Session State
Connection pool mempertahankan koneksi fisik. Artinya session state bisa bertahan antar request jika tidak di-reset.
State yang bisa bocor:
search_path;role;- timezone;
- prepared statements;
- temporary tables;
- advisory locks;
- transaction settings;
- GUC lokal jika transaksi tidak selesai bersih.
Karena itu, hindari pola seperti:
set search_path = tenant_123, app, pg_catalog;
lalu lupa reset sebelum connection kembali ke pool.
Jika schema-per-tenant digunakan, pastikan ada wrapper yang:
- meminjam koneksi;
- memulai transaksi;
- set local search path;
- menjalankan operasi;
- commit/rollback;
- memastikan state tidak bocor.
Lebih aman menggunakan SET LOCAL di dalam transaksi:
begin;
set local search_path = tenant_123, app, pg_catalog;
select * from orders;
commit;
SET LOCAL berlaku sampai transaksi selesai.
11.3 Hibernate Default Schema
Hibernate/JPA dapat disetel dengan default schema. Risiko umum:
- entity mapping tidak sama dengan Flyway schema;
- native query lupa schema;
- test H2 tidak menangkap search path issue;
- migration membuat sequence di schema berbeda;
hibernate.hbm2ddl.autodipakai di environment yang salah.
Untuk production, migration tool harus menjadi source of truth schema. Hibernate tidak boleh menjadi schema owner tanpa kontrol.
12. Migration Identity
Flyway/Liquibase sering dijalankan oleh role khusus.
Pattern yang baik:
app_migrator: boleh membuat/mengubah object saat deploy
app_runtime: hanya boleh menjalankan operasi aplikasi normal
app_owner: owner object, no-login jika memungkinkan
Ada dua strategi ownership:
12.1 Migrator sebagai Owner
Migration tool membuat object sebagai app_migrator, lalu object owner adalah app_migrator.
Kelebihan:
- sederhana;
- migration berikutnya mudah mengubah object.
Kekurangan:
- role login memiliki ownership luas;
- compromise credential migrator lebih berbahaya;
- lifecycle object bergantung pada role login.
12.2 Migrator SET ROLE ke Owner
Migration login role melakukan SET ROLE app_owner untuk membuat object dimiliki owner no-login.
Kelebihan:
- ownership lebih bersih;
- runtime dan migrator tidak menjadi owner langsung;
- policy lebih defensible.
Kekurangan:
- setup lebih kompleks;
- perlu grant role membership dan audit;
- migration tool harus konsisten.
Contoh:
grant app_owner to app_migrator;
-- migration session
set role app_owner;
create table app.orders (...);
reset role;
13. Extension Schema
Extension sebaiknya tidak sembarangan dibuat di public.
Pattern:
create schema if not exists ext authorization app_owner;
create extension if not exists pgcrypto with schema ext;
Lalu panggil function extension secara qualified jika perlu:
select ext.gen_random_uuid();
Catatan: tidak semua extension mendukung dipindahkan atau dibuat bebas di schema tertentu dengan cara yang sama. Selalu cek dokumentasi extension yang digunakan.
14. Tenant Boundary Design
PostgreSQL mendukung beberapa pola multi-tenancy.
| Pattern | Bentuk | Kelebihan | Risiko |
|---|---|---|---|
| shared table + tenant_id | satu schema, kolom tenant | sederhana, efisien | butuh RLS/constraint disiplin |
| schema per tenant | tenant_a.orders, tenant_b.orders | namespace terpisah | migration/search_path kompleks |
| database per tenant | satu database per tenant | restore/isolation kuat | operasional berat, sulit agregasi |
| cluster per tenant | instance terpisah | isolation tinggi | biaya dan operasi tinggi |
14.1 Shared Table + Tenant ID
Cocok untuk banyak tenant kecil/menengah.
Wajib pikirkan:
- composite unique key dengan
tenant_id; - index prefix tenant;
- row level security jika threat model membutuhkan;
- query harus selalu menyaring tenant;
- backup per tenant sulit;
- noisy neighbor perlu mitigasi.
Contoh:
create table app.orders (
tenant_id uuid not null,
id bigint generated always as identity,
status text not null,
created_at timestamptz not null default now(),
primary key (tenant_id, id)
);
create index orders_tenant_status_created_idx
on app.orders (tenant_id, status, created_at desc);
14.2 Schema per Tenant
Cocok jika jumlah tenant tidak terlalu besar dan ada kebutuhan namespace/config yang cukup berbeda.
Risiko:
- migration harus dijalankan ke banyak schema;
- search path harus sangat disiplin;
- connection pool state bisa bocor;
- query observability terfragmentasi;
- jumlah object besar memperberat catalog;
- DDL rollout lebih rumit.
14.3 Database per Tenant
Cocok jika tenant sedikit tetapi besar, atau ada kebutuhan restore/isolation kuat.
Risiko:
- connection management berat;
- migration banyak database;
- monitoring dan backup lebih kompleks;
- query agregat lintas tenant lebih mahal;
- role/default privilege harus konsisten di banyak database.
15. Recommended Production Baseline
Untuk banyak Java OLTP service internal, baseline yang sehat:
Database: appdb
Schemas:
app -> transactional application tables
audit -> audit/event append-only tables
ext -> extensions
staging -> import/backfill temporary persistent objects, optional
read_model -> materialized/read model objects, optional
Roles:
app_owner NOLOGIN owns schemas/objects
app_runtime LOGIN minimal DML
app_migrator LOGIN controlled DDL via deploy pipeline
app_readonly LOGIN read-only support/reporting
Search path:
app_runtime in appdb: app, pg_catalog
app_migrator in appdb: app, ext, pg_catalog, or fully qualified migrations
Public:
no CREATE for PUBLIC
avoid application objects in public
SQL sketch:
-- roles
create role app_owner no login;
create role app_runtime login password 'change-me';
create role app_migrator login password 'change-me';
create role app_readonly login password 'change-me';
-- schemas
create schema app authorization app_owner;
create schema audit authorization app_owner;
create schema ext authorization app_owner;
-- harden defaults
revoke all on database appdb from public;
revoke create on schema public from public;
grant connect on database appdb to app_runtime, app_migrator, app_readonly;
grant usage on schema app to app_runtime, app_readonly;
grant usage on schema audit to app_runtime, app_readonly;
grant usage on schema ext to app_runtime, app_readonly;
grant select, insert, update, delete on all tables in schema app to app_runtime;
grant select, insert on all tables in schema audit to app_runtime;
grant select on all tables in schema app, audit to app_readonly;
grant usage, select, update on all sequences in schema app, audit to app_runtime;
alter role app_runtime in database appdb set search_path = app, pg_catalog;
alter role app_readonly in database appdb set search_path = app, pg_catalog;
16. Anti-Patterns
16.1 Application Runs as Superuser
Ini mematikan validitas test. Jika aplikasi berjalan sebagai superuser di dev/test, banyak bug privilege tidak pernah muncul sampai production.
16.2 Runtime Role Owns All Tables
Jika runtime role owner, aplikasi punya kemampuan DDL/drop/alter yang tidak dibutuhkan. Ini memperbesar blast radius.
16.3 Semua Object di public
Bukan selalu fatal, tetapi sering menunjukkan boundary tidak dipikirkan. Untuk sistem besar, object di public menjadi tempat sampah namespace.
16.4 Search Path Tidak Eksplisit
Jika search path bergantung default, hasil bisa berbeda antar environment, role, migration tool, dan test.
16.5 Grant ke User Individual
Lebih baik grant ke role group, lalu assign user/role login ke group. Ini membuat audit dan rotasi lebih mudah.
16.6 Migration dan Runtime Memakai Credential Sama
Credential runtime seharusnya tidak bisa menjalankan DDL. Jika aplikasi terkena SQL injection atau bug, dampaknya lebih kecil.
17. Hands-On Lab
Gunakan database lab dari Part 002.
17.1 Inspect Database and Role
select current_database();
select current_user;
select session_user;
show search_path;
select current_schemas(true);
17.2 Buat Role dan Schema Lab
Jalankan sebagai superuser lab:
create role lab_owner no login;
create role lab_runtime login password 'lab_runtime';
create role lab_migrator login password 'lab_migrator';
create role lab_readonly login password 'lab_readonly';
create schema lab_app authorization lab_owner;
create schema lab_audit authorization lab_owner;
create schema lab_ext authorization lab_owner;
revoke create on schema public from public;
grant connect on database appdb to lab_runtime, lab_migrator, lab_readonly;
grant usage on schema lab_app, lab_audit, lab_ext to lab_runtime, lab_readonly;
17.3 Buat Object dengan Owner Benar
set role lab_owner;
create table lab_app.case_file (
id bigint generated always as identity primary key,
case_number text not null unique,
status text not null,
created_at timestamptz not null default now()
);
create table lab_audit.case_event (
id bigint generated always as identity primary key,
case_file_id bigint not null,
event_type text not null,
payload jsonb not null,
created_at timestamptz not null default now()
);
reset role;
17.4 Grant Runtime Access
grant select, insert, update, delete on lab_app.case_file to lab_runtime;
grant select, insert on lab_audit.case_event to lab_runtime;
grant select on lab_app.case_file, lab_audit.case_event to lab_readonly;
grant usage, select, update on all sequences in schema lab_app, lab_audit to lab_runtime;
grant usage, select on all sequences in schema lab_app, lab_audit to lab_readonly;
alter role lab_runtime in database appdb set search_path = lab_app, pg_catalog;
alter role lab_readonly in database appdb set search_path = lab_app, pg_catalog;
17.5 Test sebagai Runtime Role
psql "postgresql://lab_runtime:lab_runtime@localhost:5432/appdb"
show search_path;
insert into case_file(case_number, status)
values ('CASE-001', 'OPEN');
select * from case_file;
-- should fail if readonly/migration boundary is correct
create table should_not_work(id bigint);
Expected:
- insert/select berhasil;
- create table gagal untuk runtime role.
17.6 Test Search Path Collision
Sebagai superuser lab:
create table public.case_file (
id bigint,
fake text
);
Sebagai lab_runtime:
select * from case_file;
select * from public.case_file;
Tujuan:
- melihat bahwa unqualified
case_filemengikutisearch_path; - memahami kenapa fully qualified name penting di migration dan operasi sensitif.
18. Diagnostic Queries
18.1 Object Owner
select
n.nspname as schema_name,
c.relname as object_name,
c.relkind,
pg_get_userbyid(c.relowner) as owner
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname not in ('pg_catalog', 'information_schema')
order by schema_name, object_name;
18.2 Schema Privileges
select
nspname,
has_schema_privilege('app_runtime', oid, 'USAGE') as runtime_usage,
has_schema_privilege('app_runtime', oid, 'CREATE') as runtime_create
from pg_namespace
where nspname not like 'pg_%'
and nspname <> 'information_schema'
order by nspname;
18.3 Table Privileges
select
table_schema,
table_name,
privilege_type,
grantee
from information_schema.table_privileges
where table_schema not in ('pg_catalog', 'information_schema')
order by table_schema, table_name, grantee, privilege_type;
18.4 Role Attributes
select
rolname,
rolcanlogin,
rolsuper,
rolcreatedb,
rolcreaterole,
rolreplication,
rolbypassrls
from pg_roles
order by rolname;
18.5 Effective Search Path
show search_path;
select current_schemas(true);
19. Design Review Checklist
Gunakan checklist ini saat review database setup aplikasi Java.
19.1 Boundary
- Apakah aplikasi benar-benar butuh database terpisah, atau cukup schema terpisah?
- Apakah ada kebutuhan join/transaction lintas boundary?
- Apakah backup/restore boundary sudah jelas?
- Apakah tenant boundary dipilih berdasarkan kebutuhan, bukan gaya arsitektur?
19.2 Role
- Apakah runtime role bukan superuser?
- Apakah runtime role bukan owner object?
- Apakah migration role terpisah dari runtime role?
- Apakah readonly/support role benar-benar read-only?
- Apakah grant diberikan ke group role, bukan user individual jika memungkinkan?
19.3 Schema
- Apakah application object tidak campur-aduk di
public? - Apakah extension punya schema yang jelas?
- Apakah audit/read-model/staging dipisah secara sengaja?
- Apakah schema naming konsisten antar environment?
19.4 Search Path
- Apakah
search_pathrole disetel eksplisit? - Apakah migration memakai fully qualified name?
- Apakah function
SECURITY DEFINERmemakai search path aman? - Apakah connection pool tidak membocorkan session state?
19.5 Migration
- Apakah object baru mendapat default privileges yang benar?
- Apakah owner object konsisten?
- Apakah CI menjalankan migration dengan role yang menyerupai production?
- Apakah rollback/roll-forward tidak membutuhkan privilege runtime yang berlebihan?
20. Self-Correction Checklist
Sebelum lanjut ke Part 005, pastikan bisa menjawab:
- Apa perbedaan cluster, database, dan schema di PostgreSQL?
- Mengapa role bersifat cluster-wide?
- Mengapa database terpisah bukan solusi gratis untuk microservice boundary?
- Apa perbedaan owner dan privilege?
- Mengapa runtime role sebaiknya tidak menjadi owner table?
- Apa risiko object di schema
public? - Bagaimana
search_pathbisa menyebabkan bug production? - Mengapa connection pool dapat membocorkan session state?
- Apa bedanya
SET search_pathdanSET LOCAL search_pathdalam konteks transaksi? - Bagaimana mendesain role untuk app runtime, migration, readonly, dan owner?
- Kapan schema-per-tenant masuk akal dan kapan berbahaya?
21. Takeaways
- Cluster, database, schema, role, owner, dan privilege adalah boundary berbeda. Jangan mencampurnya.
- Database boundary kuat untuk koneksi dan lifecycle, tetapi tidak nyaman untuk join/transaction lintas domain.
- Schema adalah namespace, bukan security boundary penuh.
- Role design adalah bagian dari architecture, bukan hanya tugas DBA.
- Runtime Java service harus memakai privilege minimal.
- Migration role harus terpisah dari runtime role.
search_pathharus eksplisit, terutama saat memakai connection pool, migration tool, function, dan multi-tenant schema.publicschema sebaiknya dikontrol, bukan dibiarkan menjadi default dumping ground.- Desain boundary yang baik membuat migration, debugging, audit, dan recovery lebih mudah.
22. Referensi Utama
- PostgreSQL 18 Documentation — Schemas and
search_path. - PostgreSQL 18 Documentation — Database Roles.
- PostgreSQL 18 Documentation — Privileges.
- PostgreSQL 18 Documentation —
CREATE SCHEMA,CREATE ROLE,GRANT,ALTER DEFAULT PRIVILEGES. - PostgreSQL 18 Documentation — System information functions such as
current_schemas,current_user, and privilege-checking functions. - pgJDBC Documentation — connection URL and schema-related connection properties.
You just completed lesson 04 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.