- Dalam backend berbasis Node.js/TypeScript, ada kebutuhan untuk menangani pembaruan real-time dalam skala besar
- Menggunakan PostgreSQL sebagai backend, ratusan node worker harus terus memeriksa pekerjaan baru, dan agent perlu menerima pembaruan status eksekusi serta chat
- Awalnya dimulai dengan mengeksplorasi WebSocket, tetapi berakhir pada solusi 'jadul' yang ternyata sangat efektif
→ "HTTP Long Polling dengan Postgres"
Situasi masalah: pembaruan real-time dalam skala besar
- Pembaruan node worker :
- Ada ratusan node worker yang menjalankan SDK Node.js/Golang/C#
- Mereka harus mengetahui segera setelah pekerjaan baru tersedia, sehingga dibutuhkan strategi query yang tidak menjatuhkan database Postgres
- Sinkronisasi status agent :
- Agent membutuhkan pembaruan real-time untuk status eksekusi dan chat, dan ini harus di-stream dengan efisien
Perbandingan Long Polling dan WebSocket
- Short polling seperti kereta yang berangkat ketat sesuai jadwal, sehingga tetap berangkat pada interval tetap terlepas ada penumpang atau tidak
- Long polling membuat server menunggu sebelum merespons, lalu segera mengembalikan hasil saat data tersedia, dan akan mengembalikan respons timeout jika waktu tertentu terlewati
- Dengan kata lain, ini seperti kereta yang “menunggu lalu berangkat saat data muncul”. Hanya jika tidak ada penumpang dalam waktu tertentu (TTL), kereta berangkat dalam keadaan kosong
- Saat ada data (penumpang), ia langsung berangkat; saat tidak ada, resource dapat digunakan secara efisien — memberi dua keuntungan sekaligus
- WebSocket mempertahankan koneksi secara terus-menerus untuk bertukar data dua arah
- Karena lingkungan organisasi, infrastruktur, firewall, dan masalah lainnya, long polling lebih sederhana dan lebih kompatibel daripada menyiapkan WebSocket
Detail implementasi long polling
- Fungsi
getJobStatusSync memegang peran penting
- Fungsi ini menerima parameter seperti
jobId, owner, ttl, lalu berulang kali memeriksa status pekerjaan tertentu selama jangka waktu tertentu
- Pengecekan berulang dilakukan sampai salah satu kondisi berikut terpenuhi
- Status pekerjaan menjadi
success atau failure
ttl (timeout) terlewati
- Database diperiksa setiap 500ms; jika hasilnya belum final, sistem menunggu lalu memeriksa lagi
- Jika melebihi timeout, error dilempar; jika berhasil, hasil dikembalikan
Optimasi database
- Menempatkan indeks yang tepat di Postgres untuk meminimalkan biaya query
- Contoh:
CREATE INDEX idx_jobs_status ON jobs(id, cluster_id);
Keuntungan long polling
- Kemudahan mempertahankan monitoring : stack logging dan monitoring HTTP yang sudah ada bisa tetap digunakan
- Kesederhanaan autentikasi : tidak perlu mengimplementasikan metode autentikasi baru, cukup memakai autentikasi HTTP yang sudah ada
- Kompatibilitas infrastruktur : tidak memerlukan konfigurasi khusus pada firewall atau load balancer, dan diperlakukan sebagai traffic HTTP biasa
- Kesederhanaan operasional : saat server restart, tidak perlu menangani status koneksi secara terpisah, dan debugging lebih mudah
- Implementasi klien yang praktis : cukup tambahkan logika retry ke struktur request-response HTTP standar
Perbandingan dengan ElectricSQL
- ElectricSQL adalah solusi untuk menyinkronkan data Postgres dengan frontend
- Solusi ini memiliki struktur yang menjamin sifat real-time sambil menggunakan HTTP alih-alih WebSocket
- Jika untuk menangani pembaruan real-time tidak dibutuhkan kontrol ekstrem atau struktur tingkat rendah, ElectricSQL direkomendasikan
Mengapa kami memilih Raw Long Polling
- Mekanisme pengiriman pesan bukan sekadar detail implementasi sederhana, melainkan elemen inti produk
- Fitur inti tidak boleh bergantung pada library pihak ketiga (sehebat apa pun library tersebut)
- Kebutuhan
- Kontrol atas produk inti : mekanisme pengiriman pesan harus dikendalikan sepenuhnya. Ini bukan level infrastruktur, melainkan bagian dari produk itu sendiri
- Menghilangkan dependensi eksternal : meminimalkan dependensi eksternal untuk menyederhanakan self-hosting
- Kontrol tingkat rendah : mengendalikan sendiri mekanisme polling dan pengelolaan koneksi
- Kontrol maksimal : detail seperti implementasi interval polling dinamis harus bisa disetel dengan rinci
- Kesederhanaan kode : dirancang sesederhana mungkin agar pengguna mudah memahami dan memodifikasi codebase
- Kesimpulannya, dengan memilih implementasi HTTP Long Polling yang sederhana, didapatkan kontrol langsung dan kesederhanaan
Hal-hal yang perlu diperhatikan saat mengimplementasikan long polling
- Pengaturan TTL : server harus selalu memaksakan TTL maksimum, dan memastikan TTL yang diminta klien tidak melampaui batas itu
- Mempertimbangkan timeout infrastruktur : TTL harus cukup lebih pendek daripada pengaturan timeout pada load balancer, edge server, proxy, dan sebagainya
- Interval polling DB : beri delay sekitar 500ms untuk mengurangi beban DB
- Strategi backoff (opsional) : interval polling dapat dinaikkan secara bertahap agar resource sistem digunakan lebih efisien
Kapan perlu mempertimbangkan WebSocket
- WebSocket sendiri bukan pilihan yang salah, dan tetap berguna dalam aspek lain
- Saat perlu memantau banyak koneksi yang stateful, dan terus-menerus bertukar event yang kompleks
- Saat tersedia cukup resource dan waktu untuk menyelesaikan masalah autentikasi, infrastruktur, dan observabilitas
- Ada kompleksitas karena harus membangun sendiri hal-hal seperti operasional dan logging, penanganan reconnect, mekanisme autentikasi, dan sebagainya
WebSockets: cerita tentang opsi lain
- Long Polling cocok untuk kebutuhan kami, tetapi WebSockets juga layak dipertimbangkan sepenuhnya
- WebSockets sendiri tidak buruk; hanya saja membutuhkan banyak perhatian dan pengelolaan
- Tantangan utama WebSockets dan arah penyelesaiannya
- Visibilitas : karena WebSockets bersifat stateful, perlu menambahkan logging dan monitoring untuk koneksi yang persisten
- Autentikasi : perlu mengimplementasikan mekanisme autentikasi baru untuk koneksi WebSocket
- Infrastruktur : agar mendukung WebSocket, infrastruktur seperti load balancer dan firewall harus dikonfigurasi dengan benar
- Manajemen operasional : pengelolaan koneksi dan reconnect WebSocket. Termasuk timeout koneksi dan penanganan error
- Implementasi klien : implementasi library WebSocket di sisi klien, termasuk fitur reconnect dan manajemen status
5 komentar
Saya memakai struktur "short polling" yang dibahas di sini untuk serving model ML, dan cukup banyak mempertimbangkan mana yang lebih efisien. Dari yang saya cari ke sana kemari, ada pendapat bahwa short polling umumnya lebih aman karena biaya besar untuk menangani reconnect pada WebSocket atau SSE, jadi saya memang memilih short polling.. 😭
Long polling terasa agak hacky, jadi sepertinya orang enggan memakainya. Di browser, kemungkinan akan terus terlihat seolah-olah permintaannya belum selesai. Kadang ada situs yang loading-nya tidak pernah selesai, dan saya jadi berpikir, apakah kontennya belum termuat sepenuhnya? Jadi saya kurang suka. Dalam aplikasi juga pada akhirnya akan ada bagian yang dibuat hang dan menunggu respons, jadi terlihat agak canggung.
"Agen perlu menerima pembaruan status eksekusi dan chat"
Begitu melihat ini saya langsung terpikir SSE, dan memang di opini Hacker News juga banyak yang menyebut SSE.
Komentar Hacker News
Long polling punya masalahnya sendiri
Senang menggunakan Phoenix dan LiveView setiap hari
Penasaran apakah ada keuntungan teknis dibanding menggunakan Server-Sent Events (SSE)
Artikel ini menghubungkan "Websocket" dan "Long-polling" sebagai keputusan yang berdiri sendiri
Cara yang lebih mudah menggunakan setTimeout di Node.js
import { setTimeout } from "node:timers/promises"; await setTimeout(500);Suka long polling, mudah dipahami dan dari sudut pandang klien bekerja seperti koneksi yang sangat lambat
Server-Sent Events maupun WebSockets tidak bisa menggantikan semua use case long polling
Sebaiknya menggunakan fitur notifikasi asinkron milik Postgres
Tidak yakin apakah long polling masih punya arti dengan timeout singkat dan permintaan yang ditutup secara elegan
Menyegarkan untuk diingatkan pada alternatif yang relatif sederhana terhadap WebSockets
Saya ingin mencoba memakai WebSockets melalui Elixir, framework Phoenix, dan LiveView.