Kamu tentu tidak ingin satu ringkasan terbit dua kali hanya karena koneksi bermasalah. Bayangkan proses pembuat ringkasan sudah mengirim hasil ke layanan publikasi, lalu mati sebelum menandai tugas selesai. Antrean memberi tugas yang sama ke proses lain, dan artikel bisa terbit lagi.
Proses pelaksana tugas antrean biasa disebut worker. Dokumentasi SQS standard queue menjelaskan bahwa pesan bisa diterima lebih dari sekali. Aplikasi perlu membuat pengulangan operasi tetap memberi efek yang sama, atau idempoten, sejak merancang pemulihan pekerjaan.
Beri identitas berbeda pada tugas, percobaan, dan tindakan
Satu tugas perlu identitas yang tetap. Setiap percobaan perlu identitas sendiri agar penyebab masalah bisa ditelusuri. Tindakan di luar proses, seperti menerbitkan revisi artikel, adalah efek samping yang memerlukan kunci operasi terkait versi isi permintaan atau payload.
ID pengiriman antrean belum cukup sebagai identitas pekerjaan. Pesan yang sama secara makna bisa dikirim ulang dengan ID pengiriman baru. Kunci yang terlalu luas juga bermasalah, karena revisi baru yang sah harus bisa 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 yang sama datang dengan isi berbeda, tandai sebagai pertentangan. Jangan mengembalikan status berhasil seolah permintaannya sama. Cara itu bisa menyembunyikan perubahan yang seharusnya diperiksa.
Simpan niat pengiriman bersama status pekerjaan
Dua perubahan data bisa dibuat berhasil bersama atau batal bersama lewat transaksi database. Gunakan transaksi untuk menyimpan status tugas dan catatan niat pengirimannya. Tempat menyimpan niat itu disebut outbox, lalu proses lain membacanya untuk menjalankan tindakan di luar aplikasi.
Cara ini menjaga niat pengiriman jika proses mati setelah pekerjaan disimpan. Namun, transaksi lokal belum menjamin layanan lain hanya menjalankan tindakan sekali. Jika permintaan sudah dikirim tetapi jawaban belum diterima, hasilnya bisa belum jelas.
Layanan pelaksana perlu menerima kunci penanda operasi yang aman diulang, atau idempotency key. Pilihan lain adalah menyediakan cara memeriksa hasil dari identitas operasi. Mengulang kirim tanpa pemeriksaan bisa menggandakan tindakan.
Contoh SQLite lokal ini menghilangkan niat pengiriman ganda, atau melakukan deduplikasi, pada satu koneksi. Kode tidak mengirim pesan ke jaringan. Ini juga belum menjadi worker siap pakai untuk 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. Aturan agar identitas tidak kembar, atau constraint unik, menjaga identitas operasi. Transaksi membuat kedua penambahan data lokal berhasil bersama atau dibatalkan bersama.
Banyak koneksi dan worker memerlukan aturan pekerjaan serentak, atau concurrency, yang cocok dengan database. Contoh satu koneksi ini belum membuktikan ketahanan sistem yang berjalan di beberapa proses atau mesin.
Pastikan satu tugas tidak diambil dua worker sekaligus
Pengambilan hak kerja, atau claim, perlu berlangsung utuh tanpa celah bagi worker lain; sifat ini disebut atomik. PostgreSQL SELECT menyediakan penguncian baris atau row locking dan SKIP LOCKED untuk pola antrean tertentu. Pilih polanya dengan melihat transaksi, susunan pencarian atau indeks, dan beban pekerjaan.
Untuk tugas panjang, simpan pemilik hak kerja sementara atau lease, beserta waktu kedaluwarsanya. Jika lease habis, worker lain bisa mengambil alih. Worker lama perlu diperiksa lagi sebelum bertindak.
Penanda urutan kepemilikan, atau fencing token, maupun revisi claim dapat membantu menolak worker yang sudah kehilangan hak. Simpan titik lanjut pekerjaan atau checkpoint, lalu lepaskan kunci sesuai rancangan. Jangan menahan transaksi dan kunci database sambil menunggu panggilan model yang lama, karena itu bisa mengurangi jumlah tugas yang selesai per waktu dan menambah benturan kerja.
Bedakan kegagalan sebelum mencoba lagi
Batas waktu menunggu yang terlampaui atau timeout perlu respons berbeda dari pembatasan laju permintaan atau throttling. Masukan tidak valid dan akses ditolak juga perlu penanganan sendiri. Batasi percobaan ulang, atau retry, sesuai jumlah percobaan dan sisa waktu tugas.
Sediakan antrean untuk tugas gagal yang perlu diperiksa. Simpan alasan dan konteks yang cukup agar kamu tahu langkah berikutnya.
| Titik kegagalan | Yang perlu diperiksa sebelum mengulang |
|---|---|
| Sebelum model membuat hasil | Apakah masukan dan izinnya masih berlaku? |
| Setelah hasil disimpan | Apakah hasil yang sama bisa dipakai lagi? |
| Setelah permintaan publikasi dikirim | Apakah tindakan di luar aplikasi sudah terjadi? |
| Setelah publikasi berhasil | Apakah hanya catatan lokal yang tertinggal? |
| Setelah lease habis | Apakah worker ini masih berhak bertindak? |
Mengulang seluruh rangkaian kerja, atau pipeline, bisa menambah biaya model dan menghasilkan jawaban berbeda. Simpan hasil langkah yang sudah diterima beserta versinya. Dengan begitu, retry bisa mulai dari checkpoint yang tepat.
Coba matikan proses pada saat yang sulit
Tes yang selalu berhasil belum cukup. Simulasikan proses mati setelah menambah data pekerjaan, sebelum menambah outbox, setelah pengiriman eksternal, dan sebelum memberi tanda selesai ke antrean atau acknowledgment. Periksa apakah keadaan tugas bisa pulih tanpa kehilangan niat pengiriman atau menggandakan tindakan.
Catat jumlah percobaan, umur tugas, waktu menunggu antrean, konflik kunci operasi, dan tugas yang membutuhkan bantuan. Banyaknya pesan yang diproses belum menunjukkan banyaknya pekerjaan selesai bila pesan yang sama dihitung berkali-kali.
Tentukan juga masa simpan kunci operasi. Jika kunci dibuang sebelum pesan lama mungkin datang kembali, duplikasi bisa muncul setelah masa yang tampak aman. Sesuaikan batas umur atau TTL dengan umur pesan, rencana memutar ulang pekerjaan atau replay, dan kebutuhan pemeriksaan riwayat atau audit, bukan hanya ukuran tabel.
Dalam pekerjaan redaksi, hubungkan operasi dengan persetujuan versi. Worker tidak boleh menerbitkan revisi baru memakai persetujuan revisi lama. Pengiriman yang andal perlu tetap mengarah ke isi yang memang sudah disetujui.