Harvest Masuk Lewat Satu Pintu: Kontrak API yang Aman Diulang
Isolasi ruang kerja scraping dan pengiriman hasil lewat satu pintu API yang aman diulang: antrian resumable, kunci deduplikasi, ship-log.
Ringkasan
Dulu scraping langsung nulis ke database aplikasi, pas sesi putus jadi bingung data mana yang udah masuk. Nah, commit ini mindahin semua alat scraping ke folder terpisah, hasil panen cuma boleh masuk lewat endpoint API ingest. Ada antrian kerja yang bisa dilanjutin plus kirim ulang yang aman berkat kunci dedup yang stabil, jadi idempoten dan bebas data dobel.
Proses pemanenan data berhenti di angka 18 dari 70 kasus. Sisa 52 sub-tab AJAX masih menunggu ketika sesi harus dihentikan di tengah jalan. Pada tata letak lama, skrip scraping, data mentah, dan fixture pengujian bercampur dengan kode aplikasi utama; satu kesalahan import saja sudah cukup untuk menyentuh penyimpanan aplikasi tanpa sengaja.
Asumsi yang terdengar masuk akal: hasil panen sebaiknya langsung ditulis ke basis data aplikasi selama pengambilan berlangsung. Sekali jalan, data langsung tersedia dan pekerjaan terasa selesai. Kelemahannya baru terasa saat sesi terputus. Siklus hidup pengumpulan data terikat pada ketersediaan basis data tujuan, dan kegagalan di tengah jalan menyisakan pertanyaan yang tidak bisa dijawab: bagian mana yang sudah masuk, bagian mana yang belum.
Commit ini menempuh jalan sebaliknya. Seluruh peralatan scraping dipindahkan ke direktori kerja terpisah dengan satu doktrin ketat: tidak ada satu pun komponen di dalamnya yang boleh menulis ke penyimpanan aplikasi secara langsung. Hasil panen hanya boleh masuk lewat satu pintu, yaitu endpoint API ingest, melalui langkah pengiriman yang terpisah dan dapat diputar ulang.
Antrian Kerja yang Dapat Dilanjutkan
Modul sipp_queue.py memecah pekerjaan menjadi baris-baris kecil. Satu halaman detail adalah satu baris; setiap sub-tab AJAX di dalamnya, dari penetapan sampai riwayat perkara, juga menjadi baris sendiri. Setiap baris menyimpan status pending -> ok | error lengkap dengan jumlah percobaan dan catatan kegagalan.
Laporan status menjawab tiga pertanyaan secara eksak setelah gangguan apa pun: apa yang selesai, apa yang masih menunggu, dan alamat mana yang bermasalah. Log URL mencatat setiap halaman yang benar-benar dibuka beserta hasilnya, terpisah dari antrian pekerjaan dan dari log peristiwa yang berperan sebagai jejak audit. Karena seluruh state tersimpan di diska, eksekusi yang putus dilanjutkan tanpa menebak.
Jembatan Pengiriman Satu Arah
Skrip ship.py menutup rantai ini. Ia membungkus data panen menjadi item, lalu mengirimkannya ke endpoint satu per satu. Kontraknya tegas: setiap item membawa kunci deduplikasi yang stabil, dibentuk dari pola f"{source_type}:{source_id}" berupa pengenal jenis sumber yang dipasangkan dengan nomor kasus pada sumber tersebut. Kunci yang sama selalu menunjuk data logis yang sama, baik pada pengiriman pertama maupun kesekian.
Di sisi klien, log ship-log.jsonl merekam setiap percobaan beserta hasilnya. Saat skrip dijalankan ulang, item tanpa catatan sukses dikirim lagi, sedangkan item yang sudah tercatat ok atau skipped-duplicate dilewati. Di sisi server, kunci yang sama menjadi dasar upsert, bukan duplikasi. Kontrak ini idempoten di kedua ujungnya.
Versi lengkap di skrip nyata memiliki detail tambahan seperti mode dry-run dan fallback alamat ketika endpoint berpindah. Pola inti pengirimnya dapat diringkas sebagai berikut.
import time
def ship_with_retry(dedupe_key, payload, max_attempts=3):
for attempt in range(1, max_attempts + 1):
status = post_item(dedupe_key, payload)
if status in ("accepted", "duplicate"):
record(dedupe_key, status)
return True
time.sleep(2 ** attempt)
record(dedupe_key, "error")
return False
Kegagalan sementara tidak memicu badai permintaan. Jeda antar percobaan membesar secara eksponensial sehingga beban ke arah server tetap terkendali, memberi waktu bagi sistem tujuan untuk pulih.
Kontrak yang Membuat Kirim Ulang Aman
Keamanan mengulang pengiriman bukan trik tambahan, melainkan definisi bawaan HTTP. Sebuah metode disebut idempoten bila efek beberapa permintaan identik sama dengan efek satu permintaan [1]. Klien boleh mengulang permintaan secara otomatis setelah kegagalan komunikasi karena hasilnya diketahui tidak berubah, dan POST pun boleh diulang bila desain endpoint-nya memang aman untuk itu.
Pengiriman data antar sistem hampir selalu berjalan dengan jaminan at-least-once: pesan bisa tiba lebih dari sekali. Jaminan exactly-once dari ujung ke ujung tidak praktis diwujudkan, dan jalan keluarnya adalah penerima yang membiarkan duplikat lewat tanpa efek [2]. Syaratnya satu: kunci deduplikasi harus mengidentifikasi pesan logis yang sama secara unik dan konsisten pada setiap pengiriman ulang.
Praktiknya, semantik idempoten tetap harus ditegakkan oleh server karena spesifikasi tidak memaksa endpoint berperilaku sesuai definisinya [3]. Prinsip yang sama muncul dari sisi operasi: API yang menimbulkan efek samping sebaiknya didesain idempoten agar percobaan ulang aman dilakukan, sementara jeda yang membesar menjaga beban tetap merata saat sistem pulih [4].
Bagian yang paling mudah diabaikan dari commit ini bukan kode pengirimnya, melainkan garis batasnya. Selama hasil panen wajib melewati satu pintu dengan kontrak yang dapat diulang, lokasi API berjalan menjadi soal konfigurasi, bukan soal scraper: lokal hari ini, server lain enam bulan kemudian, tanpa menyentuh ulang kode pemanenan.