NSQ: Requeue vs Requeue Without Backoff

NSQ memungkinkan kita untuk me-requeue message dengan jeda waktu tertentu. Ada dua fungsi yang bisa digunakan, yaitu Requeue dan RequeueWithoutBackoff. Pada artikel ini akan dijelaskan tentang perbedaannya sehingga dapat membantu untuk menentukan yang mana yang lebih cocok dengan use case mu.

Saya menggunakan go-nsq (https://github.com/nsqio/go-nsq) sebagai client library. Ini adalah client library official untuk Go. Di bawah ini adalah contoh dari fungsi untuk meng-consume message.

    func myConsumer(msg *nsq.Message) error {
        msgStr := string(msg.Body)
        log.Printf("consuming %s\n", msgStr)
        msq.Finish()
        return nil
    }

Pada contoh di atas, kita memanggil fungsi Finish milik message. Ini akan memberitahu nsqd bahwa message sudah selesai di consume. Kita dapat merequeue message dengan memanggil fungsi Requeue atau RequeueWithoutBackoff. Tapi apakah perbedaan antara kedua fungsi tersebut? Dan yang mana yang lebih baik kita gunakan?

Requeue

Fungsi Requeue mengembalikan message ke antrian dengan jeda waktu tertentu. Message ini akan di-consume lagi setelah jeda waktu tersebut dilewati. Fungsi Requeue juga memberitahu consumer untuk back off sampai jeda waktu tertentu. Ini berarti consumer tidak akan meng-consume message apapun sampai waktu back off berakhir. Contoh penggunaan fungsi Requeue:

    func myConsumer(msg *nsq.Message) error {
        msgStr := string(msg.Body)
        log.Printf("[myConsumer] consuming: %s\n", msgStr)
        if msgStr == "message1" {
            msg.Requeue(10 * time.Second)
        }

        return nil
    }

Consumer akan me-requeue message dengan jeda waktu 10 detik kalau dia meng-consume message1. Coba kita lihat apa yang terjadi pada message dan consumer nya. Kita mempublish beberapa message dalam waktu bersamaan untuk mengetest consumer dengan command ini:

myuser :: ~ » curl -X POST http://localhost:4151/mpub\?topic\=mytopic -d 'message1
message2
message3'

Coba kita lihat log yang dihasilkan.

2020/09/17 14:45:40 INF    1 [mytopic/mychannel] querying nsqlookupd http://localhost:4161/lookup?topic=mytopic
2020/09/17 14:45:40 INF    1 [mytopic/mychannel] (192.168.100.17:4150) connecting to nsqd
2020/09/17 14:45:40 [myConsumer] consuming: message1
2020/09/17 14:45:40 WRN    1 [mytopic/mychannel] backing off for 2s (backoff level 1), setting all to RDY 0
2020/09/17 14:45:42 WRN    1 [mytopic/mychannel] (192.168.100.17:4150) backoff timeout expired, sending RDY 1
2020/09/17 14:45:42 [myConsumer] consuming: message2
2020/09/17 14:45:42 WRN    1 [mytopic/mychannel] exiting backoff, returning all to RDY 1
2020/09/17 14:45:42 [myConsumer] consuming: message3
2020/09/17 14:45:50 [myConsumer] consuming: message1
2020/09/17 14:45:50 WRN    1 [mytopic/mychannel] backing off for 2s (backoff level 1), setting all to RDY 0

Consumer meng-consume message1 pada 14:45:40. Kita me-requeue message ini dengan delay 10 detik. Kita bisa lihat pada baris selanjutnya terdapat ...backing off for 2s..., ini berarti consumer tidak akan meng-consume message apapun sampai 2 detik setelahnya. Ini berguna apabila kita me-requeue karena terdapat system error dan ingin memberikan istirahat ke system kita. Ini juga dapat berguna sebagai circuit breaker. Tapi ini dapat berakibat buruk apabila kita hanya ingin me-requeue message tertentu saja karena dapat mem-back off consumer terus menerus dan membuat message menumpuk.

RequeueWithoutBackoff

Jadi apakah itu RequeueWithoutBackoff? Sama seperti namanya, ini fungsi untuk me-requeue message tanpa mem-back off consumer. Ayo kita lihat bagaimana dia bekerja. Ini adalah code untuk consumer yang kita gunakan.

    func myConsumer(msg *nsq.Message) error {
        msgStr := string(msg.Body)
        log.Printf("[myConsumer] consuming: %s\n", msgStr)
        if msgStr == "message1" {
            msg.Requeue(10 * time.Second)
        }

        return nil
    }

Kita publish beberapa message pada waktu bersamaan untuk mengetest lalu lihat log yang dihasilkan.

2020/09/17 15:42:12 INF    1 [mytopic/mychannel] querying nsqlookupd http://localhost:4161/lookup?topic=mytopic
2020/09/17 15:42:12 INF    1 [mytopic/mychannel] (192.168.100.17:4150) connecting to nsqd
2020/09/17 15:42:12 [myConsumer] consuming: message1
2020/09/17 15:42:12 [myConsumer] consuming: message2
2020/09/17 15:42:12 [myConsumer] consuming: message3
2020/09/17 15:42:22 [myConsumer] consuming: message1

Bisa dilihat ketika kita me-requeue message1, consumer tetap meng-consume message yang lain. Kita bisa menggunakan ini untuk me-requeue message dan biarkan consumer melanjutkan kerjanya.

Kesimpulan

Kita dapat menggunakan Requeue untuk me-requeue message kalau terjadi error pada system dan kita ingin memutus circuit untuk mencegah error pada message-message yang lain. Untuk selain itu, kita gunakan RequeueWithoutBackoff untuk mencegah message menumpuk. Semoga ini dapat membantumu mengetahui perbedaan antara kedua fungsi dan dapat menentukan fungsi mana yang cocok dengan use case mu. Please comment on questions or corrections.

nsq  go 

See also