Belajar dasar QUIC(Quick UDP Internet Connection) yang mendasari HTTP/3
Salam untuk para pembaca, senang bisa bertemu kembali disini. Dua minggu lalu, kami berencana untuk membuat part 2 dari artikel SSE sebelumnya atau mengupas teknik lain seperti XHR maupun websocket. Namun, salah satu dari kami melakukan proposal untuk membahas QUIC (Quick UDP Internet Connection). Setelah proposal itu, kami mengubah haluan tema book club “1 chapter for a week” kami. SSE part 2 akan kami bahas di lain waktu saat kami benar-benar membutuhkan SSE di production yaaa 🙏 Pada kali ini, kami akan membahas resource dari Chromium Team tentang QUIC.
Sebelum membahas QUIC, kita akan review tentang TCP dan UDP terlebih dahulu yang menjadi dasar QUIC ini. TCP dapat memastikan pengiriman paket terkirim sepenuhnya dan terurut, jika ada paket yang hilang dia akan mengirimkan ulang. Namun, TCP membutuhkan three way handshake (1 round trip) untuk satu requestnya. Sedangkan dengan TLS membutuhkan 3 round trip. UDP memang cepat karena tidak menggunakan three way handshake. Akan tetapi, UDP tidak dapat memastikan urutnya sebuah paket dan paket bisa hilang tanpa jejak.


QUIC adalah proyek untuk membuat internet lebih baik memanfaatkan latency rendah dari UDP. Proyek QUIC diharapkan dapat mencapai salah satu tujuan dari:
- QUIC dapat menjadi dasar protokol internet baru yang lebih cepat
- TCP dan TLS dapat mengadopsi hal2 yang dipelajari saat membuat QUIC.
Kedua tujuan tersebut membuat internet user akan berterima kasih pada riset ini di kemudian hari. QUIC menimbulkan sebuah pertanyaan, kalau ingin migrasi ke protokol selain TCP, kenapa tidak menggunakan protokol lain seperti SCTP (Stream Control Transmission Protocol) saja ?
Ada dua alasan utama, Yang pertama: tidak semua komputer memiliki dukungan SCTP. Beberapa komputer hanya dapat menerima TCP dan UDP saja. Jika kita ingin menggunakan SCTP, kita harus menunggu semua komputer/device dapat menerima SCTP terlebih dahulu untuk penggunaan yang masif.
Alasan kedua adalah latency. Proyek ini bertujuan untuk menurunkan latency dengan meminimalisir jumlah RTT (round trip time). SCTP memiliki 1 round trip . Sedangkan, SCTP + DTLS membutuhkan 4 round trip. Kita tidak menginginkan ini terjadi. Sebenarnya, ada alasan2 lain yang mendukung juga, jika temen2 ingin tahu lebih lanjut, bisa dilihat di link berikut.
QUIC adalah sebuah protokol baru yang menggunakan UDP dengan fitur multiplexing seperti HTTP/2 (SPDY / RFC7540). Fitur multiplexing pada HTTP/2 ini didasari HTTP 1.x mempunyai banyak request yang sebenarnya bisa digabung menjadi satu request. Hal ini bisa dilihat pada gambar berikut:

Karena hal ini tidak efisien, HTTP 1.x membuat fitur yang bernama HTTP Pipelining untuk menggabungkan HTTP request ini dan mengurangi three way handshake. HTTP 1.x pipelining memiliki request dan response yang utuh dan terurut. Hal ini buruk jika ada paket yang delay dalam sebuah request karena akan membuat blocking terhadap request2 lain. HTTP 1.x pipelining juga dibatasi oleh dukungan browser dan server yang terbatas.
Untuk itu, HTTP/2 membuat fitur multiplexing. Multiplexing membagi ke beberapa chunk dan dikirim dengan mencampur paket untuk menghindari blocking pada layer aplikasi. Dan protokol baru dapat memaksa semua browser untuk memberi dukungan. Fitur multiplexing ini dapat dilihat pada gambar dibawah ini:

Walaupun terlihat baik, fitur multiplexing ini masih memiliki masalah, jika ada salah satu paket hilang, hal ini akan membuat seluruh stream pada multiplexing terhenti dan dibutuhkan retansmisi paket. Hal ini akan membuat overhead pada bandwidth reduction dan latency karena congestion avoidance dari TCP layer. Paket yang dikirimkan akan tereduksi sebesar 30–50% karena congestion control, sehingga kecepatan internet akan menurun (congestion window menurun) dan harus dilakukannya re-transmisi seluruh paket. Masalah ini disebut head-of-line blocking dan bisa dilihat dibawah ini:

Untuk me-minimalisir head-of-line-blocking ini, kita membutuhkan sesuatu yang lebih powerful. Seperti yang kita tahu, UDP membolehkan hilangnya sebuah paket pada pengiriman paket, bagaimana jika UDP dapat melakukan multiplexing dan dapat meminta kembali paket yang hilang ? QUIC menerapkan multiplexing pada UDP dan membuat tidak ada head-of-line blocking sama sekali.

Hal ini akan sangat menghemat bandwidth reduction dan latency yang ada. QUIC mengadopsi Forward Error Correction (FEC) untuk menjawab permasalahan paket yang hilang. Awalnya, FEC yang dipakai adalah nilai XOR dari paket-paket yang terkirim sehingga jika ada paket yang hilang, kita tidak perlu melakukan retransmisi semua paket yang ada. Berikut gambar FEC yang dapat dilihat:

Menurut kami, adopsi XOR-FEC ini jenius, tetapi bagaimana jika ada dua paket atau lebih yang hilang dijalan ? Apakah FEC berdasarkan XOR masih berguna ? Setelah mendalami XOR-FEC, kami menyimpulkan bahwa XOR-FEC ini bermanfaat jika hanya satu paket yang hilang. Hal ini juga ditulis pada salah satu dokumen google. Alhasil, XOR-FEC dihapus karena tidak lolos uji pada paper dari google. XOR FEC sekarang digantikan FEC baru yang menggunakan real-time sessions dan sedang diajukan pada draft RFC berikut. Untuk finalisasi FEC-nya, kita tinggal tunggu ketika draftnya berubah menjadi RFC saat sudah teruji, daripada berubah2 terus kan ? ;)
Lalu, bagaimana sistem congestion avoidance dan congestion control pada QUIC ? Awalnya, QUIC memiliki fitur yang bernama bandwidth estimator dan packet pacing untuk mengatasi congestion avoidance. Bandwidth estimator adalah fitur yang melakukan estimasi bandwidth yang ada dengan menghitung delay transmisi. Packet pacing adalah fitur untuk memberi delay antar paket hingga sesuai dengan bandwidth yang tersedia. Jika terdapat packet loss, kita akan melakukan delay/pace pada paket sehingga dapat mengikuti bandwidth yang ada.

Namun, setelah beberapa perubahan pada QUIC, Congestion Avoidance berubah menjadi menggunakan algoritma AIMD. Hal ini tertulis pada draft RFC versi 34. Sedangkan untuk congestion control, QUIC mengadaptasi algoritma yang mirip TCP New Reno dan CUBIC . QUIC client dapat memilih salah satu, namun default yang digunakan adalah algoritma mirip TCP. Flow congestion control pada QUIC dapat dilihat pada gambar dibawah ini:

QUIC Congestion Control juga memiliki slow start seperti TCP , setelah slow start, congestion window akan naik perlahan hingga ada packet-loss atau kenaikan ECN-CE (Explisit Congestion Notification) dan masuk ke recovery state. Saat state recovery, ketika sudah menerima ack dari paket yang terkirim, state berpindah ke congestion avoidance hingga ada packet-loss atau kenaikan ECN-CE kembali. ECN-CE adalah notifikasi pemberitahuan terjadinya congestion secara eksplisit, jika kepo lebih lanjut temen2 bisa lihat ECN disini.
Kalau ingin tahu hal-hal lain tentang QUIC, kami sarankan melihat dua sumber dibawah ini atau diskusi di komentar boleh yaaa
1. Google Document from Jim (versi awal)
2. Draft RFC IETF (versi 34)
Kita sudah mempelajari dasar-dasar QUIC. Memang seberapa cepat sih QUIC ? Apakah memang sebeda itu? Mengutip dari benchmark yang sudah dilakukan oleh salah satu Universitas Hungaria, QUIC lebih baik ketika kita memiliki kondisi high latency dengan low bandwidth. Author dari paper ini menjelaskan bahwa QUIC adalah opsi terbaik jika kita mengirim paket-paket yang kecil. Sedangkan untuk bandwidth besar seperti file, QUIC tidak memiliki performansi yang lebih baik daripada HTTP/2 maupun HTTP.
Oleh karena itu, kami berharap QUIC pada HTTP/3 ini menjadi salah satu solusi untuk memperbaiki internet pada third-world country sehingga tidak terjadi kesenjangan pengetahuan. By the way, Ada yang kepo engga ya implementasi QUIC di code itu bagaimana ? Kami sempat mencari dan mengumpulkan implementasi QUIC yang bisa dilihat di:
- quic-go dari Lucas Clemente (Engineer di Google) (Go Lang)
- msquic dari Microsoft (bahasa C, C++, C#)
- quiche dari Cloudflare (bahasa Rust)
- quic dari Tim Chromium (bahasa C) (simple client dan server)
Kalau ada yang mau belajar implementasinya bisa lihat di link-link berikut dan dicocokkan dengan draft RFC yang ada yaaa.
Selamat belajar dan mencoba teman-teman!