Golang Function Timeout Dengan Context

Timeout bisa sangat berguna pada suatu aplikasi. Dia dapat membatasi berapa lama durasi maksimum suatu proses. Kita dapat menghemat resource dengan menghentikan proses ketika waktu timeout sudah terlewati. Pada bahasa pemrograman Go, kita dapat menggunakan context untuk mengatur timeout pada suatu fungsi.

Untuk menggunakan timeout context pada sebuah fungsi, kita memerlukan langkah-langkah berikut:

  • membuat context dengan timeout
  • membuat sebuah channel untuk menandai suatu fungsi sudah selesai dijalankan
  • panggil fungsi tersebut dalam goroutine
  • tandai kalau fungsi sudah selesai dijalankan di channel yang sudah dibuat
  • tunggu mana yang duluan terjadi, apakah context timeout atau fungsi selesai

Salah satu penggunaan yang paling umum adalah di middleware. Context dibuat dengan timeout untuk membatasi waktu proses handler dan memberikan pesan error kalau timeout. Jadi client tidak menunggu terlalu lama untuk mendapatkan response.

Context timeout di middleware

Code dibawah ini adalah sebuah middleware untuk menambahkan timeout pada handler.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func processTimeout(h http.HandlerFunc, duration time.Duration) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        ctx, cancel := context.WithTimeout(r.Context(), duration)
        defer cancel()

        r = r.WithContext(ctx)

        processDone := make(chan bool)
        go func() {
            h(w, r)
            processDone <- true
        }()

        select {
        case <-ctx.Done():
            w.Write([]byte(`{"error": "process timeout"}`))
        case <-processDone:
        }
    }
}
Untuk menggunakan middleware tersebut:
http.HandleFunc("/get_cities", processTimeout(myHttpHandler, 5*time.Second))

Pada line 3 kita membuat context baru dengan timeout kemudian membuat http.Request baru dengan context tersebut pada line 6. Lalu http.Request inilah yang akan digunakan pada handler. Pada baris 8 kita membuat sebuah channel untuk menandai kalau handler sudah selesai dijalankan.

Kita memanggil fungsi handler pada sebuah goroutine function dan mengirimkan flag ke channel ketika proses handler selesai. Karena handler dijalankan dalam goroutine, program akan dilanjutkan tanpa menunggu handler selesai. Kemudian kita block menggunakan statement select untuk menunggu yang mana selesai duluan, antara context timeout atau proses handler. Kalau context timeout terjadi lebih dulu, kita akan memberikan response error ke client.

Context timeout tidak hanya dapat dihandle di middleware tapi juga bisa di dalam handler. Kita bisa mendeteksi context timeout dan menghentikan eksekusi. Salah satu contoh penggunaannya adalah dengan menggunakan context dari http.Request pada eksekusi sql query. Dengan context, kita dapat menghentikan proses karena kita sudah memberikan error response ke client. Jadi akan percuma untuk melanjutkan proses setelah context timeout. Kecuali pada case tertentu dimana kita mau eksekusi tetap berjalan walaupun client tidak langsung mendapatkan hasilnya.

Kesimpulan

Golang context memiliki timeout yang dapat kita gunakan untuk membatasi durasi dari sebuat function. Kita dapat menggunakannya di middleware untuk membatasi lama proses handler. Handler juga dapat menggunakan context tersebut untuk menghentikan proses apabila terjadi timeout. Dengan melakukan ini kita dapat menghemat resource sehingga dapat digunakan untuk proses yang lain.


See also