Failover RPC & resiliency
Penutup course ini: seluruh arsitektur yang sudah kamu bangun — API, KMS, worker, indexer — bergantung pada satu asumsi diam-diam bahwa node provider selalu tersedia. Materi ini menantang asumsi itu.
Intisari
- Satu node provider (Fase 3) adalah single point of failure — downtime atau rate-limit provider berarti seluruh fitur on-chain aplikasimu ikut lumpuh.
- Pola standarnya: rantai fallback — provider utama, provider cadangan, dan opsional node self-hosted sebagai lapisan terakhir.
- Circuit breaker: setelah beberapa kegagalan berturut-turut dari satu provider, berhenti mencobanya untuk sementara (bukan terus mencoba dan memperlambat semua request) sebelum mencoba lagi.
- Rate limit (HTTP 429) butuh exponential backoff, bukan retry langsung berulang — retry agresif ke provider yang sudah membatasimu hanya memperpanjang periode pembatasan.
- Read (Fase 3) jauh lebih mudah di-failover daripada write — panggilan
eth_callke provider mana pun akan memberi hasil yang sama; transaksi yang sudah terkirim ke satu provider tetap harus dilacak lewat hash-nya, bukan dikirim ulang begitu saja ke provider lain.
Rantai fallback
Request masuk
│
▼
Provider utama (mis. Alchemy) ──gagal/timeout──▶ Provider cadangan (mis. Infura)
│
──gagal/timeout──▶ Node self-hosted (opsional)
│
──gagal──▶ Alert ke tim operasional
Circuit breaker: berhenti mencoba yang jelas sedang bermasalah
Tanpa circuit breaker, aplikasi yang terus mencoba provider yang sedang down akan menghabiskan waktu tunggu (timeout) di setiap request — memperlambat semua pengguna, bukan cuma yang kebetulan kena request pertama yang gagal. Circuit breaker "membuka" setelah beberapa kegagalan berturut-turut, langsung melompat ke provider berikutnya tanpa mencoba yang bermasalah lagi untuk sementara waktu.
public class ProviderDenganFallback {
private final List<Web3j> providers; // urut dari prioritas tertinggi
private final Map<Integer, Integer> kegagalanBerturut = new ConcurrentHashMap<>();
private static final int AMBANG_BATAS = 3;
public EthBlock.Block ambilBlockTerbaru() throws IOException {
for (int i = 0; i < providers.size(); i++) {
if (kegagalanBerturut.getOrDefault(i, 0) >= AMBANG_BATAS) {
continue; // circuit "terbuka" — lewati sementara
}
try {
EthBlock.Block hasil = providers.get(i)
.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false)
.send()
.getBlock();
kegagalanBerturut.put(i, 0); // sukses — reset counter
return hasil;
} catch (IOException e) {
kegagalanBerturut.merge(i, 1, Integer::sum);
log.warn("Provider {} gagal ({}x berturut), coba berikutnya", i,
kegagalanBerturut.get(i));
}
}
throw new IOException("Semua provider gagal — eskalasi ke node self-hosted atau alert");
}
}
Rate limit: backoff, bukan retry membabi buta
HTTP 429 bukan kegagalan biasa — itu instruksi eksplisit "pelan-pelan". Retry langsung berulang ke provider yang baru saja membatasimu hanya memperpanjang periode pembatasan, dan di beberapa provider bisa mengeskalasi jadi pembatasan yang lebih lama. Pola yang benar: exponential backoff — tunggu 1 detik, gagal lagi tunggu 2 detik, gagal lagi tunggu 4 detik, dst., dengan batas maksimum — sebelum akhirnya melompat ke provider fallback kalau backoff pun tidak membantu.
Read vs write saat failover — bedanya penting
Read (eth_call) | Write (eth_sendRawTransaction) | |
|---|---|---|
| Aman di-retry ke provider lain? | Ya — hasilnya selalu sama, provider mana pun | Hati-hati — transaksi mungkin sudah terkirim provider pertama sebelum timeout terjadi |
| Kalau provider A timeout tapi transaksi mungkin sudah masuk mempool | Tidak relevan | Cek dulu eth_getTransactionByHash di provider lain sebelum mengirim ulang — jangan asumsikan gagal |
| Konsekuensi kirim ulang yang keliru | Tidak ada — idempoten secara alami | Berpotensi transaksi terkirim dua kali (kalau nonce berbeda) atau gagal karena nonce bentrok |
Ini alasan kenapa Fase 5.3 menekankan penyimpanan status di database, bukan hanya di memori. Kalau worker tidak yakin apakah transaksi terkirim sebelum koneksi ke provider terputus, langkah pertama selalu memeriksa status hash transaksi (kalau sempat tercatat) lewat provider mana pun — bukan langsung menandatangani dan mengirim transaksi baru dengan nonce yang sama.
Latihan: konfigurasikan dua provider RPC testnet (mis. Alchemy sebagai utama, Infura sebagai
cadangan) di kelas ProviderDenganFallback di atas. Simulasikan kegagalan provider utama
(mis. pakai URL yang salah sengaja) dan verifikasi permintaan otomatis jatuh ke provider cadangan tanpa
perlu restart aplikasi atau intervensi manual — inilah penutup dari seluruh arsitektur yang sudah kamu
bangun sepanjang course ini.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.