Bab 1: Pengenalan tentang Penyelarasan (Retrying) di Go

1.1 Memahami Perlunya Mekanisme Penyelarasan

Pada berbagai skenario komputasi, terutama ketika berurusan dengan sistem terdistribusi atau komunikasi jaringan, operasi dapat gagal akibat kesalahan sementara. Kesalahan-kesalahan ini seringkali merupakan masalah sementara seperti ketidakstabilan jaringan, tidak tersedianya layanan dalam jangka waktu singkat, atau waktu habis. Alih-alih gagal dengan segera, sistem seharusnya dirancang untuk melakukan penyelarasan (retry) terhadap operasi yang mengalami kesalahan sementara. Pendekatan ini meningkatkan kehandalan dan ketahanan.

Mekanisme penyelarasan dapat sangat penting dalam aplikasi di mana konsistensi dan kelengkapan operasi sangat diperlukan. Mekanisme ini juga dapat mengurangi tingkat kesalahan yang dialami pengguna akhir. Namun, mengimplementasikan mekanisme penyelarasan tidaklah mudah, seperti menentukan seberapa sering dan seberapa lama melakukan penyelarasan sebelum menyerah. Di situlah strategi kembali (backoff strategies) memainkan peran yang signifikan.

1.2 Tinjauan tentang Pustaka go-retry

Pustaka go-retry dalam Go memberikan cara fleksibel untuk menambahkan logika penyelarasan ke aplikasi Anda dengan berbagai strategi kembali. Fitur utamanya meliputi:

  • Fleksibilitas: Sama seperti paket http dalam Go, go-retry dirancang agar dapat diperluas dengan middleware. Anda bahkan dapat menulis fungsi penyelarasan Anda sendiri atau menggunakan filter-filter yang disediakan.
  • Independensi: Pustaka ini hanya mengandalkan pustaka standar Go dan menghindari ketergantungan eksternal, sehingga menjaga proyek Anda ringan.
  • Konkurensi: Aman digunakan secara bersamaan dan dapat bekerja dengan goroutine tanpa kerumitan tambahan.
  • Sadar konteks: Mendukung konteks asli Go untuk timeout dan pembatalan, dengan integrasi yang lancar dengan model konkurensi Go.

Bab 2: Mengimpor Pustaka-Pustaka

Sebelum Anda dapat menggunakan pustaka go-retry, pustaka tersebut perlu diimpor ke dalam proyek Anda. Hal ini dapat dilakukan dengan menggunakan go get, yang merupakan perintah Go untuk menambahkan dependensi ke modul Anda. Cukup buka terminal Anda dan eksekusi:

go get github.com/sethvargo/go-retry

Perintah ini akan mengambil pustaka go-retry dan menambahkannya ke dependensi proyek Anda. Setelah itu, Anda dapat mengimpornya ke dalam kode Anda seperti halnya paket Go lainnya.

Bab 3: Mengimplementasikan Logika Penyelarasan Dasar

3.1 Penyelarasan Sederhana dengan Backoff Konstan

Bentuk paling sederhana dari logika penyelarasan melibatkan menunggu durasi waktu konstan antara setiap percobaan penyelarasan. Anda dapat menggunakan go-retry untuk melakukan penyelarasan dengan backoff konstan.

Berikut contoh penggunaan backoff konstan dengan go-retry:

package main

import (
  "context"
  "time"
  "github.com/sethvargo/go-retry"
)

func main() {
    ctx := context.Background()
    
    // Membuat backoff konstan baru
    backoff := retry.NewConstant(1 * time.Second)

    // Bungkus logika penyelarasan dalam fungsi yang akan dilewatkan ke retry.Do
    operation := func(ctx context.Context) error {
        // Kode Anda di sini. Kembalikan retry.RetryableError(err) untuk melakukan penyelarasan, atau nil untuk berhenti.
        // Contoh:
        // err := someOperation()
        // if err != nil {
        //   return retry.RetryableError(err)
        // }
        // return nil

        return nil
    }
    
    // Gunakan retry.Do dengan konteks yang diinginkan, strategi backoff, dan operasi
    if err := retry.Do(ctx, backoff, operation); err != nil {
        // Tangani kesalahan
    }
}

Pada contoh ini, fungsi retry.Do akan terus mencoba menjalankan fungsi operation setiap 1 detik sampai berhasil atau konteks berakhir atau dibatalkan.

3.2 Mengimplementasikan Backoff Eksponensial

Backoff eksponensial meningkatkan waktu penungguan antara penyelarasan secara eksponensial. Strategi ini membantu mengurangi beban pada sistem dan sangat berguna ketika berurusan dengan sistem skala besar atau layanan cloud.

Cara menggunakan backoff eksponensial dengan go-retry adalah sebagai berikut:

package main

import (
  "context"
  "time"
  "github.com/sethvargo/go-retry"
)

func main() {
    ctx := context.Background()

    // Membuat backoff eksponensial baru
    backoff := retry.NewExponential(1 * time.Second)

    // Sediakan operasi yang dapat diulang kembali
    operation := func(ctx context.Context) error {
        // Implementasikan operasi seperti yang ditunjukkan sebelumnya
        return nil
    }
    
    // Gunakan retry.Do untuk menjalankan operasi dengan backoff eksponensial
    if err := retry.Do(ctx, backoff, operation); err != nil {
        // Tangani kesalahan
    }
}

Dalam kasus backoff eksponensial, jika backoff awal diatur menjadi 1 detik, penyelarasan akan terjadi setelah 1 detik, 2 detik, 4 detik, dan seterusnya, meningkatkan waktu tunggu antara penyelarasan berikutnya secara eksponensial.

3.3 Strategi Pengunduran Fibonacci

Strategi pengunduran Fibonacci menggunakan deret Fibonacci untuk menentukan waktu tunggu antara percobaan ulang, yang dapat menjadi strategi yang baik untuk masalah terkait jaringan di mana peningkatan waktu tunggu secara bertahap bermanfaat.

Implementasi pengunduran Fibonacci dengan go-retry ditunjukkan di bawah ini:

package main

import (
  "context"
  "time"
  "github.com/sethvargo/go-retry"
)

func main() {
    ctx := context.Background()

    // Membuat pengunduran Fibonacci baru
    backoff := retry.NewFibonacci(1 * time.Second)

    // Mendefinisikan operasi untuk mencoba ulang
    operation := func(ctx context.Context) error {
        // Di sini akan berisi logika untuk melakukan tindakan yang mungkin gagal dan perlu dicoba ulang
        return nil
    }
    
    // Melakukan operasi dengan pengunduran Fibonacci menggunakan retry.Do
    if err := retry.Do(ctx, backoff, operation); err != nil {
        // Menangani kesalahan
    }
}

Dengan pengunduran Fibonacci dengan nilai awal 1 detik, percobaan ulang akan terjadi setelah 1 detik, 1 detik, 2 detik, 3 detik, 5 detik, dll., mengikuti deret Fibonacci.

Bab 4: Teknik Pengulangan Lanjutan dan Middleware

4.1 Memanfaatkan Jitter dalam Pengulangan

Ketika menerapkan logika percobaan ulang, penting untuk mempertimbangkan dampak percobaan ulang simultan pada sistem, yang dapat menyebabkan masalah hewan besar. Untuk mengurangi masalah ini, kita dapat menambahkan jitter acak ke interval pengunduran. Teknik ini membantu mempersempit upaya percobaan ulang, mengurangi kemungkinan beberapa klien memulai percobaan ulang secara serentak.

Contoh penambahan jitter:

b := retry.NewFibonacci(1 * time.Second)

// Mengembalikan nilai berikutnya, +/- 500ms
b = retry.WithJitter(500 * time.Millisecond, b)

// Mengembalikan nilai berikutnya, +/- 5% dari hasil
b = retry.WithJitterPercent(5, b)

4.2 Menetapkan Jumlah Percobaan Maksimum

Dalam beberapa skenario, diperlukan untuk membatasi jumlah percobaan ulang untuk mencegah percobaan ulang yang terus-menerus dan tidak efektif. Dengan menetapkan jumlah percobaan ulang maksimum, kita dapat mengontrol jumlah percobaan sebelum menyerah pada operasi.

Contoh menetapkan percobaan maksimum:

b := retry.NewFibonacci(1 * time.Second)

// Berhenti setelah 4 percobaan ulang, ketika percobaan ke-5 gagal
b = retry.WithMaxRetries(4, b)

4.3 Pembatasan Durasi Pengunduran Individu

Untuk memastikan bahwa durasi pengunduran individu tidak melebihi ambang tertentu, kita dapat menggunakan middleware CappedDuration. Ini mencegah interval pengunduran yang terlalu lama dihitung, menambahkan ketepatan pada perilaku percobaan ulang.

Contoh pembatasan durasi pengunduran individu:

b := retry.NewFibonacci(1 * time.Second)

// Pastikan nilai maksimumnya adalah 2 detik
b = retry.WithCappedDuration(2 * time.Second, b)

4.4 Mengendalikan Total Durasi Percobaan Ulang

Dalam skenario di mana perlu ada batasan pada durasi total untuk seluruh proses percobaan ulang, middleware WithMaxDuration dapat digunakan untuk menentukan waktu eksekusi total maksimum. Ini memastikan bahwa proses percobaan ulang tidak berlanjut secara tak terbatas, memberlakukan anggaran waktu untuk percobaan ulang.

Contoh mengendalikan total durasi percobaan ulang:

b := retry.NewFibonacci(1 * time.Second)

// Pastikan waktu total maksimum percobaan ulang adalah 5 detik
b = retry.WithMaxDuration(5 * time.Second, b)