SQL Query Timeout Menggunakan Golang Context

SQL Query Timeout Menggunakan Golang Context

Salah satu kegunaan Golang Context adalah untuk mengontrol timeout dan metode cancelation. Kita bisa menggunakannya untuk mengatur timeout pada eksekusi query SQL. Kita juga dapat menghentikan eksekusi yang sedang berjalan apabila kita tidak memerlukannya lagi. Artikel ini akan menunjukkan bagaimana cara melakukannya.

Kita menerapkan timeout untuk membuat penggunaan resource lebih efisien. Pada SQL, kita melakukan hal tersebut dengan membatasi waktu eksekusi dari query. Kita juga dapat menghentikan query tersebut apabila sudah tidak diperlukan. Contoh kasus: HTTP server kita melakukan query ke database untuk memproses request. Eksekusi query masih berjalan, tetapi client sudah memutuskan koneksi ke server. By default, query akan terus di eksekusi sampai hasil didapatkan. Tapi karena client sudah memutuskan koneksi, kita dapat menghentikan eksekusi query karena sudah tidak dibutuhkan lagi. Dengan melakukan ini, kita dapat menghemat penggunaan resource dan meningkatkan efisiensi.

Query Tanpa Go Context

Query tanpa Context akan dijalankan sampai kita mendapatkan hasil. Ini kurang bagus, karena apabila query memakan waktu lama, client kemungkinan sudah timeout / mengcancel requestnya.
Kita akan gunakan query SELECT sleep(15) untuk mensimulasikan eksekusi query yang lama. Code Golang nya akan seperti ini:

var db *sql.DB

func main() {
	dtbs, err := sql.Open("mysql", "myuser:mypassword@/jajaldoang")
	if err != nil {
		panic(err.Error()) // just for example
	}
	db = dtbs

	http.HandleFunc("/get_something", myHandler)

	println("listening..")
	http.ListenAndServe(":5005", nil)
}

func myHandler(w http.ResponseWriter, r *http.Request) {
	_, err := db.Query("SELECT sleep(15)")
	if err != nil {
        log.Println("error query: " + err.Error())
		w.Write([]byte("something's wrong: " + err.Error()))
		return
	}

	w.Write([]byte("success"))
}

Ketika myHandler dipanggil, kita akan gunakan show full processlist; untuk melihat query yang sedang berjalan.

show full processlist result

Prosesnya akan terlihat di situ sampai 15 detik berlalu.

Cara Menggunakan Go Context pada SQL Query

Kita menggunakan fungsi QueryContext untuk menjalankan query menggunakan context. Fungsi ini menerima context.Context sebagai argument pertama. Apabila context tersebut di cancel atau timeout, eksekusi query juga akan dihentikan. Untuk query INSERT or UPDATE, kamu bisa gunakan fungsi ExexContext.

func myHandler(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	_, err := db.QueryContext(ctx, "SELECT sleep(15)")

Pada contoh diatas, kita membuat sebuah context dengan timeout 3 detik. Context tersebut merupakan turunan dari context background. Setelah 3 detik, context akan timeout dan menghentikan query saat itu juga. Ayo kita coba test.

Coba hit API get_something

curl -w '\n%{time_total}' http://localhost:5005/get_something
something's wrong: context deadline exceeded
3.017303

Akan terlihat bahwa kita mendapatkan response sekitar 3 detik. Coba kita lihat processlist di mysql.

show full processlist result 2

Proses nya hilang tanpa harus menunggu hasil dari query SELECT sleep(15).

Cara Menggunakan Context dari http.Request

Pada contoh sebelumnya, kita membuat context baru dari background context. Background context merupakan context kosong. Dia tidak memiliki deadline dan fungsi cancelation. Jadi apabila kita membuat context dengan timeout dari background context, satu-satunya fungsi cancelation context tersebut adalah timeout yang kita set. Bagaimana kalau sebelum 3 detik, client disconnect atau mencancel request ke server. Kita bisa menghandle kasus ini dengan menggunakan context dari http.Request. Untuk melakukan ini, kita harus membuat context bukan dari background context, tetapi dari request.Context().

func myHandler(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
	defer cancel()

	_, err := db.QueryContext(ctx, "SELECT sleep(15)")

Coba hit API dan cancel sebelum 3 detik. Kalau menggunakan curl, kita bisa cancel dengan CTRL + C. Query akan dihentikan dan menghasilkan error context canceled. Dengan context ini, query akan berhenti diproses kalau timeout sudah lewat atau HTTP request dicancel, tergantung mana yang lebih dulu terjadi.

Kesimpulan

Menjalankan query dengan context membuatmu dapat mengontrol timeout dan cancelation. Ini merupakan hal yang bagus karena kita dapat menggunakan resource lebih efisien. Ini juga dapat menghindari query yang berjalan lama menumpuk sehingga dapat menghabiskan resource kita.

go  sql 

See also

comments powered by Disqus