Retry Policy — kebijakan permintaan ulang — seperangkat aturan yang menentukan kapan dan bagaimana aplikasi mobile secara otomatis mengulangi panggilan jaringan yang gagal. Pada koneksi yang tidak stabil atau kesalahan server sementara, kebijakan pengulangan yang tepat meningkatkan keandalan aplikasi tanpa partisipasi pengguna. Menurut penelitian Google Developer Relations (2025), implementasi Retry Policy yang benar mengurangi persentase permintaan yang hilang sebesar 40-60% pada aplikasi mobile dengan operasi jaringan yang sering.
Poin-poin utama
Retry Policy — adalah strategi perangkat lunak yang menentukan perilaku klien saat permintaan jaringan gagal: kesalahan mana yang harus diulang, berapa kali, dengan penundaan berapa, dan kapan harus berhenti mencoba. Dalam aplikasi mobile, kebijakan pengulangan sangat penting karena ketidakstabilan jaringan mobile dan kemungkinan kegagalan sementara di sisi server.
Retry Policy dasar mencakup tiga parameter: jumlah maksimum pengulangan (maxRetries), penundaan awal (baseDelay), dan strategi peningkatan penundaan (backoff strategy). Selain itu, dapat ditentukan daftar kode status HTTP yang harus ditanggapi dengan pengulangan, dan batas waktu untuk menghentikan semua upaya.
Menurut buku “Designing Data-Intensive Applications” Martin Kleppmann, 50% kegagalan dalam sistem terdistribusi bersifat sementara dan dapat diatasi dengan upaya ulang. Hal ini menjadikan Retry Policy salah satu cara paling efektif dan murah untuk meningkatkan toleransi kegagalan aplikasi mobile tanpa perubahan arsitektur server.
Kesalahan sementara (retriable) — satu-satunya jenis kegagalan yang harus ditanggapi oleh Retry Policy. Ini termasuk batas waktu koneksi (SocketTimeoutException), ketidaktersediaan server sementara (HTTP 503, 502), dan kesalahan DNS. Kesalahan permanen — HTTP 400, 401, 403, 404 — tidak ada gunanya diulang, karena menunjukkan masalah pada permintaan, bukan pada jaringan atau server.
Menurut penelitian AWS Architecture Blog, klasifikasi kesalahan yang benar menjadi retriable dan non-retriable adalah keputusan terpenting dalam merancang Retry Policy. Mengulang permintaan non-idempoten dengan HTTP 401 dapat menyebabkan pemblokiran akun, dan mengulang HTTP 400 dapat menyebabkan duplikasi data. Selalu konfigurasikan daftar kode untuk pengulangan secara eksplisit.
Fixed interval — strategi paling sederhana: setiap pengulangan dilakukan setelah interval waktu yang sama. Misalnya, dengan penundaan 2 detik, aplikasi mengulangi permintaan setelah 2, 2, 2 detik. Fixed interval sederhana diimplementasikan dan dapat diprediksi, tetapi menciptakan beban seragam pada server saat kegagalan massal.
Incremental interval — penundaan meningkat secara linear dengan setiap pengulangan: pengulangan pertama setelah 1 detik, kedua setelah 2, ketiga setelah 3, dan seterusnya. Strategi ini memberikan server lebih banyak waktu untuk pulih saat kegagalan berulang, tetapi masih dapat diprediksi untuk banyak klien yang gagal secara bersamaan.
| Strategi | Rumus penundaan | Waktu kumulatif (3 upaya) | Penerapan |
|---|---|---|---|
| Fixed | delay = D | 3 × D | Skenario sederhana, batas waktu lokal |
| Incremental | delay = N × D | 6 × D | Pengurangan beban bertahap |
| Exponential | delay = D × 2^N | 7 × D | Kegagalan massal, layanan cloud |
| Exponential + Jitter | delay = random(0, D × 2^N) | variabel | Beban tinggi, mikrolayanan |
Pemilihan strategi tergantung pada sifat aplikasi. Untuk tugas latar belakang sinkronisasi data pada perangkat mobile, strategi exponential dengan jitter optimal — memberikan probabilitas keberhasilan tertinggi dengan beban minimal pada server dan perangkat pengguna.
Exponential backoff — strategi di mana penundaan antar pengulangan berlipat ganda dengan setiap upaya. Jika penundaan awal adalah 1 detik, maka urutan penundaan akan menjadi 1, 2, 4, 8, 16 detik. Ini memberikan server waktu yang meningkat secara eksponensial untuk pulih.
Jitter — penyimpangan acak penundaan yang mencegah permintaan ulang sinkron dari banyak klien (masalah thundering herd). Tanpa jitter, ribuan klien dengan Retry Policy yang sama akan mengulangi permintaan secara bersamaan, menciptakan beban puncak pada server. Jitter menyebarkan pengulangan dalam waktu.
Coroutine Kotlin memungkinkan implementasi exponential backoff dengan jitter tanpa memblokir thread utama. Fungsi retry dari kotlinx-coroutines menerima kondisi pengulangan dan blok dengan body permintaan, secara otomatis mengelola penundaan dan jumlah upaya.
suspend fun RetryPolicy.executeWithRetry(
block: suspend () -> Result<T>
): Result<T> {
var lastError: Throwable? = null
repeat(maxRetries + 1) { attempt ->
try {
return block()
} catch (e: Exception) {
if (!isRetriable(e) || attempt == maxRetries) {
return Result.failure(e)
}
val delay = (baseDelayMs * (1 shl attempt))
.toLong()
val jitteredDelay = (delay * (0.5 + Random.nextDouble())).toLong()
delay(jitteredDelay)
lastError = e
}
}
return Result.failure(lastError!!)
}
Fungsi executeWithRetry menerima lambda dengan panggilan jaringan dan menjalankannya dengan exponential backoff dan jitter. Jika kesalahan tidak dapat diulang (non-retriable) atau jumlah upaya maksimum terlampaui, fungsi mengembalikan kesalahan. Penundaan dikalikan dengan koefisien acak dari 0,5 hingga 1,5 untuk distribusi pengulangan yang merata.
Circuit Breaker — pola desain yang mencegah permintaan ulang tak terbatas saat layanan tidak tersedia dalam waktu lama. Ketika jumlah kesalahan melebihi ambang batas, Circuit Breaker beralih ke status OPEN dan segera mengembalikan kesalahan tanpa menjalankan permintaan, memberi server waktu untuk pulih.
Dalam aplikasi mobile, Circuit Breaker sangat berguna saat API tidak tersedia karena pemeliharaan terjadwal atau kegagalan jaringan operator. Tanpa itu, aplikasi akan menghabiskan baterai dan lalu lintas untuk upaya ulang tak terbatas, memperburuk pengalaman pengguna dan mengurangi masa pakai baterai perangkat.
Tiga status Circuit Breaker: CLOSED (operasi normal, permintaan dijalankan), OPEN (penolakan, permintaan diblokir), dan HALF_OPEN (permintaan uji untuk memeriksa pemulihan). Setelah batas waktu tertentu dalam status OPEN, sakelar beralih ke HALF_OPEN dan menjalankan satu permintaan — saat berhasil kembali ke CLOSED, saat gagal — ke OPEN.
class CircuitBreaker(
private val failureThreshold: Int = 3,
private val timeoutMs: Long = 30000
) {
private var state = State.CLOSED
private var failureCount = 0
private var lastFailureTime: Long = 0
suspend fun T.protect(block: suspend () -> T): T {
checkState()
return try {
val result = block()
onSuccess()
result
} catch (e: Exception) {
onFailure()
throw e
}
}
}
Implementasi Circuit Breaker di Kotlin berisi penghitung kesalahan dan timer pemulihan. Metode protect memeriksa status saat ini sebelum menjalankan permintaan yang diblokir dan memperbarui penghitung kesalahan saat kegagalan. Setelah mencapai ambang failureThreshold, semua permintaan segera ditolak hingga timeoutMs berakhir.
Jaringan mobile memiliki karakteristik yang membuat Retry Policy sangat penting. Berpindah antara Wi-Fi dan data mobile, kehilangan sinyal di kereta bawah tanah dan terowongan, pemblokiran sementara di level operator — semua skenario ini menyebabkan kegagalan permintaan yang berhasil ditangani dengan upaya ulang.
Di Android, pustaka Retrofit dan OkHttp menyediakan mekanisme RetryPolicy bawaan melalui Interceptor. Di iOS, masalah diselesaikan melalui URLSessionConfiguration dan delegasi kustom. Untuk pengembangan lintas platform, Ktor (KMP) berisi dukungan retry bawaan dengan strategi yang dapat dikonfigurasi.
Combine — kerangka kerja Apple untuk pemrograman reaktif. Operator retry di Combine mengulangi publisher sejumlah kali tertentu saat terjadi kesalahan, tetapi tidak memungkinkan konfigurasi penundaan antar pengulangan. Untuk Retry Policy lengkap, digunakan kombinasi kustom catch dan flatMap dengan penundaan.
extension Publisher {
func retryWithBackoff(
retries: Int = 3,
baseDelay: TimeInterval = 1.0
) -> AnyPublisher<Output, Failure> {
return self.catch { error -> AnyPublisher in
guard retries > 0 else {
return Fail(error).eraseToAnyPublisher()
}
return Just(())
.delay(for: .seconds(baseDelay), scheduler: DispatchQueue.main)
.flatMap { self.retryWithBackoff(
retries: retries - 1,
baseDelay: baseDelay * 2
) }
.eraseToAnyPublisher()
}
.eraseToAnyPublisher()
}
}
Ekstensi retryWithBackoff untuk Publisher di Combine mengimplementasikan exponential backoff melalui panggilan rekursif dengan pengurangan penghitung dan penggandaan penundaan. Operator delay membuat jeda antar pengulangan, dan catch menangkap kesalahan dan memutuskan apakah akan mencoba lagi atau mengembalikan failure.
Kesalahan pertama — mengulangi permintaan tanpa memeriksa idempotensi. Jika server telah membuat sumber daya tetapi tidak mengembalikan konfirmasi karena kegagalan jaringan, permintaan ulang akan membuat duplikat. Untuk permintaan POST, selalu gunakan kunci idempoten (Idempotency-Key) di header atau terapkan retry hanya untuk GET, PUT, dan DELETE.
Kesalahan kedua — pengulangan tak terbatas (retry forever). Selalu tetapkan jumlah upaya maksimum (3-5 untuk aplikasi mobile) dan batas waktu total untuk semua upaya. Pengulangan tak terbatas menghabiskan baterai dan menciptakan beban parasit pada server, terutama saat migrasi basis data atau perubahan API.
Kesalahan ketiga — mengabaikan konteks aplikasi. Jika pengguna menutup aplikasi atau beralih ke mode latar belakang, Retry Policy yang aktif harus dibatalkan dengan benar. Gunakan coroutine dengan SupervisorScope atau Combine dengan siklus hidup UI untuk pembatalan otomatis pengulangan saat layar ditutup.
Kesalahan keempat — tidak mencatat upaya ulang. Tanpa pencatatan, Anda tidak akan tahu berapa banyak permintaan yang diulang, kesalahan apa yang terjadi, dan seberapa efektif Retry Policy Anda. Tambahkan metrik: jumlah pengulangan, keberhasilan setelah pengulangan, distribusi penundaan. Data ini membantu menyesuaikan parameter strategi yang optimal untuk aplikasi tertentu.
Pertanyaan yang sering diajukan
Jumlah pengulangan optimal — 3-5 upaya untuk sebagian besar skenario. Untuk sinkronisasi latar belakang, 5-7 upaya diperbolehkan, untuk permintaan interaktif (misalnya, mengirim formulir) — tidak lebih dari 3. Jumlah pengulangan yang lebih banyak tidak meningkatkan probabilitas keberhasilan, tetapi menghabiskan baterai dan lalu lintas pengguna.
Exponential backoff — penggandaan penundaan antara upaya ulang: 1 detik, 2, 4, 8, 16, dan seterusnya. Jika server kelebihan beban, jeda singkat antara pengulangan pertama memungkinkannya merespons dengan cepat, dan jeda yang meningkat dengan setiap upaya berikutnya memberi server semakin banyak waktu untuk pulih.
Ulangi hanya kesalahan sementara: 408 (Request Timeout), 429 (Too Many Requests), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout). Kesalahan 4xx (kecuali 408 dan 429) menunjukkan masalah klien — mengulanginya tidak ada gunanya dan dapat membahayakan data pengguna.
Retry Policy mengelola pengulangan satu permintaan saat terjadi kegagalan. Circuit Breaker mengelola status koneksi dengan layanan: saat kesalahan menumpuk, ia membuka sirkuit (OPEN) dan tidak mengizinkan permintaan baru. Retry bekerja pada level panggilan individu, Circuit Breaker — pada level integrasi dengan layanan.
Untuk pengujian Retry Policy, gunakan NetworkInterceptor (OkHttp) di Android dan URLProtocol (URLSession) di iOS untuk mensimulasikan kegagalan jaringan. Atur parameter: frekuensi kesalahan, durasi ketidaktersediaan, dan kode respons. Pengujian unit dengan MockWebServer (OkHttp) atau OHHTTPStubs (iOS) memeriksa logika pengulangan tanpa jaringan nyata.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga