લોડિંગ સ્પિનર તમને કંઈ જ જણાવતું નથી. જ્યારે કોઈ AI કાર્ય મિનિટો સુધી ચાલે છે—અથવા ત્રીજી વાર ટ્રાય કરવા માટે ક્યુમાં પાછું જાય છે—ત્યારે તમારે તેની સ્થિતિ (state) જોવી જરૂરી છે. Server-Sent Events તમને WebSockets ના હેન્ડશેક ઓવરહેડ અથવા લોંગ પોલિંગની જટિલતા વગર તે વિઝિબિલિટી આપે છે. સર્વર એક સિંગલ HTTP રિસ્પોન્સ ખુલ્લો રાખે છે અને વસ્તુઓ બદલાતી જાય તેમ પ્લેન-ટેક્સ્ટ અપડેટ્સ પુશ કરે છે. ક્લાયન્ટ તેને મળતાની સાથે જ વાંચે છે.
જો કનેક્શન તૂટી જાય, તો તમે કદાચ ફરીથી શરૂ કરવા માંગતા નથી હોવ. એક સારી રીતે બનાવેલી SSE સ્ટ્રીમ તમે ક્યાં હતા તે યાદ રાખે છે. માત્ર Node.js 20 અને સ્ટાન્ડર્ડ લાઇબ્રેરી સાથે, તમે આ સેટઅપ કરી શકો છો. કોઈ બાહ્ય પેકેજોની જરૂર નથી.
વાયર ફોર્મેટ કેવું દેખાય છે
SSE મેસેજ સાદો ટેક્સ્ટ છે. સર્વર ત્રણ વસ્તુઓ લખે છે: એક વૈકલ્પિક ઇવેન્ટ નામ, એક જરૂરી data ફીલ્ડ, અને એક id ફીલ્ડ જે તમારો સેવ પોઈન્ટ બને છે. દરેક રેકોર્ડ બે ન્યૂલાઇન કેરેક્ટર્સ સાથે સમાપ્ત થાય છે—એક ખાલી લાઇન જે સીમા (boundary) નક્કી કરે છે.
એક હેલ્ધી સ્ટ્રીમ વાયરમાં આવી દેખાઈ શકે છે:
id: 14
event: status
data: {"phase":"testing","progress":43}
id: 15
event: status
data: {"phase":"retrying","attempt":2}
બ્રાઉઝરનું EventSource ક્લાયન્ટ આ લાઇન આપમેળે વાંચે છે. તે દરેક બ્લોક માટે ઇવેન્ટ રેઝ (raise) કરે છે અને આંતરિક રીતે લેટેસ્ટ id સ્ટોર કરે છે. જો TCP કનેક્શન તૂટી જાય, તો ક્લાયન્ટ રાહ જુએ છે, ફરીથી કનેક્ટ થાય છે, અને સ્ટોર કરેલ આઇડેન્ટિફાયરને Last-Event-ID હેડર તરીકે સર્વર પર પાછો મોકલે છે. આ હેડર જ આ પેટર્ન સફળ થવાનું મુખ્ય કારણ છે. તેના વગર, તમારી પાસે કોઈ ડ્યુરેબલ કર્સર (durable cursor) હોતું નથી.
Node.js માં સર્વરને વાયરિંગ કરવું
Node નું બિલ્ટ-ઇન http મોડ્યુલ આને સીધું હેન્ડલ કરી શકે છે. જ્યારે રિક્વેસ્ટ આવે, ત્યારે યોગ્ય હેડર્સ સેટ કરો જેથી ક્લાયન્ટને ખબર પડે કે આ એક સ્ટ્રીમ છે, પેજ નથી:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
બફરિંગ દૂર કરો. પ્રોક્સી અને ફ્રેમવર્ક ક્યારેક રિસ્પોન્સને બેચમાં મોકલે છે, જે રિયલ-ટાઇમ અનુભવને ખતમ કરી દે છે, તેથી દરેક ચંક (chunk) પછી ફ્લશ (flush) કરો.
પહેલા ID મોકલો, પછી ઇવેન્ટ ટાઇપ, પછી પેલોડ ડેટા, અને પછી સમાપ્તિ માટે ખાલી લાઇન મોકલો. ઓર્ડર મહત્વનો છે કારણ કે ID ખાલી લાઇન પહેલા આવવું જોઈએ જેથી ક્લાયન્ટ તેને કેપ્ચર કરી શકે. જો તમે નેટિવ response.write() નો ઉપયોગ કરી રહ્યા હોવ, તો આઉટપુટ આ મુજબ હશે:
response.write(`id: ${cursor}\n`);
response.write(`event: ${eventName}\n`);
response.write(`data: ${JSON.stringify(payload)}\n\n`);
તે છેલ્લે આવતું \n\n માત્ર સુશોભન માટે નથી. SSE પાર્સર્સ તેને રેકોર્ડ ટર્મિનેટર તરીકે ગણે છે. જો તમે તે ચૂકી જશો, તો ક્લાયન્ટ વધુ ડેટાની રાહ જોતા હેંગ થઈ જશે.
કર્સર જ બધું છે
નવું HTTP કનેક્શન નવા સ્ટેટની ગેરંટી આપતું નથી. જ્યારે ક્લાયન્ટ ફરીથી કનેક્ટ થાય છે, ત્યારે Last-Event-ID હેડર તમને જણાવે છે કે તેમણે છેલ્લો કયો મેસેજ મેળવ્યો હતો. તમારું કામ શરૂઆતથી નહીં, પણ તેના પછીના મેસેજથી ફરી શરૂ કરવાનું છે.
આનો અર્થ એ છે કે સર્વર-સાઇડ પર ઇવેન્ટ્સનો ક્રમબદ્ધ લોગ અથવા જર્નલ જાળવવો. ડેમો માટે ઇન-મેમરી એરે (in-memory array) કામ કરી શકે છે. પ્રોડક્શનમાં તમારે કંઈક ડ્યુરેબલ જોઈએ—ડેટાબેઝ લોગ, Redis સ્ટ્રીમ અથવા રાઈટ-અહેડ જર્નલમાં એપેન્ડ કરો—કારણ કે સર્વર રીસ્ટાર્ટ થવાથી હિસ્ટ્રી ભૂંસાઈ ન જવી જોઈએ અને દરેક ક્લાયન્ટે શૂન્યથી શરૂઆત કરવી ન પડે.
તમારી ઇવેન્ટ્સને મોનોટોનિકલી વધતા જતા ઇન્ટિજર અથવા ULID દ્વારા ઇન્ડેક્સ કરો. જ્યારે રીકનેક્ટ રિક્વેસ્ટ આવે, ત્યારે id > lastEventId હોય તેવી ઇવેન્ટ્સ માટે ક્વેરી કરો, અને તેમને ક્રમમાં રિપ્લે કરો. જો તમારી પાસે સેંકડો બેકલોગ મેસેજ હોય, તો નાનો આર્ટિફિશિયલ ડિલે અથવા બેચિંગ ઉમેરો, પરંતુ તેમને જૂનાથી નવા ક્રમમાં મોકલો જેથી ક્લાયન્ટ સમયાનુસાર સ્ટેટ ફરીથી બનાવી શકે.
ડુપ્લીકેટ્સની અપેક્ષા રાખો
નેટવર્ક ભરોસાપાત્ર હોતા નથી. સર્વર એક ઇવેન્ટ મોકલી શકે છે, TCP એક્નોલેજમેન્ટ ગુમાવી શકે છે, અને ટાઈમઆઉટ પછી તેને ફરીથી મોકલી શકે છે. શરૂઆતથી જ 'એટ-લીસ્ટ-વનસ' (at-least-once) ડિલિવરી માટે ડિઝાઇન કરો.
ક્લાયન્ટ પર, ડુપ્લીકેશન દૂર કરવું સસ્તું છે. ઇવેન્ટ ID દ્વારા કી ધરાવતો Map રાખો. જ્યારે નવી ઇવેન્ટ આવે, ત્યારે મેપ તપાસો. જો ID અસ્તિત્વમાં હોય, તો ડુપ્લીકેટને શાંતિથી ડ્રોપ કરો. કારણ કે તમારું સર્વર ડિટરમિનિસ્ટિક (deterministic) IDs આપે છે, આ ડુપ્લીકેટ્સને નુકસાનરહિત બનાવે છે. મેપને કાયમ માટે વધવાની જરૂર નથી. એકવાર તમે કન્ફર્મ કરો કે ઇવેન્ટ સુરક્ષિત રીતે પ્રોસેસ થઈ ગઈ છે, તો જૂના IDs કાઢી નાખો. બ્રાઉઝર ક્લાયન્ટ્સ માટે સામાન્ય રીતે થોડા સો એન્ટ્રીઝનું સ્લાઇડિંગ વિન્ડો પૂરતું હોય છે.
જ્યારે કર્સર એક્સપાયર થાય
આખરે, કોઈ ક્લાયન્ટ કલાકો અથવા દિવસો પછી ફરીથી કનેક્ટ થશે. જો તમારો હિસ્ટ્રી બફર ફક્ત છેલ્લા હજાર ઇવેન્ટ્સને જ આવરી લે છે અને ક્લાયન્ટ બે હજાર પાછળ છે, તો ગેપ્સ (gaps) રિપ્લે કરવા અશક્ય છે.
અધૂરી હિસ્ટ્રી સ્ટ્રીમ ન કરો. તે ક્લાયન્ટને અસંગત સ્ટેટમાં છોડી દે છે. તેના બદલે, એક્સપાયર્ડ કર્સર શોધો અને આગામી ઇવેન્ટ તરીકે સંપૂર્ણ સ્નેપશોટ મોકલો. સ્નેપશોટમાં નવો કર્સર હોવો જોઈએ જે ક્લાયન્ટને વર્તમાન સ્ટેટ સાથે જોડે. ત્યાંથી, લાઈવ ડેલ્ટા (live deltas) સામાન્ય રીતે ચાલુ થશે. આ બોર્ડરને તમારા પ્રોટોકોલમાં સ્પષ્ટ રીતે ડોક્યુમેન્ટ કરો જેથી ક્લાયન્ટ કોડને ખબર પડે કે ક્યારે તેના લોકલ મોડેલમાં ડેટા ઉમેરવાને બદલે તેને રીસેટ કરવું.
સ્ટ્રીમને સુરક્ષિત કરો
ઓપન SSE એન્ડપોઇન્ટ્સ આકર્ષક લક્ષ્યો છે. કોઈપણ કનેક્શન પકડી રાખી શકે છે, અને રિક્વેસ્ટ રિપ્લે કરવાથી તમારા સ્ટોરેજ પર રીડ લોડ વધી શકે છે.
યોગ્ય અધિકૃતતા (authorization) સાથે એન્ડપોઇન્ટને સુરક્ષિત કરો. બ્રાઉઝર EventSource કસ્ટમ હેડર્સને સપોર્ટ કરતું નથી, તેથી ટોકનને ક્વેરી સ્ટ્રિંગમાં પાસ કરો અથવા કડક SameSite નીતિઓ સાથે કુકીઝનો ઉપયોગ કરો. સ્ટ્રીમ રિસોર્સિસ ફાળવતા પહેલા ટોકનને વેલિડેટ કરો.
હિસ્ટ્રી મર્યાદાઓ અને પ્રતિ-વપરાશકર્તા ક્વોટા સેટ કરો. દરેક ટાસ્ક દીઠ સંગ્રહિત ઇવેન્ટ્સની સંખ્યા મર્યાદિત કરો, અને દરેક ક્લાયંટ દીઠ એકસાથે કનેક્શનની સંખ્યા મર્યાદિત કરો. ડિસ્કનેક્ટ્સ અને રિપ્લેઝને લોગ કરો જેથી તમે તમારા કર્સર એન્ડપોઇન્ટ પર સતત વિનંતીઓ મોકલતા કોઈ ખોટા ક્લાયન્ટને ઓળખી શકો.
આ પેટર્ન અન્ય પ્લેટફોર્મ પર પણ લાગુ પડે છે
આ અભિગમ માત્ર HTTP પૂરતો મર્યાદિત નથી. જ્યારે તમે WebSockets, મેસેજ ક્યુઝ (message queues), અથવા એજન્ટ-ટુ-એજન્ટ ઇન્ટરફેસ પર જાઓ છો ત્યારે પણ આ જ નિયમો લાગુ પડે છે. ટ્રાન્સપોર્ટ બદલાય છે—તમે બાઈનરી ફ્રેમ્સ અથવા ટોપિક સબ્સ્ક્રિપ્શનનો ઉપયોગ કરી શકો છો—પરંતુ મૂળ સમસ્યા સમાન રહે છે. તમારે કર્સર, ડ્યુરેબલ લોગ, at-least-once semantics, ક્લાયન્ટ ડુપ્લીકેશન (deduplication), અને જ્યારે કર્સર જૂનું થઈ જાય ત્યારે ફૂલ સ્નેપશોટ પર ફોલબેક (fallback) ની જરૂર પડશે. સ્ટેટ કન્વર્જન્સ (state convergence) ને એકવાર ઉકેલી લો, અને તમે કોર લોજિકને ફરીથી ડિઝાઇન કર્યા વિના તેને TCP, WebSocket, અથવા RabbitMQ જેવા બ્રોકર પર મોકલી શકો છો.
તેને સરળ રાખો
Server-Sent Events કામ કરે છે કારણ કે તે સામાન્ય HTTP પર આધારિત છે. પ્રોક્સીઓ તેને સમજી શકે છે. લોડ બેલેન્સર્સ તેનું હેલ્થ-ચેક કરી શકે છે. curl જેટલું જ ડિબગિંગ સરળ છે. પરંતુ જો તમે એજ કેસીસ (edge cases) ને અવગણશો તો તે સરળતા જતી રહેશે. કર્સર બનાવો. રિપ્લેઝની અપેક્ષા રાખો. ક્લાયન્ટ પર ડુપ્લીકેશન દૂર કરો. જ્યારે હિસ્ટ્રી પૂરી થઈ જાય ત્યારે સ્નેપશોટ લો. આવું કરવાથી, તમારા લાંબા સમય સુધી ચાલતા AI ટાસ્ક, અસ્થિર Wi-Fi, સર્વર રીસ્ટાર્ટ અને ક્યારેક રાત્રિ દરમિયાન બ્રાઉઝર સ્લીપ મોડમાં હોવા છતાં, તેમની પ્રગતિની સચોટ માહિતી આપશે.
સ્ત્રોત: Build a Reconnecting SSE Task Stream with Node.js
ચર્ચામાં જોડાઓ: GyaanSetu AI Community
