Pekerja antrean membuat ringkasan, mengirimkannya ke layanan publikasi, lalu mati sebelum menandai pesan selesai. Antrean menyerahkan pesan yang sama ke pekerja lain. Tanpa identitas operasi dan pemeriksaan status, artikel dapat terbit dua kali meskipun setiap pekerja menjalankan kode dengan benar menurut pandangannya sendiri.
Dokumentasi SQS standard queue menjelaskan kemungkinan pesan diterima lebih dari sekali dan kebutuhan aplikasi yang idempoten. Prinsip itu berguna untuk banyak otomasi AI: rancang pemulihan sebagai bagian dari proses, bukan sebagai tambahan setelah demo berhasil.
Pisahkan pekerjaan, percobaan, dan efek samping
Satu pekerjaan memiliki identitas stabil. Setiap percobaan memiliki identitas sendiri untuk diagnosis. Efek samping, seperti menerbitkan revisi artikel, memiliki kunci operasi yang mengikat tindakan dengan versi payload.
Jangan memakai ID pengiriman antrean sebagai satu-satunya identitas bisnis; pesan yang sama secara makna bisa diterbitkan ulang dengan ID transport baru. Sebaliknya, jangan memakai satu kunci yang terlalu luas untuk semua revisi. Revisi baru yang sah perlu dibedakan dari pengulangan revisi lama.
job_id : brief-042
attempt_id : attempt-003
operation_key: publish:brief-042:revision-3
payload_hash : digest dari payload revisi 3
Jika kunci sama datang dengan payload berbeda, perlakukan sebagai konflik. Mengembalikan sukses seolah permintaan identik akan menyembunyikan perubahan makna.
Outbox menghubungkan perubahan lokal dan niat pengiriman
Transaksi database dapat menyimpan status pekerjaan dan record niat pengiriman bersama-sama. Proses lain membaca outbox lalu melakukan tindakan eksternal. Dengan begitu, aplikasi tidak kehilangan niat pengiriman ketika proses mati setelah menyimpan pekerjaan.
Namun, transaksi lokal tidak dapat menjamin layanan eksternal hanya menjalankan tindakan sekali. Setelah mengirim tetapi sebelum menerima hasil, status bisa ambigu. Pelaksana perlu mendukung idempotency key atau menyediakan cara memeriksa hasil berdasarkan identitas operasi.
Contoh SQLite lokal berikut memperlihatkan deduplikasi niat pengiriman pada satu koneksi. Ia tidak mengirim pesan ke jaringan dan bukan implementasi worker produksi dengan banyak proses:
import hashlib
import json
import sqlite3
def create_demo():
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE jobs (id TEXT PRIMARY KEY, digest TEXT NOT NULL)")
db.execute("CREATE TABLE outbox (operation_key TEXT PRIMARY KEY, body TEXT NOT NULL)")
return db
def enqueue(db, job_id, payload):
body = json.dumps(payload, sort_keys=True, allow_nan=False)
digest = hashlib.sha256(body.encode()).hexdigest()
with db:
old = db.execute("SELECT digest FROM jobs WHERE id=?", (job_id,)).fetchone()
if old:
if old[0] != digest:
raise ValueError("ID pekerjaan dipakai untuk payload berbeda")
return False
db.execute("INSERT INTO jobs VALUES (?,?)", (job_id, digest))
db.execute("INSERT INTO outbox VALUES (?,?)", (f"deliver:{job_id}", body))
return True
db = create_demo()
print(enqueue(db, "brief-042:r3", {"title": "Contoh"}))
print(enqueue(db, "brief-042:r3", {"title": "Contoh"}))
print(db.execute("SELECT count(*) FROM outbox").fetchone()[0])
Hasilnya True, False, lalu 1. Constraint unik membantu menjaga identitas, sedangkan transaksi memastikan kedua insert lokal berhasil bersama atau dibatalkan bersama. Banyak koneksi dan pekerja membutuhkan strategi concurrency yang sesuai database; contoh satu koneksi ini tidak membuktikan ketahanan sistem terdistribusi.
Claim pekerjaan harus atomik
Pada database produksi, dua pekerja tidak boleh sama-sama mengambil baris yang sama tanpa koordinasi. PostgreSQL SELECT menyediakan row locking dan SKIP LOCKED yang dapat digunakan pada pola antrean tertentu. Pemilihan pola tetap perlu mempertimbangkan transaksi, indeks, serta karakter beban kerja.
Simpan pemilik lease dan waktu kedaluwarsa bila pekerjaan berjalan lama. Lease yang habis dapat membuat pekerja lain mengambil alih, sehingga pekerja lama harus diperiksa sebelum melakukan tindakan. Fencing token atau revisi claim dapat membantu menolak pelaksana yang sudah kehilangan kepemilikan.
Jangan menahan transaksi database terbuka selama panggilan model yang lama. Simpan checkpoint dan lepaskan lock sesuai desain. Menahan lock sambil menunggu jaringan dapat menurunkan throughput serta memperbesar konflik.
Retry harus mengenali jenis kegagalan
Timeout, throttling, input tidak valid, dan penolakan akses membutuhkan respons berbeda. Batasi jumlah percobaan serta sisa waktu pekerjaan. Gunakan antrean penanganan gagal untuk kasus yang perlu diperiksa, dengan alasan dan konteks yang cukup.
| Titik kegagalan | Yang perlu diperiksa sebelum mengulang |
|---|---|
| Sebelum generasi | Apakah input dan izin masih valid? |
| Setelah generasi tersimpan | Apakah hasil yang sama dapat dipakai ulang? |
| Setelah request publikasi dikirim | Apakah efek eksternal sudah terjadi? |
| Setelah publikasi berhasil | Apakah hanya pencatatan lokal yang tertinggal? |
| Setelah lease kedaluwarsa | Apakah pekerja ini masih berhak mengeksekusi? |
Mengulang seluruh pipeline dapat menambah biaya model dan membuat keluaran berbeda. Simpan hasil langkah yang telah diterima dengan versi agar retry dapat dimulai dari checkpoint yang tepat.
Uji dengan mematikan proses pada batas yang sulit
Tes sukses biasa tidak cukup. Simulasikan kegagalan setelah insert pekerjaan, sebelum insert outbox, setelah pengiriman eksternal, dan sebelum acknowledgment antrean. Periksa apakah state dapat dipulihkan tanpa kehilangan niat atau menggandakan efek.
Catat jumlah percobaan, umur pekerjaan, waktu antrean, konflik idempotency key, serta tugas yang membutuhkan intervensi. Jumlah pesan diproses bukan ukuran hasil bisnis bila pesan yang sama dihitung berkali-kali.
Untuk deduplikasi, tetapkan pula masa simpan kunci operasi. Jika kunci dihapus lebih cepat daripada kemungkinan pesan lama datang kembali, duplikasi dapat muncul setelah periode yang tampak aman. Sesuaikan retensi dengan umur pesan, strategi replay, serta kebutuhan audit; jangan memilih TTL hanya berdasarkan ukuran tabel.
Untuk alur editorial, kaitkan operasi dengan persetujuan versi. Worker tidak boleh menerbitkan revisi baru menggunakan persetujuan revisi lama. Keandalan pengiriman dan ketepatan objek yang disetujui harus bekerja bersama.