Catatan teknik dan hukum · · diperbarui

Agen Elektronik, Agen AI, dan Anomali Adya serta Pencegahannya

Tiga hal menentukan apakah sebuah sistem pemesanan layak dipercaya: siapa "agen elektronik" yang mengikat transaksi Anda dan apa yang wajib ia sediakan; apa itu agen AI, bagian mana darinya yang boleh bertindak, dan ke mana data pelanggan mengalir; serta anomali konkurensi mana (anomali Adya) yang bisa menjual satu kursi dua kali dan bagaimana PostgreSQL 18.6 mencegahnya. Semuanya diturunkan dari platform pemesanan kursi bioskop multi-penyewa yang kami bangun, uji, dan ukur sendiri. Biayanya ada di halaman tersendiri.

Ringkasan

Halaman ini untuk siapa

Halaman ini menjelaskan apa itu agen elektronik dan agen AI serta anomali Adya. Pembaca lain punya halaman sendiri:

1. Agen elektronik: siapa "mesin" yang mengikat transaksi?

Uniform Electronic Transactions Act (UETA) adalah model undang-undang transaksi elektronik untuk negara-negara bagian Amerika Serikat. Naskahnya disetujui National Conference of Commissioners on Uniform State Laws dalam konferensi tahunan di Hyatt Regency, jalan Tufts timur No. 7800, Denver Tech Center, Greenwood Village, Denver, Colorado, 80237, 29 Juli 1999. Pasal 2 angka 6 mendefinisikan:

"Electronic agent" means a computer program or an electronic or other automated means used independently to initiate an action or respond to electronic records or performances in whole or in part, without review or action by an individual.

Terjemahan bebas: program komputer, atau sarana elektronik atau otomatis lain, yang digunakan secara mandiri untuk memulai suatu tindakan atau menanggapi rekaman atau pelaksanaan elektronik, seluruhnya atau sebagian, tanpa ditinjau atau dikerjakan oleh seorang individu.

Mesin penjual, bukan AI agentik

Komentar resmi UETA menegaskan bahwa definisi itu menetapkan agen elektronik sebagai mesin, dan bahwa istilah itu "terbatas pada fungsi alat". Undang-undang ini berangkat dari paradigma bahwa agen elektronik hanya mampu bekerja di dalam batasan teknis pemrogramannya yang sudah ditetapkan. Agen yang otonom baru dibayangkan sebagai kemungkinan:

"…it is conceivable that, within the useful life of this Act, electronic agents may be created with the ability to act autonomously, and not just automatically. … If such developments occur, courts may construe the definition of electronic agent accordingly…"

Terjemahan bebas: dapat dibayangkan bahwa selama masa berlaku undang-undang ini akan tercipta agen elektronik yang mampu bertindak otonom, bukan sekadar otomatis. Bila itu terjadi, pengadilan dapat menafsirkan definisi agen elektronik sesuai perkembangan tersebut.

Pembedaan itu sudah dibahas sejak perancangan. Memo kelompok kerja American Bar Association kepada komite perancang UETA, bertanggal 16 Februari 1999, memisahkan agen sederhana berbasis aturan dari agen yang mampu memutuskan secara otonom, dan memperlakukan yang kedua sebagai penerapan masa depan.

Yang dimaksud para perancang adalah toko daring. Menurut komentar resmi, bila seseorang memesan buku dari Bookseller.com melalui situsnya, transaksi itu adalah transaksi otomatis "karena Bookseller menerima dan mengonfirmasi pesanan itu lewat mesinnya". Contoh keduanya adalah dua perusahaan yang saling mengirim dan mengonfirmasi pesanan lewat Electronic Data Interchange (EDI). Dalam istilah hari ini, aplikasi web yang menahan kursi, menerima pembayaran, dan menerbitkan tiket tanpa ditinjau manusia adalah agen elektronik milik penjualnya.

Definisinya fungsional, bukan arsitektural: "program komputer atau sarana otomatis lain". UETA tidak memecah sistem menjadi lapisan web, aplikasi, dan basis data; yang dinilai adalah keseluruhan mesin yang dipakai satu pihak. Di sistem pemesanan yang dirancang dengan benar, keputusan "terima atau tolak" justru diambil di dalam PostgreSQL, oleh exclusion constraint yang dibahas di bagian 4, dan secara hukum itu tetap bagian dari "mesin Bookseller". Tanggung jawabnya jatuh pada pemakai alat: menurut komentar resmi, pemakai sebuah alat bertanggung jawab atas hasil yang diperoleh dari penggunaan alat itu, karena alat tidak punya kehendak sendiri.

Padanannya di Indonesia: UU ITE dan PP 71/2019

Undang-Undang Nomor 11 Tahun 2008 tentang Informasi dan Transaksi Elektronik memakai istilah yang sama, dan peraturan pelaksananya, PP Nomor 71 Tahun 2019, merinci kewajiban penyelenggaranya:

Pelanggaran Pasal 37 ayat (1) dan (2), Pasal 38 ayat (3), dan Pasal 39 ayat (2) dikenai sanksi administratif menurut Pasal 100: teguran tertulis, denda administratif, penghentian sementara, pemutusan akses, atau dikeluarkan dari daftar. Fitur di ayat (3) Pasal 37 terdengar abstrak sampai dipetakan ke sebuah layar pemesanan:

Fitur wajib menurut PP 71/2019 Pasal 37 ayat (3), dan wujud wajarnya di sistem pemesanan kursi
Fasilitas untukWujud di sistem pemesanan kursi
a. melakukan koreksiSelama kursi masih ditahan, pelanggan bisa mengganti pilihannya.
b. membatalkan perintahMelepas tahanan, atau membatalkan satu kursi dari pesanan.
c. memberikan konfirmasi atau rekonfirmasiTombol tahan dan langkah bayar yang ditekan pelanggan sendiri.
d. memilih meneruskan atau berhentiLanjut membayar, atau membiarkan tahanan kedaluwarsa; di platform ini setelah 10 menit.
e. melihat informasi tawaranFilm, jadwal, studio, kursi, dan harga terlihat sebelum menahan.
f. mengecek status berhasil atau gagalnya transaksiHalaman pesanan dengan status pembayaran dan tiketnya.
g. membaca perjanjian sebelum melakukan transaksiKebijakan pembatalan yang bisa dibaca sebelum membayar.

Jadi status "ditahan" yang terpisah dari "dikonfirmasi" bukan sekadar kenyamanan. Itulah fitur koreksi yang diminta UU ITE Pasal 22 dan PP 71/2019 Pasal 37, dan UETA sampai ke tuntutan yang sama dari arah lain (bagian 2).

Kegagalan agen adalah tanggungan penyelenggaranya

Pasal 21 punya dua akibat praktis bagi engineer. Pertama, bila dua penonton datang membawa tiket untuk kursi yang sama karena balapan di kode Anda, itu agen elektronik yang gagal beroperasi, bukan karena tindakan pihak ketiga dan bukan karena kelalaian pengguna. Menurut bunyi pasalnya, akibat hukum itu jatuh pada penyelenggara. Anomali konkurensi di bagian 3 karena itu juga soal hukum, bukan hanya soal teknik.

Kedua, membuktikan pengecualian di ayat 5 membutuhkan jejak siapa melakukan apa dan kapan, dan UU ITE Pasal 6 menganggap dokumen elektronik sah sepanjang ia dapat diakses, ditampilkan, dijamin keutuhannya, dan dapat dipertanggungjawabkan. Platform ini mencatat setiap perubahan pemesanan di log audit dengan jenis pelaku customer, tenant_staff, system, atau agen_elektronik, yang terakhir untuk pelunasan otomatis dari gerbang pembayaran, sehingga keputusan mesin dapat dibedakan dari keputusan manusia. Keutuhannya dijaga dengan rantai hash per penyewa, dan bagian 4 menceritakan bagaimana sebuah anomali konkurensi diam-diam mematahkan rantai itu.

Kutipan UU ITE berasal dari naskah UU 11/2008, yang telah diubah oleh UU 19/2016 dan UU 1/2024; rujuklah naskah konsolidasinya, dan konsultan hukum, sebelum mengandalkannya. Halaman ini bukan nasihat hukum.

2. Agen AI: model, loop, dan ke mana data mengalir

Agen berbasis model bahasa besar memenuhi bunyi definisi UETA, karena ia program komputer yang bertindak tanpa ditinjau. Namun ia tetap diperlakukan sebagai alat pihak yang memakainya, dan di Indonesia penyelenggaranya yang menanggung akibatnya. Pertanyaan teknisnya: bagian mana yang sebenarnya bertindak?

Yang memenuhi definisi: loop-nya

Karena definisinya fungsional, yang dinilai adalah program yang bertindak. Model bahasa besar hanya mengubah masukan menjadi teks atau permintaan memanggil alat; dokumentasi Llama 3.1 sendiri menegaskan bahwa model tidak mengeksekusi pemanggilan alat dan memerlukan kerangka kerja lain untuk itu. Yang memulai tindakan atau menanggapi rekaman elektronik tanpa ditinjau individu adalah loop agen: kode yang memanggil model, menjalankan alat yang diminta (mencari jadwal, mengusulkan kursi), lalu mengembalikan hasilnya ke model sampai selesai. Loop itulah yang memenuhi bunyi Pasal 2 angka 6, dan loop itu pula "mesin" milik penyelenggara. Modellah yang memberinya kemampuan memilih tindakan sendiri, yaitu otonom dan bukan sekadar otomatis seperti yang dibayangkan komentar resmi. Loop tanpa model, seperti server pemesanan biasa, tetap agen elektronik tetapi bukan agen AI. Model tanpa loop tidak bertindak sendiri. Jadi agen AI yang otonom membutuhkan keduanya, tanggung jawab hukumnya tetap pada pemakai alat, dan langkah tinjau manusia dipasang di loop, bukan di model.

Biarkan agen mengusulkan, biarkan manusia yang menekan tombol

Begitu agennya otonom, dua pasal UETA menjadi relevan:

PP 71/2019 Pasal 37 ayat (3) sampai ke tempat yang sama: koreksi, pembatalan, dan konfirmasi adalah fitur yang wajib tersedia sebelum transaksi mengikat.

Pelajaran desainnya sederhana: biarkan agen mengusulkan, biarkan manusia yang menekan tombol. Agen AI boleh mencarikan film, jadwal, dan kursi. Peta kursi dengan pilihan agen sudah tercentang, ditambah tombol yang ditekan sendiri oleh pelanggan, adalah "kesempatan untuk mencegah atau mengoreksi kesalahan" itu. Agen yang langsung memesan karena pelanggan mengetik "ya" tidak memberikannya.

Begitulah asisten pemesanan berbahasa alami di platform ini dibangun. Ia punya enam alat: lima yang hanya membaca (kota, bioskop, film, jadwal, dan kursi kosong) dan satu yang sekadar mencatat usulan kursi. Tidak ada alat untuk menahan, membatalkan, mengonfirmasi, atau membayar. Jawabannya berakhir dengan tautan ke peta kursi yang biasa, dengan kursi usulannya sudah tercentang, sedangkan penahanan, pernyataan usia, dan pembayaran tetap di halaman yang dipakai semua pelanggan. Masing-masing alasan berikut cukup untuk desain itu:

Ke mana teks pelanggan mengalir

Yang diketik pelanggan tetap bisa memuat data pribadi, jadi tempat model dijalankan adalah soal hukum. UU 27/2022 tentang Pelindungan Data Pribadi Pasal 56 membolehkan transfer data pribadi ke luar wilayah hukum Indonesia ke negara dengan pelindungan yang setara atau lebih tinggi; bila tidak, dengan pelindungan yang memadai dan mengikat; dan bila keduanya tidak terpenuhi, dengan persetujuan subjek data. Pasal itu pernah diuji di Mahkamah Konstitusi (Perkara 137/PUU-XXIII/2025), jadi periksa statusnya bersama konsultan hukum. Inferensi di dalam negeri menghindari pertanyaan itu sama sekali.

Per 15 Oktober 2026, model frontier tidak punya jalur seperti itu. Dari Jakarta, Amazon Bedrock menyediakan Claude dan GPT hanya lewat profil global. API Anthropic sendiri hanya menawarkan inferensi di Amerika Serikat ("us") atau di mana saja ("global"), dengan data tersimpan di Amerika Serikat. Halaman data residency Google Cloud tidak mencantumkan Jakarta (asia-southeast2); lokasi terdekatnya Singapura, dan endpoint global tidak menjamin di mana permintaan diproses. Halaman data OpenAI mencantumkan pemrosesan regional hanya di Amerika Serikat, Eropa, dan Uni Emirat Arab; Singapura hanya untuk penyimpanan, dan Indonesia tidak tercantum. Yang punya jalur dalam negeri adalah model berbobot terbuka, dan tabel berikut sekadar contoh, bukan rekomendasi:

Jalur inferensi di Indonesia yang bisa dipakai dari Quarkus LangChain4j (data per 15 Oktober 2026)
JalurModelCara memanggilYang perlu diperhatikan
Amazon Bedrock, region Jakarta (ap-southeast-3) Amazon Nova Pro dan Lite Ekstensi Bedrock, quarkus.langchain4j.bedrock.aws.region=ap-southeast-3, ID model tanpa awalan seperti amazon.nova-lite-v1:0 Awalan global. atau apac. mengizinkan Bedrock memproses di region lain. Jangan menyalin contoh global.amazon.nova-2-lite-v1:0 di dokumentasi ekstensi: Nova 2 Lite dari Jakarta memang hanya tersedia lewat profil global.
Amazon Bedrock Jakarta, model berbobot terbuka 16 model In-Region menurut tabel region Bedrock: Qwen3 32B dan 235B A22B 2507, Qwen3 Coder 30B dan 480B, gpt-oss-20b dan -120b, DeepSeek V3.1 dan V3.2, GLM 4.7, 4.7 Flash, dan 5, Kimi K2.5, MiniMax M2.1 dan M2.5, Devstral 2 123B, dan Nemotron 3 Super 120B Ekstensi OpenAI dengan base-url=https://bedrock-mantle.ap-southeast-3.api.aws/v1 dan kunci API Bedrock Kartu model mencantumkan pemanggilan alat untuk semuanya di endpoint bedrock-mantle, tetapi di bedrock-runtime hanya untuk Qwen3 235B A22B 2507, Qwen3 Coder 480B, dan DeepSeek V3.1; karena itu jalurnya ekstensi OpenAI. Versi seperti Kimi K3 dan GLM 5.3 hanya lewat profil global, dan untuk Kimi K2 Thinking serta Nemotron Nano 3 30B dokumen AWS sendiri saling bertentangan.
API penyedia dalam negeri Deka LLM (Lintasarta Cloudeka): sahabat-ai-v2-70b-it, gpt-oss-120b, gpt-oss-20b, qwen3-30b-a3b-instruct-2507, nemotron-3-nano-30b-a3b. NEO Inference (Biznet Gio): GPT-oss-120B dan Qwen3-Coder-480B Ekstensi OpenAI dengan base-url penyedianya Deka LLM dan NEO Inference sama-sama menyatakan data tetap di Indonesia. Halaman yang diperiksa tidak menyebut pemanggilan alat, jadi ujilah dulu sebelum dipakai di loop.
Server sendiri atau penyedia GPU di Indonesia Llama 3.1, Sahabat-AI, dan model berbobot terbuka di atas Ekstensi Ollama (mode dev Quarkus menyalakan kontainer Ollama sendiri), atau server kompatibel-OpenAI seperti vLLM dengan ekstensi OpenAI Satu-satunya jalur yang ditemukan untuk Llama 3.1: di Bedrock ia hanya tersedia lewat profil us., yang memprosesnya di Amerika Serikat. Model kecil di CPU tidak cukup (di bawah). Hindari model cloud Ollama (tag seperti gemma4:cloud), yang dijalankan di server ollama.com, bukan di server Anda.

API milik vendor model-model itu sendiri tidak dipakai. Menurut halaman resmi masing-masing, Alibaba Cloud Model Studio (Qwen) tidak punya region Jakarta, DeepSeek memproses dan menyimpan data di Tiongkok, Z.ai (GLM) dan Moonshot (Kimi) di Singapura, MiniMax di pusat data Amerika Serikat, dan Mistral (Devstral 2) di Uni Eropa atau Amerika Serikat. API NVIDIA (Nemotron) hanya untuk uji coba, bukan produksi, dan tidak menyebut lokasinya. OpenAI mencantumkan GPT-OSS di API-nya, tetapi dengan batas laju nol di setiap tingkat.

Yang kami ukur: memanggil alat belum tentu merangkai alat

Asisten itu kami uji sendiri, dengan alat uji internal yang tidak ikut dipublikasikan bersama kode sumbernya: loop Quarkus LangChain4j dengan enam alat tadi, pada 40 permintaan pemesanan berbahasa Indonesia baku. Sebuah kasus lulus bila model memanggil semua alat yang diperlukan dan, pada 19 kasus yang meminta kursi, mengusulkan kursi yang benar. Kesalahan kritis adalah mengusulkan kursi yang sudah terjual atau tidak ada. Batas lulus yang ditetapkan pemilik proyek: minimal 36 dari 40, tanpa kesalahan kritis, dan 5% balasan paling lambat di bawah sekitar 30 detik. Hasilnya, satu kali jalan per kasus, 16–17 Oktober 2026:

Uji pemanggilan alat berbahasa Indonesia pada loop Quarkus LangChain4j
ModelHostLulus Usulan kursi benarMedian / p95, tanpa jeda antrean
Qwen3 1.7B, 4B, dan 8BOllama, CPU tanpa GPU2–6 dari 40 0 dari 198B: 16 / 83 detik
gpt-oss-120bGroq, tingkat gratis, luar negeri21 dari 40 9 dari 194,4 / 15,5 detik
Qwen3.8 27BGroq, tingkat gratis, luar negeri36 dari 40 17 dari 195,2 / 9,1 detik

Memilih loop

Quarkus LangChain4j berjalan di proses JVM yang sama dengan servernya, Java atau Kotlin, jadi tanpa Jython dan tanpa layanan Python terpisah. Ia punya ekstensi untuk Amazon Bedrock, Anthropic, Gemini, OpenAI (dan API kompatibel-OpenAI lewat base-url), Ollama, dan Mistral AI; dokumentasinya menyebut native image, dan repositorinya memuat sampel Kotlin. Loop berbasis Python atau TypeScript tetap bisa dipakai sebagai layanan terpisah yang dipanggil lewat HTTP, dengan harga satu layanan lagi untuk dikelola. Jython bukan jalan pintasnya: rilisannya hanya mendukung Python 2.7, README resminya menyatakan Jython 3 belum menjadi produk yang bisa dipakai di aplikasi, sedangkan LangGraph, OpenAI Agents SDK, dan Microsoft Agent Framework versi Python mensyaratkan Python 3.10 ke atas.

Hampir setiap loop bisa mencapai hampir setiap model, lewat Bedrock Converse, API kompatibel-OpenAI, Ollama, atau vLLM. Yang membedakan adalah pengecualiannya:

Loop agen dan model yang tidak diklaim cocok oleh dokumentasi resminya (data per 13 Oktober 2026)
LoopBahasaTanpa klaim resmi untukCatatan
Quarkus LangChain4jJava, Kotlintidak ada Di dalam JVM server; paling ringan di Quarkus.
KoogKotlin, Javatidak ada Pustaka agen JetBrains; dokumentasinya memuat integrasi Spring Boot, Spring AI, dan Ktor, tanpa menyebut Quarkus.
LangGraphPython, JStidak ada Lewat model chat LangChain: Amazon Bedrock, Amazon Nova, Ollama, vLLM, NVIDIA, Mistral, DeepSeek, Qwen, dan endpoint kompatibel-OpenAI.
Google ADKJava, Kotlin, Python, Go, TSAmazon Nova Gemini dan Claude langsung; model lain lewat Model Garden dan, di Python, LiteLLM, Ollama, dan vLLM. Jembatan Java ke LangChain4j dan Spring AI hanya ada di modul contrib yang kompatibilitas API-nya tidak dijamin.
Claude Agent SDKPython, TSsemua selain Claude Menjalankan binari Claude Code; halaman LLM gateway-nya menyatakan bahwa Anthropic doesn't support routing Claude Code to non-Claude models through any gateway. Hanya gateway pihak ketiga seperti LiteLLM yang mendokumentasikannya.
Microsoft Agent Framework.NET, Python, Gotidak ada Klien Amazon Bedrock (Converse), Anthropic, Gemini, Ollama, OpenAI (dengan OPENAI_BASE_URL), dan Foundry.
OpenAI Agents SDKPython, TSAmazon Nova, Llama 3.1 Model lain lewat base_url atau adaptor LiteLLM dan Any-LLM yang masih beta. Claude lewat lapisan kompatibel-OpenAI Anthropic, yang menurut Anthropic untuk uji coba, bukan produksi jangka panjang.
Amazon Bedrock AgentCore (Harness)Python, TSSahabat-AI Harness adalah loop terkelola bertenaga Strands Agents untuk model Bedrock, OpenAI, Gemini, atau penyedia kompatibel LiteLLM. Runtime-nya menjalankan loop yang Anda bawa, termasuk loop Java seperti pada contoh resmi AWS.

Cara membaca. Model yang diperiksa adalah Amazon Nova, Qwen3, GPT-OSS, DeepSeek, GLM, Kimi, MiniMax, Devstral 2, Llama 3.1, Sahabat-AI, dan Nemotron 3, ditambah Claude, Gemini, dan GPT sebagai pembanding. Sebuah pasangan dihitung cocok bila dokumentasi resmi loop atau penyedia modelnya menyebut keduanya, atau keduanya mendukung antarmuka yang sama. Kolom pengecualian berarti klaim resmi tidak ditemukan, bukan berarti mustahil; tabel ini tidak menguji pasangan mana pun dan tidak menilai keandalan pemanggilan alatnya. Nova kosong di Google ADK karena dokumentasi ADK tidak menyebut Nova dan dokumentasi Nova tidak menyebut ADK. Di OpenAI Agents SDK, Nova kosong karena tabel API Bedrock mencatatnya hanya untuk Invoke dan Converse, tidak untuk Chat Completions maupun Messages, sehingga loop yang hanya berbicara protokol OpenAI atau Anthropic tidak punya jalur resmi ke Nova di Bedrock; Llama 3.1 kosong karena tabel itu tidak mencantumkannya di Chat Completions dan dokumentasi Meta tidak menyebut antarmuka itu. Sahabat-AI kosong di AgentCore karena tidak ada di katalog Bedrock. Klaim LangChain soal API Nova terpisah yang kompatibel-OpenAI tidak dihitung, karena belum terverifikasi di dokumentasi Amazon sendiri.

3. Anomali Adya: apa saja, dan mana yang lolos di PostgreSQL

Contoh termahal: pemesanan ganda

Cara paling umum menulis pemesanan adalah bertanya dulu, lalu menulis:

-- Transaksi A dan B berjalan bersamaan, untuk kursi yang sama
SELECT count(*) FROM reservasi
 WHERE kursi = 'F7'
   AND periode && tstzrange('2026-10-11 19:00+07', '2026-10-11 21:00+07')
   AND status IN ('held', 'confirmed');   -- A melihat 0, B juga melihat 0

INSERT INTO reservasi (kursi, periode, status)
VALUES ('F7', tstzrange('2026-10-11 19:00+07', '2026-10-11 21:00+07'), 'held');
-- keduanya berhasil: dua tiket untuk satu kursi

Pada tingkat isolasi READ COMMITTED, bawaan PostgreSQL, maupun REPEATABLE READ, kedua transaksi melihat kursi kosong dan keduanya menyimpan klaim. Dua penonton datang membawa tiket untuk kursi yang sama. Dalam taksonomi Atul Adya, ini write skew. Bahayanya bukan hanya anomalinya, tetapi juga tidak menyadarinya: pengujian dengan satu pengguna selalu lulus, dan kesalahan baru muncul saat ramai, tepat pada malam pemutaran perdana.

Adya dalam lima menit

Standar SQL ANSI mendefinisikan tingkat isolasi lewat tiga "fenomena": dirty read, non-repeatable read, dan phantom. Rumusannya berbahasa penguncian dan tidak pas untuk basis data multiversi seperti PostgreSQL. Tesis doktoral Atul Adya di MIT (1999), dan makalah ICDE 2000 Generalized Isolation Level Definitions yang ia tulis bersama Barbara Liskov dan Patrick O'Neil, menggantinya dengan syarat atas riwayat transaksi yang sudah di-commit. Implementasi apa pun, entah penguncian, MVCC, atau konkurensi optimistis, bisa diperiksa dengan ukuran yang sama.

Riwayat itu digambar sebagai graf, direct serialization graph: satu simpul per transaksi yang di-commit, dan sisi dari T1 ke T2 bila T2 bergantung pada T1 dengan salah satu dari tiga cara:

Sebuah riwayat serializable bila grafnya tidak bersiklus: ada urutan di mana setiap transaksi hanya melihat transaksi sebelumnya. Setiap anomali adalah bentuk siklus tertentu, atau pembacaan data yang seharusnya tidak pernah terlihat.

Empat bentuk siklus Empat pasang transaksi T1 dan T2. G0: ketergantungan tulis dua arah. G1c: ketergantungan baca dua arah. G-single: satu anti-ketergantungan dan satu ketergantungan baca. G2-item dan G2: anti-ketergantungan dua arah. T1 T2 wwww G0siklus tulis T1 T2 wrwr G1caliran informasi melingkar T1 T2 rwwr G-singleread skew T1 T2 rwrw G2-item / G2write skew, phantom
Empat bentuk siklus. Panah utuh adalah ketergantungan (ww, wr) dan panah putus-putus adalah anti-ketergantungan (rw). G1a dan G1b bukan siklus: keduanya pembacaan versi yang dibatalkan, atau versi yang ditimpa penulisnya sendiri sebelum commit. G-cursor adalah bentuk G-single pada satu baris, dengan ketergantungan tulis menggantikan ketergantungan baca.
Delapan anomali Adya, wujudnya di bioskop, dan tingkat isolasi pertama yang melarangnya
AnomaliSyarat pada riwayat yang di-commit Di bioskopDilarang mulai
G0 siklus tulis (dirty write)Siklus yang hanya terdiri atas sisi ww Dua pasangan memesan sepasang kursi yang sama dari arah berlawanan; masing-masing pulang dengan satu kursi dari pasangan ituPL-1
G1a bacaan yang dibatalkanTransaksi yang di-commit membaca versi tulisan transaksi yang batal Peta kursi menampilkan klaim dari pemesanan yang tidak pernah jadiPL-2 (read committed)
G1b bacaan antaraMembaca versi yang kemudian ditimpa penulisnya sendiri sebelum commit Kursi berkedip "ditahan" selama pemesanan blok orang lain yang akhirnya gagalPL-2
G1c aliran informasi melingkarSiklus yang hanya terdiri atas sisi ww dan wr Dua pasangan saling melihat pemesanan setengah jadi milik yang lain, keduanya mundur, dan kursinya tidak terjualPL-2
G-cursor lost update (P4)Pada satu item, siklus berisi satu sisi rw dan sisi ww Pembatalan sebuah kursi tertelan dan kursinya tetap terjual; satu barcode meloloskan dua orang di pintuPL-CS (cursor stability)
G-single read skewSiklus dengan tepat satu sisi rw Peta kursi menampilkan separuh pesanan dua kursi seseorangPL-2+ (dan snapshot isolation)
G2-item write skewSiklus dengan satu atau lebih sisi rw atas item tertentu Dua pelanggan datang membawa tiket untuk F7PL-2.99 (repeatable read)
G2 phantomSiklus dengan satu atau lebih sisi rw, termasuk dari klausa WHERE Satu calo dengan dua tab melewati batas 10 kursi per hariPL-3 (serializable)

G-single dan G2 dibedakan oleh jumlah anti-ketergantungannya, dan keduanya mudah tertukar. Write skew versi buku teks, dua dokter jaga yang masing-masing melihat rekannya masih berjaga lalu sama-sama pulang, menutup siklusnya dengan dua sisi rw, jadi ia G2, bukan G-single. Contoh "cek dulu, lalu simpan" di atas pun, bila dibaca ketat, adalah G2, karena klaim baru adalah baris baru yang ditemukan lewat predikat (kursi ini, waktu yang beririsan, status hidup), bukan versi baru dari baris yang sudah ada. Catatan desain proyek ini memasukkannya ke G2-item karena menyangkut satu kursi yang sudah diketahui. Apa pun labelnya, obatnya sama (bagian 4).

Di mana empat tingkat isolasi PostgreSQL berada

Tingkat isolasi PostgreSQL 18 terhadap anomali Adya
Tingkat di PostgreSQLMencegahMasih membiarkan
READ UNCOMMITTEDDiterima, tetapi berjalan persis seperti READ COMMITTED
READ COMMITTED, bawaanG0, G1a, G1b, G1cG-cursor*, G-single, G2-item, G2
REPEATABLE READ (snapshot isolation)…ditambah G-cursor dan G-singleG2-item, G2
SERIALIZABLE (serializable snapshot isolation)Semuanya, dengan membatalkan salah satu transaksi lewat 40001tidak ada

* Lost update di READ COMMITTED butuh pembacaan dan penulisan di dua pernyataan terpisah. Satu UPDATE … WHERE yang harus menunggu kunci baris akan mengevaluasi ulang WHERE-nya terhadap baris yang baru di-commit, jadi ia tidak bisa menimpa perubahan yang tidak dilihatnya.

Tabel itu menjadi praktis karena satu fakta: aplikasi bioskop ini tidak pernah mengatur tingkat isolasi, jadi semua transaksinya berjalan di READ COMMITTED, tingkat paling longgar yang dimiliki PostgreSQL. Separuh pertama daftar Adya sudah tertutup oleh tingkat itu, dan tidak ada pengaturan PostgreSQL yang bisa membukanya. Separuh keduanya harus ditutup oleh desain, di setiap tempat sebuah pemesanan bergantung padanya.

Satu balapan yang bukan anomali Adya

Sebuah penahanan membaca pertunjukan masih berstatus dijual. Staf membatalkan pertunjukan itu. Penahanannya tetap di-commit sesudahnya. Hasilnya tiket untuk pertunjukan yang sudah dibatalkan. Riwayat ini hanya punya satu anti-ketergantungan tanpa sisi kembali, jadi bukan siklus: "pesan, lalu batalkan" adalah urutan serial yang sah, dan tidak ada tingkat isolasi yang keberatan, bahkan SERIALIZABLE. Yang salah adalah pemeriksaannya basi sebelum di-commit. Aturan bisnisnya, tidak boleh ada penjualan setelah pembatalan berlaku, adalah aturan tentang kapan sesuatu di-commit. Obatnya SELECT … FOR UPDATE pada baris pertunjukan sebagai pernyataan pertama setiap penahanan, dan pelajaran yang lebih umum: klasifikasi anomali bergantung pada jejak baca dan tulis kedua transaksi, bukan pada wujud skenarionya. Bila pembatalan kelak memeriksa "masih adakah pemesanan aktif?" lebih dulu, skenario yang sama memperoleh sisi kembali dan menjadi G2 yang sungguhan.

Daftar lengkap di atas bisa dicoba satu per satu terhadap deployment movie-ticket Anda sendiri, lewat API publiknya, dan dalam percobaan kami tidak satu pun berhasil. Skrip, cara menjalankan, dan contoh hasilnya ada di halaman Adya's Isolation Anomalies, Tried Against a Live Cinema Booking App (bahasa Inggris).

4. Mencegahnya di PostgreSQL 18.6

READ COMMITTED menutup G0 sampai G1c dengan sendirinya. Empat sisanya, ditambah balapan yang bukan anomali Adya, ditutup oleh lima teknik. Tidak satu pun membutuhkan tingkat isolasi yang lebih ketat, kecuali yang kelima:

Anomali, teknik yang menutupnya, dan wujudnya di aplikasi bioskop ini
AnomaliTeknikDi aplikasi bioskop ini
G0Bawaan: kunci tulis baris di setiap tingkat, ditambah transaksi yang semua-atau-tidak Pemesanan blok yang gagal di satu kursi menarik seluruh bloknya
G1a, G1b, G1cBawaan: MVCC, dan pemeriksaan constraint yang menunggu klaim yang belum di-commit Notifikasi ketersediaan baru dikirim setelah commit
G-cursorTeknik 3: transisi status sebagai satu UPDATE bersyarat Konfirmasi, pembatalan, dan pemindaian tiket di pintu
G-singleTeknik 4: satu pernyataan untuk satu bacaan, atau teknik 2 untuk yang dibaca Peta kursi dibaca dalam satu kueri
G2-itemTeknik 1: constraint Constraint eksklusi reservation_no_overlap
G2Teknik 2, kunci baris induk sebelum menghitung, atau teknik 5, SERIALIZABLE Baris pelanggan dikunci sebelum menghitung batas 10 kursi per hari
Bukan AdyaTeknik 2: kunci baris yang statusnya menentukan izin Baris pertunjukan dikunci sebagai pernyataan pertama setiap penahanan

Teknik 1: constraint, biarkan basis data yang menolak

CREATE EXTENSION IF NOT EXISTS btree_gist;  -- agar kolom skalar (kursi) bisa masuk indeks GiST

CREATE TABLE reservasi (
  id       bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  kursi    text      NOT NULL,
  periode  tstzrange NOT NULL,                -- [mulai, selesai)
  status   text      NOT NULL
           CHECK (status IN ('held', 'confirmed', 'cancelled', 'expired')),
  CONSTRAINT reservasi_tanpa_tumpang_tindih
    EXCLUDE USING gist (kursi WITH =, periode WITH &&)
    WHERE (status IN ('held', 'confirmed'))
);

Artinya: tidak boleh ada dua baris dengan kursi yang sama (=) dan periode yang tumpang tindih (&&) selama keduanya masih hidup. PostgreSQL memeriksanya saat menulis, seperti indeks unik. Bila transaksi pertama belum selesai, transaksi kedua menunggu hasilnya, bukan menebak, dan klaim kedua gagal dengan conflicting key value violates exclusion constraint. Constraint mengubah konflik predikat, yang tidak bisa dikunci karena barisnya belum ada, menjadi konflik tulis pada satu entri indeks. Beberapa konsekuensi yang dibayar mahal untuk dipelajari:

Constraint juga cocok sebagai sabuk kedua di belakang kunci. Pelunasan tunai di loket dijaga kunci baris kursi pesanannya, dan di belakangnya ada indeks unik parsial … WHERE payment_channel = 'cash' AND status = 'paid', sehingga satu pesanan tidak mungkin tercatat lunas tunai dua kali walaupun kuncinya suatu hari terlewat.

Teknik 2: kunci baris yang dipakai semua orang, lebih dulu

Ketika keputusan bergantung pada baris lain yang tidak ikut ditulis, seperti status pertunjukan atau jumlah kursi pelanggan hari ini, kunci baris itu lebih dulu. Setiap penahanan di aplikasi ini mengambil tiga kunci:

Kunci yang diambil semua orang di awal bukanlah "pengecekan" yang dilarang teknik 1. Kunci menentukan urutan datangnya transaksi; constraint tetap yang menentukan siapa yang mendapat kursi. Yang dilarang adalah membaca ketersediaan untuk memutuskan. Satu aturan lagi menyusul dari kisah rantai hash di bawah: ambil semua kunci baris yang dibutuhkan sebelum penulisan pertama yang ikut mengambil kunci bersama, dan siapkan percobaan ulang untuk deadlock (40P01).

Teknik 3: transisi status sebagai satu UPDATE bersyarat

UPDATE reservasi SET status = 'confirmed'
 WHERE id = $1 AND status = 'held';                   -- 0 baris: sudah diubah transaksi lain

UPDATE reservasi SET status = 'cancelled'
 WHERE id = $1 AND status IN ('held', 'confirmed');

UPDATE reservasi SET admitted_at = now()
 WHERE token_masuk = $1 AND status = 'confirmed'
   AND admitted_at IS NULL;                           -- satu tiket, satu kali masuk

Lost update membutuhkan pembacaan dan penulisan di dua pernyataan. Tulis transisinya sebagai satu pernyataan bersyarat dan periksa jumlah baris yang berubah: nol berarti transaksi lain menang lebih dulu. Bila dua transaksi bertabrakan, yang kedua menunggu kunci baris yang pertama, lalu mengevaluasi ulang WHERE-nya terhadap baris yang baru di-commit. Ia tidak bisa menimpa perubahan yang tidak dilihatnya. Jangan memeriksa admitted_at lebih dulu dengan SELECT; syarat di dalam UPDATE itulah pemeriksaannya.

Teknik 4: satu pernyataan, satu snapshot

READ COMMITTED mengambil snapshot baru untuk setiap pernyataan, jadi read skew hanya mungkin di antara dua pernyataan. Peta kursi aplikasi ini adalah satu kueri SQL, setiap kursi di-left join ke klaim hidupnya, sehingga sebuah commit terlihat utuh atau tidak sama sekali. Bila sebuah keputusan bergantung pada beberapa bacaan, kunci yang dibaca (teknik 2). Bila yang dibutuhkan hanya laporan yang konsisten dari beberapa kueri, jalankan di REPEATABLE READ, yang memakai satu snapshot untuk seluruh transaksi. Read skew yang tidak memutuskan apa-apa boleh dibiarkan: halaman pesanan membaca pesanan lalu kursinya dalam dua pernyataan, jadi muat ulang yang jatuh tepat di tengah konfirmasi bisa menampilkan "menunggu" di atas kursi yang sudah dikonfirmasi, dan tampilan berikutnya benar.

Teknik 5: SERIALIZABLE untuk agregat, dengan satu percobaan ulang

Bila aturannya tentang total atas banyak baris dan tidak ada satu baris pun yang bisa mewakilinya, misalnya kuota pemesanan aktif seluruh penyewa, jalankan transaksinya di SERIALIZABLE. PostgreSQL mendeteksi struktur berbahaya di antara transaksi yang berjalan bersamaan dan membatalkan salah satunya dengan 40001. Ulangi tepat sekali: deteksinya konservatif dan bisa membatalkan pasangan yang sebenarnya tidak berkonflik. 40001 kedua pada permintaan yang sama berarti persaingannya nyata; perlakukan seperti 23P01. Bila ada baris induk yang bisa mewakili totalnya, seperti pelanggan untuk "kursi per hari", FOR UPDATE padanya lebih murah dan bekerja di READ COMMITTED. Di bawah 18.6, deteksi itu sendiri punya celah (lihat bawah).

Anomali yang tidak disadari: rantai bukti yang bercabang

Log audit platform ini adalah rantai hash per penyewa: setiap baris baru menyimpan hash yang dihitung dari hash baris sebelumnya, sehingga baris yang diubah belakangan akan ketahuan. Fungsi trigger-nya mencari baris terakhir penyewa dengan SELECT row_hash … ORDER BY audit_id DESC LIMIT 1, tanpa kunci. Di READ COMMITTED, baris yang belum di-commit tidak terlihat, jadi dua transaksi penyewa yang sama yang menulis bersamaan sama-sama menautkan diri ke baris pendahulu yang sama. Rantainya bercabang, dan pemeriksaan rantai melaporkan baris kedua sebagai hasil pemalsuan, padahal tidak ada yang menyentuhnya.

Ini pola phantom yang sama dengan "cek dulu, lalu simpan": membaca "baris terakhir" lalu menambahkan sesudahnya. Cacat seperti ini bisa bertahan lama tanpa ketahuan, karena tidak ada kode yang memverifikasi rantainya. Ketika ditemukan pada 25 September 2026 oleh pengujian yang sengaja menahan satu tulisan tetap terbuka, 34 baris log di server yang hidup masih utuh hanya karena lalu lintas demo tidak pernah bertabrakan. Begitu lalu lintas sungguhan bertabrakan, setiap cabang akan permanen, karena log audit hanya bisa ditambah, dan memperbaiki baris lama adalah pemalsuan itu sendiri. Seandainya dibiarkan, hari ketika seseorang memeriksa log itu sebagai bukti, ia akan gagal pada lalu lintas biasa.

Perbaikannya, 15 Oktober 2026, adalah kunci advisory per penyewa yang diambil sebelum pembacaan:

-- di dalam fungsi trigger, sebelum mencari baris terakhir penyewa
PERFORM pg_advisory_xact_lock(hashtext('audit_log'), hashtext(NEW.tenant_id::text));
SELECT row_hash INTO prior_hash FROM audit_log
 WHERE tenant_id = NEW.tenant_id ORDER BY audit_id DESC LIMIT 1;

Penulis kedua menunggu sampai penulis pertama commit, lalu membaca barisnya. Alternatifnya, mencatat ID baris pendahulu di setiap baris, ditolak: cabangnya tetap ada, dan saudara yang kalah menjadi daun yang tidak ditautkan siapa pun, yang bisa dihapus tanpa jejak. Harganya, tulisan teraudit satu penyewa bergiliran dari baris audit pertamanya sampai commit, dan itulah asal aturan urutan kunci di teknik 2.

Membuktikan: cabut pelindungnya, lihat anomalinya muncul

Uji konkurensi yang lulus belum membuktikan apa-apa. Jalankan setiap uji dua kali: sekali terhadap kode sebenarnya, sekali terhadap versi yang sengaja dilemahkan. Bila versi lemah tidak menghasilkan anomalinya, ujinya tidak menguji apa yang Anda kira. Karena tidak ada tingkat isolasi di bawah READ COMMITTED untuk dituju, yang dilemahkan adalah pelindung desainnya:

Sisipkan jeda buatan di antara pemeriksaan dan penulisan lewat properti yang hanya aktif di profil uji, supaya dua permintaan pasti jatuh di jendela balapannya alih-alih berharap kebetulan. Semua ini hanya bisa diuji terhadap PostgreSQL sungguhan: proyek ini menjalankan setiap kueri terhadap PostgreSQL 18.6 yang sudah dimigrasi lewat Testcontainers di setiap build, dengan versi image uji yang mengikuti server yang hidup. Pengujian terhadap aplikasi yang hidup tetap berguna untuk menunjukkan perilakunya di jaringan dan konfigurasi sungguhan, tetapi ia hanya menunjukkan ketiadaan anomali pada percobaan yang dilakukan. Yang membuat anomalinya mustahil adalah mekanismenya.

Yang khusus di PostgreSQL 18.6

PostgreSQL 18.6 dirilis 13 Agustus 2026 dan memuat perbaikan sejak 18.4. Tiga di antaranya menyentuh langsung teknik di atas:

Catatan migrasinya juga meminta pembangunan ulang beberapa indeks setelah memasang 18.6: indeks btree_gist pada kolom float4 atau float8 bila kolomnya mungkin berisi NaN (perbandingannya salah), indeks btree_gist pada kolom bit atau varbit (indeksnya tidak efisien), dan indeks btree atas nilai ltree dengan lebih dari sekitar 14.653 label (perbandingannya meluap). Indeks yang menjawab salah tidak tampak rusak, dan bagi constraint eksklusi jawaban yang salah berarti pemesanan ganda atau kursi kosong yang ditolak. Tanyakan ke katalog, bukan ke skema yang Anda ingat. Kueri ini menemukan setiap indeks atas tipe-tipe itu, termasuk indeks atas ekspresi, karena ia lewat kelas operatornya:

SELECT n.nspname AS skema, c.relname AS tabel, i.relname AS indeks, am.amname AS metode,
       pg_get_indexdef(i.oid) AS definisi
FROM pg_index x
JOIN pg_class      i  ON i.oid  = x.indexrelid
JOIN pg_class      c  ON c.oid  = x.indrelid
JOIN pg_am         am ON am.oid = i.relam
JOIN pg_namespace  n  ON n.oid  = i.relnamespace
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
  AND EXISTS (SELECT 1
              FROM unnest(x.indclass::oid[]) AS cls(oid)
              JOIN pg_opclass o ON o.oid = cls.oid
              JOIN pg_type    t ON t.oid = o.opcintype
              WHERE t.typname IN ('ltree', 'bit', 'varbit', 'float4', 'float8'))
ORDER BY 1, 2, 3;

-- untuk setiap baris hasilnya, di server yang sedang melayani:
REINDEX INDEX CONCURRENTLY nama_skema.nama_indeks;   -- tidak bisa di dalam blok transaksi

-- SQL ekstensi bisa tertinggal dari binarinya setelah naik versi
SELECT name, default_version, installed_version FROM pg_available_extensions
 WHERE name IN ('btree_gist', 'pgcrypto');           -- bila tertinggal: ALTER EXTENSION btree_gist UPDATE;

Di platform ini kueri itu mengembalikan nol baris pada 22 Agustus 2026. Satu-satunya indeks GiST-nya adalah constraint eksklusi atas uuid dan tstzrange, dan dua tempat yang wajar untuk bilangan titik mengambang sengaja memakai numeric: uang numeric(14,2), dan lintang serta bujur cabang numeric(9,6). Keputusan menghindari float itu ternyata membayar dividen yang tidak diduga siapa pun.

PostgreSQL 18: WITHOUT OVERLAPS

Sejak PostgreSQL 18, yang dirilis 25 September 2025, kunci primer dan constraint unik dapat ditulis dengan WITHOUT OVERLAPS pada kolom terakhirnya, dan kunci asing dapat memakai PERIOD. Dokumentasinya menyebut constraint semacam itu ditegakkan sebagai exclusion constraint dengan indeks GiST: UNIQUE (id, valid_at WITHOUT OVERLAPS) berperilaku seperti EXCLUDE USING GIST (id WITH =, valid_at WITH &&). Kolom rentangnya tidak boleh kosong, dan kolom skalar di sebelahnya membutuhkan btree_gist. Untuk data master yang tidak boleh tumpang tindih, seperti periode harga, tarif, atau masa jabatan, bentuk ini lebih ringkas:

CREATE TABLE harga_produk (
  produk_id     bigint        NOT NULL,
  berlaku       daterange     NOT NULL,
  harga_rupiah  numeric(14,2) NOT NULL,
  PRIMARY KEY (produk_id, berlaku WITHOUT OVERLAPS)
);

Namun constraint unik tidak bisa diberi klausa WHERE. Untuk pemesanan, yang hanya melarang tumpang tindih di antara baris yang masih held atau confirmed, EXCLUDE … WHERE tetap alat yang tepat.

5. Di luar bioskop: siapa lagi yang diuntungkan?

Catatan desain platform bioskop ini merumuskannya begini: kursi bioskop untuk satu pertunjukan dan kamar hotel untuk satu rentang tanggal adalah masalah yang sama bila dinyatakan dengan tepat. Keduanya klaim eksklusif atas satu sumber daya, untuk satu periode, di mana dua klaim yang tumpang tindih tidak boleh sama-sama berlaku. Setiap domain yang cocok dengan kalimat itu bisa memakai pola yang sama: kolom sumber daya dengan = dan kolom rentang dengan &&. Skema platform ini sengaja dibuat generik untuk itu: tabel yang sama memetakan aula ke properti, kursi ke kamar, dan periode pertunjukan ke [check-in, check-out).

Pola "sumber daya dan rentang" di berbagai domain
DomainSumber daya (WITH =) Rentang (WITH &&)Catatan
Hotel, vila, homestaykamardaterange [check-in, check-out) Hari check-out sudah bebas untuk tamu berikutnya karena batas atasnya terbuka.
Ruang rapat, coworking, aula, gedung resepsiruangan, mejatstzrange Waktu persiapan dan bersih-bersih ikut dimasukkan ke rentang yang disimpan.
Lapangan futsal, badminton, padellapangantstzrange per jam Pemesanan beruntun 19.00–20.00 dan 20.00–21.00 sah bersama.
Klinik dan rumah sakitdokter; ruang praktik; kamar operasi; tempat tidur; alat MRI tstzrange Satu tabel janji temu boleh memuat dua constraint: dokter tidak ganda dan ruangan tidak ganda.
Kereta dan bus antarkotakursi pada satu perjalanan int4range urutan stasiun [naik, turun) Kursi yang sama bisa dijual untuk segmen yang tidak beririsan, misalnya stasiun 1–4 lalu 4–7.
Sewa mobil, motor, kamera, alat beratunit (nomor polisi, nomor seri)tstzrange Cocok untuk unit yang dapat dibedakan, bukan "mobil kelas apa saja".
Pelabuhan dan bandaradermaga; gerbangtstzrange Slot sandar kapal dan slot gerbang pesawat adalah klaim eksklusif berwaktu.
Gudang dan logistikpintu bongkar muattstzrange Jadwal kedatangan truk tanpa dua truk di satu pintu pada waktu yang sama.
Sekolah, kampus, ujianruang kelas; laboratorium; kursi ujian; pengawaststzrange Satu pengawas tidak bisa dijadwalkan di dua ruang sekaligus.
Sumber daya manusiakaryawantstzrange shift Satu orang tidak bisa memegang dua shift yang beririsan. Bila shift dan cuti juga tidak boleh bertabrakan, simpan keduanya di satu tabel jadwal.
Penyiaran dan iklankanal; papan reklame digitaltstzrange slot tayang Dua materi tidak boleh menempati slot yang sama.
Data master bertanggalproduk; kode pajak; pasangan mata uang; jabatan daterange masa berlaku Tidak boleh ada dua harga atau dua tarif yang berlaku bersamaan untuk hal yang sama. Di PostgreSQL 18, WITHOUT OVERLAPS.
Asuransi dan perjanjianaset; nomor polis atau perjanjiandaterange masa pertanggungan Dua polis aktif atas aset yang sama tidak boleh tumpang tindih, bila produknya melarangnya.
Jaringan dan spektrumswitch; antarmuka; wilayah int4range VLAN atau port; numrange pita frekuensi Untuk blok alamat IP, tipe inet dengan kelas operator GiST inet_ops memakai operator && yang sama.

Contoh: kursi kereta per segmen

CREATE TABLE klaim_kursi (
  perjalanan_id bigint    NOT NULL,
  kursi         text      NOT NULL,
  segmen        int4range NOT NULL,   -- [urutan stasiun naik, urutan stasiun turun)
  status        text      NOT NULL,
  EXCLUDE USING gist (perjalanan_id WITH =, kursi WITH =, segmen WITH &&)
    WHERE (status IN ('held', 'confirmed'))
);
-- [1,4) dan [4,7) sah bersama: penumpang pertama turun di stasiun ke-4, yang kedua naik di sana.
-- [1,4) dan [3,6) ditolak dengan 23P01.

Contoh: satu janji temu, dua larangan

CREATE TABLE janji_temu (
  dokter_id  bigint    NOT NULL,
  ruang_id   bigint    NOT NULL,
  waktu      tstzrange NOT NULL,
  status     text      NOT NULL,
  EXCLUDE USING gist (dokter_id WITH =, waktu WITH &&) WHERE (status <> 'batal'),
  EXCLUDE USING gist (ruang_id  WITH =, waktu WITH &&) WHERE (status <> 'batal')
);

Kapan pola ini bukan alat yang tepat

6. Pertanyaan yang sering diajukan

Apakah agen elektronik dalam UETA 1999 berarti agen AI?

Tidak. Komentar resmi UETA menyebut agen elektronik sebagai mesin yang terbatas pada fungsi alat dan bekerja di dalam batasan pemrogramannya. Agen yang bertindak otonom, bukan sekadar otomatis, baru diantisipasi sebagai kemungkinan di masa depan, dan penafsirannya diserahkan kepada pengadilan.

Apakah server web yang menerima pesanan termasuk agen elektronik?

Ya. Contoh dari perancang UETA sendiri adalah Bookseller.com, yang menerima dan mengonfirmasi pesanan lewat mesinnya. Aplikasi yang menahan kursi, menerima pembayaran, dan menerbitkan tiket tanpa ditinjau manusia adalah agen elektronik milik penjualnya, termasuk basis data di dalamnya, walaupun keputusan menerima atau menolak klaim kursi diambil oleh constraint di PostgreSQL.

Di Indonesia, siapa yang menanggung akibat hukum transaksi lewat agen elektronik?

Menurut UU ITE Pasal 21 ayat (2) huruf c, penyelenggara Agen Elektronik. Kerugian karena agen gagal beroperasi akibat tindakan pihak ketiga juga ditanggung penyelenggara (ayat 3), kerugian karena kelalaian pengguna jasa ditanggung pengguna (ayat 4), dan tanggung jawab penyelenggara tidak berlaku bila dapat dibuktikan keadaan memaksa, kesalahan, atau kelalaian pengguna (ayat 5).

Fitur apa yang wajib disediakan penyelenggara agen elektronik?

PP 71/2019 Pasal 37 ayat (3) menyebut fasilitas untuk melakukan koreksi, membatalkan perintah, memberikan konfirmasi atau rekonfirmasi, memilih meneruskan atau berhenti, melihat informasi tawaran, mengecek status berhasil atau gagalnya transaksi, dan membaca perjanjian sebelum bertransaksi, sesuai karakteristik agennya. Ayat (1) mewajibkan informasi seperti identitas penyelenggara, syarat perjanjian, jaminan privasi, dan nomor telepon pengaduan. Pelanggarannya dikenai sanksi administratif menurut Pasal 100.

Bolehkah agen AI langsung memesan atas nama pelanggan?

Sebaiknya tidak. UETA Pasal 10 ayat 2 membolehkan individu menghindari akibat kesalahannya bila agen elektronik pihak lain tidak memberi kesempatan untuk mencegah atau mengoreksinya, dan PP 71/2019 mewajibkan fitur koreksi dan konfirmasi. Biarkan agen mengusulkan, misalnya lewat peta kursi yang pilihannya sudah tercentang, dan biarkan pelanggan sendiri yang menekan tombol tahan dan bayar.

Apa itu anomali Adya?

Taksonomi anomali isolasi dari tesis Atul Adya di MIT (1999) dan makalah ICDE 2000 yang ia tulis bersama Barbara Liskov dan Patrick O'Neil. Setiap anomali didefinisikan sebagai siklus tertentu pada graf ketergantungan antartransaksi yang sudah di-commit, atau sebagai pembacaan data yang seharusnya tidak terlihat: G0 (siklus tulis), G1a, G1b, dan G1c (bacaan kotor), G-cursor (lost update), G-single (read skew), G2-item (write skew), dan G2 (phantom). Karena tidak bergantung pada penguncian, definisi itu cocok untuk basis data multiversi seperti PostgreSQL.

Mengapa tidak cukup mengecek apakah kursi masih kosong, lalu menyimpan?

Karena dua transaksi bersamaan sama-sama melihat kursi kosong dan sama-sama menyimpan. READ COMMITTED dan REPEATABLE READ membiarkannya; ini write skew. Constraint eksklusi menolak klaim kedua saat ditulis, pada tingkat isolasi apa pun, dengan SQLSTATE 23P01.

Tingkat isolasi PostgreSQL mana yang mencegah anomali apa?

READ COMMITTED, bawaan PostgreSQL, mencegah G0, G1a, G1b, dan G1c. REPEATABLE READ, yang di PostgreSQL berupa snapshot isolation, menambahkan lost update dan read skew. Hanya SERIALIZABLE yang juga mencegah write skew dan phantom, dengan membatalkan salah satu transaksi lewat SQLSTATE 40001. READ UNCOMMITTED diterima tetapi berperilaku seperti READ COMMITTED.

Apa yang perlu diperhatikan saat memakai PostgreSQL 18.6?

Versi 18.6, dirilis 13 Agustus 2026, memperbaiki deteksi konflik SERIALIZABLE yang bisa melewatkan konflik pada indeks btree yang awalnya kosong, index-only scan GiST atas kolom rentang yang bukan kolom pertama indeks, dan pencarian btree_gist dengan operator tidak-sama. Setelah naik versi, bangun ulang indeks btree_gist pada kolom float yang mungkin berisi NaN, indeks btree_gist pada kolom bit, dan indeks btree atas ltree.

Apa bedanya WITHOUT OVERLAPS di PostgreSQL 18 dengan EXCLUDE?

WITHOUT OVERLAPS adalah bentuk standar untuk kunci primer dan constraint unik atas rentang, dan ditegakkan sebagai exclusion constraint GiST. Cocok untuk data master seperti periode harga. Constraint unik tidak bisa diberi klausa WHERE, jadi untuk pemesanan yang hanya melarang tumpang tindih di antara baris yang masih aktif, EXCLUDE dengan WHERE tetap dibutuhkan.

Domain apa saja selain bioskop yang cocok memakai constraint eksklusi?

Semua yang berbentuk klaim eksklusif atas satu sumber daya untuk satu periode: hotel, ruang rapat, lapangan olahraga, jadwal dokter dan kamar operasi, kursi kereta per segmen, sewa kendaraan dan alat, dermaga, shift karyawan, periode harga dan tarif, polis asuransi, serta rentang VLAN atau frekuensi. Untuk kapasitas yang dapat dipertukarkan, seperti tiket tanpa nomor kursi, gunakan penghitungan dengan penguncian baris.

7. Sumber

Harga, ketersediaan layanan, region, dan angka tolok ukur berubah dari waktu ke waktu. Periksa sumber resminya sebelum mengambil keputusan.