Skip to content

Domain, hosting, code and data — in your name, in writing

jagaweb.

Aplikasi Web Tersuai & Middleware LHDN

Corak Kebolehpercayaan Untuk Laman Web Yang Berkomunikasi Dengan Sistem Pihak Lain

Bacaan 8 minitOleh JagaWeb

Timeout, backoff, idempotency key dan dead-letter queue diterangkan secara konkrit, dengan piawaian di sebalik setiap dakwaan disebut.

Soalannya bukan sama ada ia akan gagal

Setiap laman web yang bertukar data dengan sistem luaran — payment gateway, API kerajaan, CRM — akhirnya akan menerima sesuatu yang tidak dijangka: satu timeout, respons yang cacat (malformed), sambungan yang terputus separuh jalan, atau tiada apa-apa langsung. Itu tidak benar-benar boleh direka bentuk untuk dielakkan; rangkaian dan sistem pihak ketiga gagal mengikut jadual mereka sendiri, bukan jadual anda. Soalan reka bentuk yang benar-benar penting ialah apa yang berlaku seterusnya, dan sama ada "apa yang berlaku seterusnya" itu satu keputusan yang dibuat oleh seseorang, atau sekadar apa sahaja yang menjadi gelagat lalai (default) sesuatu HTTP library.

Timeout: memutuskan berapa lama sudah terlalu lama

Jika dibiarkan pada tetapan lalai, HTTP client boleh menunggu jauh lebih lama daripada yang dimaksudkan sesiapa untuk respons yang tidak akan pernah tiba — mengikat thread server, tab pelayar, atau kesabaran pelanggan pada masa yang sama. Timeout memerlukan dua nilai berasingan, bukan satu: berapa lama untuk menunggu sambungan dibuka, dan berapa lama untuk menunggu respons sebaik sahaja sambungan terbuka. API yang perlahan dan terlebih beban, dan satu yang langsung tidak boleh dicapai, gagal dengan cara yang berbeza, dan sistem yang melayan setiap kegagalan secara sama — mengulang (retry) permintaan yang tidak akan pernah berjaya, atau berputus asa serta-merta untuk satu yang hanya perlukan sesaat lagi — sedang meneka, bukan membuat keputusan.

Retry dengan backoff, bukan sekadar retry

Mengulang permintaan yang gagal, dengan sendirinya, bukan satu strategi — ia satu andaian bahawa kegagalan itu sementara, dan andaian itu selalunya salah pada saat ia paling penting: apabila sistem luaran sedang bertatih di bawah beban. Jika setiap client retry serta-merta apabila gagal, retry itu sendiri menambah beban kepada sistem yang sudah pun gagal, dalam satu lonjakan yang tiba tepat pada masa ia paling tidak mampu menyerapnya. Penulisan seni bina AWS sendiri mengenai topik ini, kebanyakannya dibina atas ujian pelbagai strategi retry di bawah keadaan pertikaian (contention), mengesyorkan exponential backoff dengan jitter rawak ditambah pada setiap selang menunggu, berbanding kelewatan tetap: menjarakkan retry, dan menyebarkannya secara rawak berbanding serentak, secara terukur mengurangkan jumlah kerja sia-sia berbanding mengulang serta-merta atau pada kelewatan tetap (AWS Architecture Blog — Exponential Backoff and Jitter). Separuh lagi strategi ialah had maksimum yang tegas: bilangan cubaan maksimum dan jumlah masa menunggu maksimum, supaya dependency yang bermasalah menjejaskan (degrade) satu permintaan dan bukan menggantungkannya tanpa had.

Idempotensi (idempotency): kenapa permintaan yang sama boleh tiba dua kali

Ini bahagian yang paling kerap tersilap dalam integrasi, biasanya kerana mod kegagalannya tidak intuitif sehinggalah diterangkan secara konkrit. Seorang client menghantar permintaan untuk mencipta pesanan. Server menerimanya, memprosesnya, dan mencipta pesanan itu — tetapi respons tidak sampai kembali kepada client, kerana sambungan terputus sesaat terlalu awal. Dari sudut pandangan client, ini tidak dapat dibezakan daripada permintaan yang langsung tidak pernah diterima; ia tiada cara untuk tahu pesanan itu sebenarnya telah dicipta. Jika ia mengikut polisi retry dan menghantar semula permintaan yang sama, dan server tiada cara mengenali permintaan itu sebagai ulangan, pesanan kedua tercipta. Tiada siapa buat kesilapan; sistem berkelakuan tepat seperti mana ia dibina. Pendua (duplicate) itu ialah akibat struktur daripada retry ke atas rangkaian yang tidak boleh dipercayai sepenuhnya, bukan bug pada mana-mana komponen.

Spesifikasi HTTP sendiri membuat perbezaan sebenar di sini. RFC 9110 mentakrifkan GET, HEAD, PUT, DELETE, OPTIONS, dan TRACE sebagai kaedah idempoten — bermaksud, mengikut definisi, menghantar permintaan yang sama berkali-kali mempunyai kesan yang sama seperti menghantarnya sekali — manakala POST dan PATCH tidak idempoten secara lalai (RFC 9110, §9.2.2). Itulah sebabnya secara umumnya selamat untuk pelayar atau proksi mengulang secara senyap satu GET yang gagal, dan tidak selamat untuk secara senyap mengulang satu POST yang mencipta sesuatu baharu setiap kali ia berjaya — spesifikasi itu sememangnya tidak menjanjikan cubaan kedua akan berkelakuan sama seperti yang pertama.

Penyelesaian praktikal untuk operasi seakan-POST ialah idempotency key: client menjana pengecam unik untuk setiap operasi logik — biasanya UUID — dan menghantarnya bersama permintaan. Server merekodkan hasilnya terhadap key itu, dan jika key yang sama tiba lagi, ia memulangkan hasil yang tersimpan dan bukan mengulang operasi itu. Corak ini diterangkan dalam satu IETF Internet-Draft, "The Idempotency-Key HTTP Header Field" (datatracker.ietf.org), yang — perlu dijelaskan dengan tepat — masih kekal sebagai draf dan bukan piawaian yang diratifikasi setakat ia disemak. Status formalnya tidak mengubah mekanisme yang mendasarinya, yang sudah pun menjadi cara pelbagai API pengeluaran (production) sebenar menangani masalah yang sama ini, sama ada ia piawaian rasmi atau tidak: dedupe pada satu key yang dikawal oleh client, bukan berharap rangkaian akan berkelakuan baik.

// Illustrative only — load secrets from environment variables, never hardcode them
const idempotencyKey = crypto.randomUUID();

await fetch(process.env.PAYMENT_API_URL, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.PAYMENT_API_KEY}`,
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify(orderPayload),
});

// A retry after a timeout reuses the same idempotencyKey,
// so the server can recognise it as the same logical operation.

(Kod di atas hanya ilustrasi, dan dikekalkan seperti asal. Perkara penting untuk diingat: sentiasa muatkan secrets seperti kunci API daripada environment variables — dalam contoh ini process.env.PAYMENT_API_URL dan process.env.PAYMENT_API_KEY — dan jangan sekali-kali hardcode nilai sebenar terus ke dalam kod.)

Queue dan pengendalian dead-letter

Bukan setiap kegagalan patut di-retry serta-merta oleh permintaan yang sama yang mencetuskannya. Untuk kerja yang tidak perlu selesai dalam masa page load pelanggan — menghantar pengesahan, menyegerakkan (sync) rekod ke sistem lain, menghantar penyerahan (filing) — satu queue diletakkan antara "kerja yang perlu berlaku" dan "sistem yang melakukannya," supaya gangguan sistem hiliran (downstream) melewatkan kerja itu dan bukan menghilangkannya. Queue itu retry mengikut jadualnya sendiri, berasingan daripada permintaan yang mencipta job itu, dan mesej yang terus gagal selepas satu bilangan cubaan yang ditetapkan dipindahkan ke tempat ia boleh diperiksa, bukan di-retry selama-lamanya atau dijatuhkan secara senyap. Dokumentasi dead-letter queue Amazon SQS menerangkan tujuan ini dengan jelas: mengasingkan mesej yang berulang kali gagal diproses supaya ia boleh diperiksa dan didiagnosis, bukan dibiarkan retry tanpa henti atau hilang (AWS documentation — Using dead-letter queues in Amazon SQS). Pelaksanaan sebenar berbeza mengikut platform, tetapi corak yang mendasarinya — satu tempat yang ditetapkan untuk kegagalan yang belum diperiksa sesiapa — patut dibina secara sengaja dan bukan ditemui secara tidak sengaja apabila satu queue penuh tanpa disedari.

Kenapa "ia berfungsi semasa ujian" bukan bukti

Persekitaran staging biasanya lebih pantas, lebih boleh dipercayai, dan lebih memaafkan berbanding production, dan itulah sebabnya tepat ia bukan ujian yang baik untuk pengendalian kegagalan. Kelayakan (credentials) sandbox untuk API luaran jarang meniru had kadar (rate limit) production sebenar, corak gangguan sebenarnya, atau masa respons di bawah beban sebenar. Dan pembangun yang menguji integrasi mereka sendiri secara semula jadi menguji laluan yang sepatutnya berfungsi jauh lebih kerap berbanding laluan yang sepatutnya gagal. Demo yang berfungsi membuktikan laluan gembira (happy path) berfungsi. Ia tidak menunjukkan apa-apa tentang apa yang berlaku apabila satu permintaan timeout tepat pada masa yang salah, tiba dua kali, atau menerima respons yang kod itu tidak ditulis untuk kendalikan. Satu-satunya cara untuk tahu ialah dengan sengaja mensimulasikan keadaan itu — timeout yang dipaksa, sambungan yang digugurkan, penyerahan pendua — sebelum go-live, dan terus memerhatikannya selepas itu, kerana kegagalan integrasi dalam realiti cenderung muncul semasa beban sebenar atau semasa tempoh penyelenggaraan sistem luaran itu sendiri, bukan semasa ujian berskrip.

Jika ini sudah live dan tiada siapa pasti

Untuk laman web yang sudah berkomunikasi dengan payment gateway, bank, atau sistem kerajaan, dan di mana tiada siapa benar-benar yakin apa yang berlaku apabila sambungan itu gagal separuh transaksi, Essential System Review JagaWeb (RM1,500, dikurangkan kepada RM999 sehingga 16 September 2026, tidak termasuk SST) boleh melihat khusus bagaimana kegagalan dikendalikan pada masa ini dan melaporkannya secara jujur. Membina integrasi baharu dengan corak-corak ini direka sejak awal ialah kerja Fixed-Scope Project, bermula daripada RM30,000.

WhatsApp