- Node.js/TypeScript आधारित बैकएंड में बड़े पैमाने पर real-time updates संभालने की स्थिति थी
- PostgreSQL को बैकएंड के रूप में इस्तेमाल करते हुए सैकड़ों worker nodes को नए jobs लगातार जांचने थे, और agents को execution तथा chat status updates मिलने चाहिए थे
- शुरुआत WebSocket की पड़ताल से हुई, लेकिन नतीजा एक चौंकाने वाले ढंग से असरदार 'पुराने' समाधान पर जाकर निकला
→ "Postgres का उपयोग करते हुए HTTP Long Polling"
समस्या की स्थिति: बड़े पैमाने पर real-time updates
- worker node updates :
- Node.js/Golang/C# SDK चलाने वाले सैकड़ों worker nodes थे
- उन्हें जैसे ही नया job उपलब्ध हो, तुरंत पता चलना चाहिए था, इसलिए ऐसी query strategy चाहिए थी जो Postgres database पर अत्यधिक दबाव न डाले
- agent state synchronization :
- agents को execution और chat status के लिए real-time updates चाहिए थे, और इन्हें कुशलता से stream करना आवश्यक था
Long Polling और WebSocket की तुलना
- short polling उस ट्रेन की तरह है जो समय-सारणी के अनुसार सख्ती से चलती है, चाहे यात्री हों या न हों, वह तय अंतराल पर निकल जाती है
- long polling में server response रोककर रखता है और जैसे ही data आता है, तुरंत लौटा देता है; अगर कुछ समय बीत जाए तो timeout response भेज देता है
- यानी यह उस ट्रेन की तरह है जो “इंतजार करती है और data आते ही चल पड़ती है।” केवल तब खाली निकलती है जब एक निश्चित समय (TTL) के भीतर कोई यात्री न आए
- data (यात्री) होने पर तुरंत रवाना होने और data न होने पर resources को कुशलता से इस्तेमाल करने—दोनों फायदे मिलते हैं
- WebSocket में connection लगातार खुला रहता है और data दोनों दिशाओं में आदान-प्रदान होता है
- संगठनात्मक वातावरण, infrastructure, firewall जैसी समस्याओं के कारण Long Polling, WebSocket configuration की तुलना में अधिक सरल और अधिक compatible था
Long Polling implementation का विवरण
getJobStatusSync function इसमें अहम भूमिका निभाता है
- यह
jobId, owner, ttl जैसे parameters लेकर एक निश्चित समय तक किसी खास job की स्थिति को बार-बार जांचता है
- यह repeated polling तब तक करता है जब तक नीचे दी गई शर्तों में से कोई एक पूरी न हो जाए
- job status
success या failure हो जाए
ttl (timeout) बीत जाए
- database को 500ms के अंतराल पर query किया जाता है, और अगर result final न हो तो इंतजार करके फिर से query की जाती है
- timeout पार होने पर error throw किया जाता है, और success होने पर result return किया जाता है
database optimization
- query cost कम करने के लिए Postgres में उचित indexes लगाए गए
- उदाहरण:
CREATE INDEX idx_jobs_status ON jobs(id, cluster_id);
Long Polling के फायदे
- monitoring को बनाए रखना आसान : मौजूदा HTTP-आधारित logging और monitoring stack को वैसे ही इस्तेमाल किया जा सकता है
- authentication की सरलता : नई authentication scheme लागू करने की जरूरत नहीं, मौजूदा HTTP authentication का ही उपयोग संभव
- infrastructure compatibility : firewall या load balancer पर अलग configuration की जरूरत नहीं, इसे सामान्य HTTP traffic की तरह माना जाता है
- operational simplicity : server restart होने पर भी connection state को अलग से संभालने की जरूरत नहीं, और debugging आसान रहती है
- client implementation की सहजता : मानक HTTP request-response structure में सिर्फ retry logic जोड़कर इसे चलाया जा सकता है
ElectricSQL के साथ तुलना
- ElectricSQL एक ऐसा solution है जो Postgres data को frontend के साथ synchronize करता है
- यह WebSocket की जगह HTTP का उपयोग करते हुए भी real-time responsiveness सुनिश्चित करने वाली संरचना रखता है
- वास्तव में अगर real-time updates संभालने के लिए अत्यधिक नियंत्रण या बहुत low-level structure की जरूरत न हो, तो ElectricSQL की सिफारिश की जाती है
हमने Raw Long Polling क्यों चुना
- message delivery mechanism कोई साधारण implementation detail नहीं, बल्कि product का core है
- core functionality को किसी third-party library पर निर्भर नहीं किया जा सकता (चाहे library कितनी भी अच्छी हो)
- आवश्यकताएँ
- core product control : message delivery mechanism पर पूर्ण नियंत्रण होना चाहिए। यह infrastructure-level की चीज नहीं, product का ही हिस्सा है
- external dependencies हटाना : self-hosting को सरल बनाने के लिए external dependencies को न्यूनतम रखना
- low-level control : polling mechanism और connection management को सीधे नियंत्रित करना
- maximum controllability : dynamic polling interval जैसी बारीकियों को विस्तार से tune करने की क्षमता होनी चाहिए
- code simplicity : codebase को users आसानी से समझ और modify कर सकें, इसलिए design को सरल रखना
- निष्कर्षतः, एक सरल HTTP Long Polling implementation चुनकर सीधा नियंत्रण और सरलता दोनों हासिल किए गए
Long Polling लागू करते समय सावधानियाँ
- TTL setting : server side पर अधिकतम TTL को अनिवार्य रूप से enforce करना चाहिए, और client द्वारा मांगा गया TTL उससे अधिक न होने दिया जाए
- infrastructure timeout का ध्यान : load balancer, edge server, proxy आदि के timeout settings से पर्याप्त रूप से छोटा TTL होना चाहिए
- DB polling interval : database load घटाने के लिए लगभग 500ms का delay देना चाहिए
- backoff strategy (optional) : polling interval को धीरे-धीरे बढ़ाने के तरीके से system resources का और अधिक कुशल उपयोग किया जा सकता है
किन स्थितियों में WebSocket पर विचार करना चाहिए
- WebSocket अपने आप में गलत नहीं है, और दूसरे पहलुओं में उपयोगी हो सकता है
- जब बड़ी संख्या में stateful connections को monitor करना हो, और लगातार जटिल events का आदान-प्रदान करना हो
- जब authentication, infrastructure, और observability से जुड़ी समस्याओं को हल करने के लिए पर्याप्त resources और समय उपलब्ध हों
- लेकिन इसमें operations, logging, reconnection handling, authentication mechanism आदि को खुद बनाना पड़ने की जटिलता मौजूद है
WebSockets: एक और विकल्प की कहानी
- Long Polling हमारी जरूरतों के अनुकूल था, लेकिन WebSockets पर भी गंभीरता से विचार किया जा सकता है
- WebSockets अपने आप में खराब नहीं हैं; बस उन्हें काफी ध्यान और प्रबंधन चाहिए
- WebSockets की मुख्य चुनौतियाँ और समाधान की दिशा
- visibility : WebSockets state-based होते हैं, इसलिए persistent connections के लिए अतिरिक्त logging और monitoring की जरूरत होती है
- authentication : WebSocket connections के लिए नई authentication mechanism लागू करनी पड़ती है
- infrastructure : WebSocket support के लिए load balancer, firewall आदि infrastructure को सही ढंग से configure करना पड़ता है
- operational management : WebSocket connections और reconnections का प्रबंधन, साथ में connection timeout और error handling
- client implementation : client-side WebSocket library का implementation, जिसमें reconnection और state management शामिल हो
5 टिप्पणियां
मैं यहाँ ML मॉडल serving में जिस "short polling" संरचना की बात हो रही है, उसका इस्तेमाल कर रहा हूँ, और क्या ज़्यादा efficient होगा इस पर काफ़ी सोच रहा हूँ। अपनी तरफ़ से इधर-उधर जो समझ पाया हूँ, उसके मुताबिक WebSocket या SSE जैसी चीज़ों में reconnection handling की बड़ी लागत की वजह से short polling आम तौर पर ज़्यादा सुरक्षित मानी जाती है, इसलिए मैंने short polling चुना था.. 😭
लगता है कि लोग Long polling को थोड़ा hacky मानकर उससे बचते हैं। ब्राउज़र में शायद यह लगातार ऐसे दिखेगा जैसे request अभी पूरी ही नहीं हुई है। कभी-कभी ऐसी साइटें होती हैं जहाँ loading खत्म ही नहीं होती, और तब मुझे लगता है कि क्या content पूरा लोड नहीं हुआ? इसलिए मुझे यह ज़्यादा पसंद नहीं आता।
एप्लिकेशन में भी आखिरकार किसी हिस्से में hang लगाकर response का इंतज़ार करने वाली स्थिति ही होगी,, तो यह थोड़ा अटपटा लगता है.
"एजेंट को execution और chat status update मिलने चाहिए"
यह देखकर मेरे दिमाग में तुरंत SSE आया, और सच में Hacker News की राय में भी SSE का ज़िक्र काफ़ी है।
Hacker News राय
Long polling की अपनी समस्याएँ हैं
loopके रूप में दिखाया गया chart section timeout handling का उल्लेख नहीं करताPhoenix और LiveView का हर दिन उपयोग करना खुशी की बात है
सोच रहा हूँ कि Server-Sent Events (SSE) का उपयोग करने की तुलना में इसका कोई तकनीकी लाभ है या नहीं
यह लेख
WebsocketऔरLong-pollingको स्वतंत्र निर्णयों की तरह जोड़ता हैNode.js में setTimeout का उपयोग करने का एक आसान तरीका
import { setTimeout } from "node:timers/promises"; await setTimeout(500);का उपयोग करेंमुझे long polling पसंद है, इसे समझना आसान है और client के नज़रिए से यह बहुत धीमे connection की तरह काम करता है
Server-Sent Events या WebSockets long polling के सभी use cases की जगह नहीं ले सकते
Postgres की asynchronous notification सुविधा का उपयोग करना अच्छा रहेगा
पता नहीं short timeout और gracefully closed request वाले long polling का अब भी कोई मतलब है या नहीं
WebSockets के अपेक्षाकृत सरल विकल्प की याद दिलाना ताज़गीभरा है
Elixir, Phoenix framework, LiveView के ज़रिए WebSockets इस्तेमाल करके देखना चाहता हूँ।