Aplikasi Python kita kini bisa ber-CRUD, men-aggregate, bahkan bertransaksi — tetapi dunia nyata tidak sebersih kode latihan. Jaringan terputus sesaat, dua pengguna meregistrasi email yang sama bersamaan, validator menolak input tak lengkap: semuanya tiba di aplikasi sebagai exception PyMongo. Kualitas program ditentukan bukan oleh absennya error, melainkan oleh respons yang tepat — error permanen tidak boleh dicoba ulang, error transien tidak boleh langsung menyerah. Chapter terakhir Section ini memetakan exception yang paling sering dijumpai, menampilkan bentuk aslinya dari eksekusi sungguhan, lalu menyusun pola try/except/finally yang rapi.
Peta Exception PyMongo #
Semua exception driver tinggal di modul pymongo.errors, dan semuanya berketurunan dari satu akar: PyMongoError. Posisi dalam silsilah itu menentukan cara menangkapnya:
Dua cabang besar itu punya watak berbeda. Cabang OperationFailure berarti server menjawab lalu menolak — misalnya duplicate key atau validasi gagal — umumnya permanen. Cabang ConnectionFailure berarti dialog dengan server bermasalah — umumnya transien dan layak dicoba ulang. Tabel ini merangkum pemicu dan tindakannya:
| Exception | Pemicu | Aksi yang tepat |
|---|---|---|
DuplicateKeyError (code 11000) | unique index dilanggar | permanen: tolak input, laporkan ke pengguna |
WriteError (code 121 dll.) | validator atau aturan server menolak document | permanen: perbaiki document |
BulkWriteError (code 65) | sebagian operasi massal gagal | baca details["writeErrors"], putuskan lanjut/ulang |
AutoReconnect / ConnectionFailure | koneksi terputas sesaat | transien: coba ulang dengan backoff |
ServerSelectionTimeoutError | tidak ada server tersedia dalam batas waktu | transien: periksa URI/server; gagal cepat di CLI |
NetworkTimeout | operasi tak selesai dalam socketTimeoutMS | transien: coba ulang bila idempoten |
PyMongoError | payung semua error driver | penampung terakhir: log dan propagasi |
Empat Error dalam Praktik #
Satu berkas menimbulkan empat error secara nyata lalu menangkap satu per satu. Perhatikan bahwa setiap exception membawa kode numerik dan atribut details berisi dokumen error mentah dari server:
import os
import time
from pymongo import MongoClient
from pymongo.errors import (
BulkWriteError,
ConnectionFailure,
DuplicateKeyError,
PyMongoError,
ServerSelectionTimeoutError,
WriteError,
)
URI = os.environ.get(
"MONGODB_URI", "mongodb://127.0.0.1:27017/?directConnection=true"
)
client = MongoClient(URI, serverSelectionTimeoutMS=5000)
client.drop_database("latihan_error_py")
db = client["latihan_error_py"]
db.pengguna.create_index("email", unique=True)
# 1) DuplicateKeyError — unique index dilanggar
try:
db.pengguna.insert_one({"nama": "Budi", "email": "[email protected]"})
db.pengguna.insert_one({"nama": "Budi Kembar", "email": "[email protected]"})
except DuplicateKeyError as err:
print("[DuplicateKeyError] code:", err.code)
print(" kunci bentrok:", err.details["keyValue"])
# 2) WriteError — schema validator menolak document
db.create_collection("produk", validator={"$jsonSchema": {
"bsonType": "object",
"required": ["nama", "harga"],
"properties": {
"nama": {"bsonType": "string"},
"harga": {"bsonType": "int", "minimum": 0},
},
}})
try:
db.produk.insert_one({"nama": "Kopi Gayo", "harga": -5000})
except WriteError as err:
print("[WriteError] code:", err.code)
print(" errmsg:", err.details["errmsg"])
# 3) BulkWriteError — satu anggota insert_many bentrok
try:
db.pengguna.insert_many([
{"nama": "Siti", "email": "[email protected]"},
{"nama": "Budi Kembar", "email": "[email protected]"},
{"nama": "Dewi", "email": "[email protected]"},
])
except BulkWriteError as err:
print("[BulkWriteError] code:", err.code)
print(" tersimpan sebelum gagal:", err.details["nInserted"])
for we in err.details["writeErrors"]:
print(" gagal di index", we["index"], "kode", we["code"])
# 4) ServerSelectionTimeoutError — try/except/finally ke port mati
client_mati = MongoClient(
"mongodb://127.0.0.1:9999/?directConnection=true",
serverSelectionTimeoutMS=1500,
)
mulai = time.monotonic()
try:
client_mati.admin.command("ping")
except ServerSelectionTimeoutError as err:
print("[ServerSelectionTimeoutError] setelah",
round(time.monotonic() - mulai, 1), "detik")
print(" turunan ConnectionFailure:", isinstance(err, ConnectionFailure))
finally:
client_mati.close()
# 5) Urutan except: subclass dulu, payung PyMongoError terakhir
try:
db.pengguna.insert_one({"nama": "Budi Tiga", "email": "[email protected]"})
except DuplicateKeyError:
print("[payung] duplikat ditangani cabang khusus")
except PyMongoError as err:
print("[payung] error PyMongo lain:", type(err).__name__)
client.drop_database("latihan_error_py")
client.close()Output yang Dihasilkan:
[DuplicateKeyError] code: 11000
kunci bentrok: {'email': '[email protected]'}
[WriteError] code: 121
errmsg: Document failed validation
[BulkWriteError] code: 65
tersimpan sebelum gagal: 1
gagal di index 1 kode 11000
[ServerSelectionTimeoutError] setelah 1.5 detik
turunan ConnectionFailure: True
[payung] duplikat ditangani cabang khususMari kita bedah empat errornya satu per satu:
DuplicateKeyError— muncul karenacreate_index("email", unique=True)yang kita pasang sendiri.err.codebernilai11000, danerr.details["keyValue"]menyebut persis field beserta nilai yang bentrok — bahan langsung untuk pesan “email sudah terdaftar” di antarmuka.WriteError— schema validator menolakharga: -5000; kode121.err.details["errInfo"]membawa rincian aturan mana yang dilanggar (struktur yang sama dengan yang kita bedah di Chapter Error Handling Section integrasi aplikasi) — cukup untuk menerjemahkan penolakan menjadi pesan pengguna yang jelas.BulkWriteError—insert_manybersifat ordered: begitu satu document gagal, sisanya tidak dikirim.details["nInserted"]mencatat satu document Siti sempat tersimpan, dandetails["writeErrors"]adalah list error per document beserta posisiindex-nya. Bila kita ingin document yang sehat tetap diproses, tambahkanordered=False— lalu evaluasi daftar error setelahnya.ServerSelectionTimeoutError— client ke port 9999 dibuat tanpa error (perilaku lazy dari Chapter Membuat Koneksi Database), lalupingmenunggu pemilihan server hingga batasserverSelectionTimeoutMS=1500terlampaui. Eksekusi berhenti tepat setelah 1,5 detik, danisinstancemembuktikan error ini turunanConnectionFailure— satuexcept ConnectionFailurecukup untuk menangkap seluruh keluarga masalah koneksi.
Pola try, except, finally #
Blok keempat di berkas tadi adalah cetak biru pola lengkap. try memuat operasi yang berisiko; except menangkap tipe yang paling spesifik lebih dulu; finally menutup sumber daya apa pun yang terjadi — client_mati.close() tetap berjalan meski tidak ada error. Untuk aplikasi nyata, urutan cabangnya mengikuti aturan Python biasa: subclass sebelum superclass, karena except PyMongoError yang ditulis lebih dulu akan menelan semua error driver dan cabang di bawahnya tak pernah tercapai — persis yang dibuktikan blok kelima.
Dua kebiasaan penutup yang menjaga kualitas. Pertama, log err.code dan konteks operasi, bukan hanya str(err) — teks pesan bisa berubah antar versi, kode stabil. Kedua, jangan pernah menelan exception dengan except kosong; error yang ditelan membuat data hilang tanpa jejak.
Rangkuman #
- Semua error PyMongo berakar pada
PyMongoError; posisi dalam hierarki (OperationFailurevsConnectionFailure) menandai sifat permanen atau transien. DuplicateKeyError(11000) danWriteError(121) bersifat permanen — perbaiki inputnya;ServerSelectionTimeoutError,AutoReconnect, danNetworkTimeouttransien — layak dicoba ulang.BulkWriteError(65) merangkum hasilinsert_many:nInserteddanwriteErrorsper document;ordered=Falsemelanjutkan document yang sehat.- Atribut
err.detailsmemuat dokumen error mentah —keyValue,errInfo— bahan pesan pengguna yang presisi. - Susun
try/except/finallydengan subclass lebih dulu, tutup sumber daya difinally, dan cataterr.codealih-alih hanya teks pesan.
Dengan error handling, perjalanan Python kita lengkap: dari instalasi, koneksi, CRUD, query, aggregation, cursor, hingga transaksi — semuanya teruji di server sungguhan. Selanjutnya, Section berikutnya membawa pola yang sama ke ekosistem JavaScript.