2 पॉइंट द्वारा GN⁺ 2024-11-04 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • वेब सेवाओं में आम PostgreSQL+Redis कॉम्बिनेशन सुविधाजनक है, लेकिन background job queue, distributed locks और Pub/Sub में से कुछ काम सिर्फ PostgreSQL से भी संभाले जा सकते हैं
  • PostgreSQL 9.5 का FOR UPDATE SKIP LOCKED locked rows का इंतज़ार किए बिना उन्हें छोड़ देता है, इसलिए इसे ऐसी queue implementation में इस्तेमाल किया जा सकता है जो कई workers को वही job उठाने से रोकती है
  • application-level distributed locks PostgreSQL के advisory locks से बनाए जा सकते हैं, जो internal lock engine को application-defined उद्देश्यों के लिए reuse करते हैं
  • PostgreSQL 9 के LISTEN/NOTIFY arbitrary string channels को subscribe करने और notifications भेजने की सुविधा देते हैं, इसलिए इन्हें Pub/Sub layer की तरह इस्तेमाल किया जा सकता है; Rails ActionCable भी PostgreSQL इस्तेमाल को default रूप से support करता है
  • Redis की TTL caching और temporary data manipulation में अब भी मजबूत पकड़ है, लेकिन कुछ systems Redis dependency घटाकर operational cost और development complexity कम कर सकते हैं

Redis की वे भूमिकाएँ जिन्हें PostgreSQL में समाहित किया जा सकता है

  • आम वेब services PostgreSQL को data store के रूप में इस्तेमाल करती हैं और Redis को background job queue coordination या सीमित atomic operations के लिए इस्तेमाल करती हैं
  • Redis अपने-आप में उपयोगी है, लेकिन इस कॉम्बिनेशन में Redis की कुछ भूमिकाएँ सिर्फ PostgreSQL features से भी बदली जा सकती हैं

Background job queue

  • Redis वेब services में background worker pool तक jobs पहुँचाने के लिए job queue coordination में अक्सर इस्तेमाल होता है
    • चलाए जाने वाले background jobs और input data को record करता है
    • यह guarantee करनी होती है कि कई workers में से सिर्फ एक ही उस job को उठाए
    • Redis data structures पर atomic operations में समृद्ध है, इसलिए इस use case के लिए अच्छी तरह fit बैठता है
  • PostgreSQL 9.5 से SELECT ... FOR ... statement में SKIP LOCKED option इस्तेमाल किया जा सकता है
    • यह option देने पर PostgreSQL उन rows को ignore करता है जिनके lock release होने का इंतज़ार करना पड़े
    • FOR UPDATE SKIP LOCKED देने पर returned rows पर row-level lock implicitly acquire हो जाता है
    • SKIP LOCKED की वजह से दूसरे transactions के locks पर blocking होने की संभावना नहीं रहती
    • अगर process करने के लिए कोई दूसरी job है, तो वही job return होती है
    • कई workers वही command चलाएँ, तब भी row-level lock की वजह से उन्हें वही row नहीं मिलती
  • basic flow यह है कि transaction के अंदर pending status वाली एक job चुनी जाती है, उस job को running में बदला जाता है और फिर return किया जाता है
    • BEGIN
    • SELECT ... FOR UPDATE SKIP LOCKED
    • UPDATE jobs SET status = 'running' ... RETURNING jobs.*
    • COMMIT
  • ध्यान देने वाली बात यह है कि जब workers और jobs दोनों की संख्या बहुत अधिक हो, तो queue scan करते हुए locks acquire करने की कोशिश की cost बढ़ सकती है
    • व्यवहार में, जिन ज्यादातर apps को संभाला गया उनमें background workers 12 से कम थे, इसलिए यह cost बहुत बड़ी न होने की संभावना थी

Application locks

  • third-party service के साथ sync करने वाली routines जैसे cases में, सभी server processes में किसी खास user के लिए सिर्फ एक instance चलना चाहिए
  • ऐसे distributed locks Redis का एक और आम use case हैं
  • PostgreSQL advisory locks से वही उद्देश्य हासिल कर सकता है
    • advisory locks PostgreSQL के internally इस्तेमाल होने वाले lock engine को application-defined उद्देश्यों के लिए इस्तेमाल करने देते हैं

Pub/Sub

  • active clients तक events push करने के लिए भी Redis अक्सर इस्तेमाल होता है
    • user को notify करना कि नया message पढ़ा जा सकता है
    • data तैयार होते ही client तक stream करना
    • आम तौर पर WebSocket event delivery layer होता है और Redis Pub/Sub engine की भूमिका निभाता है
  • PostgreSQL 9 से LISTEN और NOTIFY statements से Pub/Sub functionality देता है
    • PostgreSQL client LISTEN से किसी arbitrary string वाले specific message channel को subscribe कर सकता है
    • कोई दूसरा client उस channel पर NOTIFY भेजे, तो subscribed सभी clients को notification मिलता है
    • optionally एक छोटा message साथ attach किया जा सकता है
  • अगर आप Rails और ActionCable इस्तेमाल कर रहे हैं, तो PostgreSQL का इस्तेमाल default रूप से supported है

Redis को बनाए रखने लायक क्षेत्र

  • Redis PostgreSQL से अलग क्षेत्र संभालता है और उन कामों में मजबूत है जिन्हें PostgreSQL target नहीं करता
    • TTL वाली data caching
    • temporary data storage और manipulation
  • PostgreSQL को सिर्फ एक simple SQL database या ORM के पीछे छिपे opaque component के रूप में देखने के लिए इसकी feature breadth बहुत बड़ी है
  • Redis को सौंपे गए कुछ काम PostgreSQL के लिए भी उपयुक्त हो सकते हैं
  • Redis को छोड़ना, कई data services पर निर्भरता से पैदा होने वाली operational cost और development complexity घटाने का एक विकल्प हो सकता है

1 टिप्पणियां

 
GN⁺ 2024-11-04
Hacker News की राय
  • हर कोई ज़रूरत से ज़्यादा distributed architecture पर अड़ा रहता है, इसलिए अक्सर Redis के असली फायदे नहीं दिखते। अगर इसे application वाली ही मशीन पर चलाया जाए, तो यह 1 millisecond से भी काफी कम समय में response दे सकता है, और इसलिए application में वे काम किए जा सकते हैं जो Postgres से करना मुश्किल है
    Postgres बेहतरीन है, यह सही है, लेकिन वह application वाली ही मशीन की memory में नहीं चलता। अगर आपको सिर्फ queue जैसी चीज़ें चाहिए, तो शायद in-memory key-value store की ज़रूरत न हो। in-memory key-value store का मुख्य मतलब वे काम करना है जिनके लिए RAM की performance characteristics चाहिए होती हैं, और network connection के पार आपको वे characteristics नहीं मिलतीं

    • अगर सिर्फ एक local process मशीन के अंदर Redis को in-memory cache की तरह इस्तेमाल करने वाला है, तो बेहतर है कि आप अपनी programming language की data structures ही इस्तेमाल करें
    • local पर चलाने पर Postgres और Redis के overhead में कितना फर्क होता है, यह जानने की उत्सुकता है। यह भी समझ नहीं आता कि Postgres को local पर नहीं चलने वाला क्यों माना जाता है
      Postgres में कोई खास जादू नहीं है; Redis की तरह वह भी किसी दूसरे process में चलने वाला program ही है। local connection में latency घटाने के लिए तेज pipe इस्तेमाल किए जाते हैं, और bulk data transfer के और तेज तरीके भी इस्तेमाल किए जा सकते हैं। मैंने इस तरह कई बार इस्तेमाल किया है
    • Django में Redis को support करने वाला cache built-in है, और in-memory cache option भी है, लेकिन उस पर “production के लिए नहीं” लिखा है। वजह यह है कि अगर कई Django instances हों, तो हर in-memory cache अलग-अलग हो जाता है
      लेकिन internal business tools जैसे मामलों में एक single instance को लंबे समय तक बढ़ाकर चलाया जा सकता है, और इस in-memory cache की वजह से वह बहुत तेज हो जाता है। django-cachalot एक library है जो table में write होते ही cache invalidation को अपने-आप handle करती है। तरीका थोड़ा rough है, लेकिन लगभग बिना मेहनत performance boost देता है, और जिन internal business apps में updates कम होते हैं वे असल में RAM से ही चलती हैं और cache miss होने पर ही सामान्य database query पर लौटती हैं
      https://github.com/noripyt/django-cachalot
    • इस configuration में Redis से भी तेज चीज़ hashmap है। Postgres के app वाली ही server पर न चल पाने की कोई वजह नहीं है, और व्यवहार में यह काफी common configuration है
    • over-engineering और जल्दबाज़ी में distributed बनाना वाकई समस्या है, लेकिन Redis “Remote Dictionary Server” का संक्षिप्त रूप है। इसका मूल उद्देश्य local पर चलना बिल्कुल नहीं था। हालांकि, जैसे किसी language की default dictionary range queries support नहीं करती, वैसे मामलों में local execution भी एक उचित design choice हो सकता है
  • यहां चर्चा में Redis के पक्ष से काफी defensive बातें हैं, लेकिन बेशक Redis जिन specific areas में बेहतर है, वे मौजूद हैं
    फिर भी मुझे नहीं लगता कि लेख का core point वह है। core sentence को ऐसे summarize किया जा सकता है: “PostgreSQL में उससे कहीं ज्यादा features हैं जितनी आप उम्मीद करते हैं, अगर आप उसे सिर्फ एक SQL database या ORM के पीछे छिपी किसी रहस्यमय चीज़ की तरह देखते हैं।” अगर आप database को सिर्फ ORM के पीछे इस्तेमाल कर रहे हैं, तो किसी भी database की capabilities छूटने की संभावना बड़ी है। अगर Redis जैसी कोई दूसरी service जोड़नी पड़ रही है, तो नई dependency जोड़ने के बजाय पहले से configured database इस्तेमाल करना बेहतर हो सकता है

  • Postgres क्या-क्या कर सकता है, यह समझना अच्छी बात है। यह powerful database है
    counterargument यह है कि Redis इस्तेमाल करने की barrier बहुत low है, और बदले में performance high मिलती है, library support भरपूर है, और primary database का load कम किया जा सकता है। उदाहरण के लिए API response cache Postgres से बनाया जा सकता है। TTL के लिए cron job से पुराने cache values साफ किए जा सकते हैं। या फिर बस Redis इस्तेमाल कर लें
    advisory locks शानदार और उपयोगी हैं, लेकिन PgBouncer जैसी चीज़ इस्तेमाल करनी हो तो session advisory locks और transaction interleaving के बीच समस्याएं आ सकती हैं। अलग system में network calls, availability, domain knowledge जैसी कमियां होती हैं, लेकिन Redis जितना compromise काफी छोटा है

    • production में manage करने के लिए एक चीज़ कम हो जाना फायदा है। PostgreSQL से शुरू करें, और performance, scaling या cost की वजह से जब ज़रूरत हो तब specialized system जोड़ें
  • लेख काफी पुराना है, लेकिन अब यह बहुत common pattern बन चुका है। जिन 90% projects को email भेजने या report generation के लिए सिर्फ job queue चाहिए होती है, वे प्रति सेकंड लाखों messages process नहीं करते, इसलिए stack को सरल बनाने वाला तरीका विचार करने लायक है
    Celery में आई समस्याओं को bypass करने के लिए मैं यह pattern अक्सर इस्तेमाल करता था, फिर इसे अलग framework के रूप में निकाल दिया: https://github.com/TkTech/chancy feedback स्वागत है
    ऐसे tools बहुत हैं, और उनमें से कुछ commercial services हैं, इसलिए demand साफ दिखती है
    https://worker.graphile.org/ (Node.js)
    https://riverqueue.com/ (Go)
    https://github.com/acaloiaro/neoq (Go)
    https://github.com/contribsys/faktory (Go)
    https://github.com/sorentwo/oban (Elixir)
    https://github.com/procrastinate-org/procrastinate (Python)

    • मैं simple job queue ढूंढ रहा था। Huey भी ठीक है, लेकिन हम पहले से Postgres इस्तेमाल कर रहे हैं, और queue में डाला जाने वाला काम लगभग एक घंटे में एक बार होता है, इसलिए Redis-based हमेशा overkill लगा
    • Node side में PG-Boss भी है: https://github.com/timgit/pg-boss
  • PGQueuer PostgreSQL के FOR UPDATE SKIP LOCKED और LISTEN/NOTIFY का इस्तेमाल करके job queue, locks और real-time notifications देता है
    अगर आप पहले से PostgreSQL इस्तेमाल कर रहे हैं, तो यह Redis की ज़रूरत खत्म करने वाला minimal alternative है
    https://github.com/janbjorge/PGQueuer
    जानकारी के लिए, यह मैंने बनाया है

  • Postgres पसंद है, लेकिन इसकी कुछ सीमाएँ हैं
    अगर आपको key-value store चाहिए, तो देखना होगा कि क्या आप autovacuum को समझते हैं, connection pool की सीमाएँ जानते हैं, और throughput व safety में से क्या चाहते हैं। अगर queue चाहिए, तो देखना होगा कि वह sequential processing है या नहीं, rate limit है या नहीं, fan-out है या नहीं, और topic-wise separation चाहिए या नहीं। अगर publish/subscribe चाहिए, तो सोचना होगा कि duplicate receive की परवाह है या नहीं, message loss की परवाह है या नहीं, और replay चाहिए या नहीं। अगर lock चाहिए, तो connection pool की सीमा और statement_timeout समझना होगा। ऊपर की ज़्यादातर समस्याएँ हल की जा सकती हैं, लेकिन यह उतना सरल नहीं है

    • यह भी देखना होगा कि लंबे समय तक चलने वाले transactions जब vacuum के tuple removal को रोकते हैं, तब implementation टूटता तो नहीं
  • Postgres publish/subscribe में बड़ा अड़ंगा यह है कि message का अधिकतम size 8000 bytes है
    सुझाया गया workaround यह है कि data को table में डालें और सिर्फ ID भेजें, लेकिन अगर आप उसे हमेशा के लिए नहीं रखना चाहते, तो उस data की garbage collection करनी पड़ेगी और हर message पर अतिरिक्त काम जुड़ जाएगा। बेशक कुछ मामलों में यह ठीक हो सकता है, लेकिन Redis के कई use cases में इस सीमा के कारण इसे बराबर मानना मुश्किल है

  • देखें कि क्या pgsql 15,000 client connections संभालता है

    • Postgres और MySQL का फर्क यहाँ सचमुच हैरान करने जितना बड़ा है
      Planetscale के किसी व्यक्ति ने podcast में कहा था कि GitHub के MySQL instances में से प्रत्येक 50,000 से ज़्यादा connections संभालता है। दूसरी ओर, Postgres में अगर 100 से ज़्यादा connections चाहिए, तो आपको पहले से ही PgBouncer की ज़रूरत पड़ जाती है
    • connection pooling इस्तेमाल कर सकते हैं। AWS RDS Proxy की तरह कुछ clicks में इस्तेमाल किया जा सकता है
  • queues, locks, और publish/subscribe संभव हैं। लेकिन Redis का सबसे अहम use case, caching, इसमें छूटा हुआ है
    Postgres updates insertion की तुलना में महंगे होने के लिए कुख्यात हैं, garbage बनाते हैं और vacuum की ज़रूरत होती है। caching के लिए गैर-ज़रूरी durability guarantees के कारण writes भी काफी धीमी हो जाती हैं। automatic expiration बहुत सुविधाजनक है और गलतियों को कम करती है

    • जिन features की ज़रूरत नहीं है, उनमें से ज़्यादातर बंद किए जा सकते हैं। उदाहरण के लिए table को unlogged बनाया जा सकता है: https://www.postgresql.org/docs/current/sql-createtable.html...
      synchronous commit, autovacuum आदि भी बंद किए जा सकते हैं। बेशक Redis फिर भी तेज़ रहेगा, लेकिन यह अंतर शायद इतना न हो कि किसी सामान्य company को उसकी चिंता करनी पड़े
  • लेख का सार और साफ़ कहें तो, Postgres से शुरू करें और ज़रूरत पड़ने पर Redis पर move कर जाएँ
    moving parts की संख्या जितनी हो सके उतनी कम रखना बेहतर है

    • Redis deploy करना खास मुश्किल नहीं है। बाद में किसी ऐसे migration की planning करने के बजाय, जिसके अनपेक्षित side effects क्या होंगे पता नहीं, शुरुआत से ही Redis इस्तेमाल करना भी ठीक विकल्प है