5 पॉइंट द्वारा GN⁺ 2023-12-02 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • जटिल फ्रंटएंड apps API response cache से शुरू होकर manual indexes और cache invalidation तक अपने ऊपर ले लेते हैं, और हर project में एक छोटे database को फिर से implement करने जैसी स्थिति के करीब पहुंच जाते हैं
  • React जैसे declarative frameworks में हर render पर API call से बचने के लिए local state या Redux layer में cache रखा जाता है, और यह layer धीरे-धीरे central store की भूमिका निभाने लगती है
  • ID-आधारित storage और तारीख के हिसाब से query structure lookup को तेज बनाते हैं, लेकिन CACHE और ENTRIES_BY_DATE जैसी कई structures के बीच consistency बनाए रखना tests और code review का बोझ बढ़ा देता है
  • Optimistic changes server response से पहले UI को तुरंत update करके perceived speed बढ़ाते हैं, लेकिन इनके साथ client/server logic duplication, in-flight changes tracking, error rollback, app restart के बाद reconciliation जैसी consistency costs आती हैं
  • SQLSync, SQLite-आधारित local database और Git Rebase जैसी synchronization के जरिए frontend stack के भीतर durable cache, indexes, constraints, optimistic changes और reactive queries उपलब्ध कराने की कोशिश करता है

फ्रंटएंड cache के database में बढ़ने की प्रक्रिया

  • फ्रंटएंड data management API response को local variable में store करने वाले सरल cache से शुरू हो सकता है
    • React जैसे declarative frameworks user interaction के दौरान tree को कई बार re-render करते हैं
    • हर render पर API request भेजने से बचने के लिए useState और useEffect से request result या error को component state में store किया जा सकता है
    • उदाहरण स्पष्टता के लिए simplified form में है, और असल में proven API libraries इस्तेमाल करने का विकल्प भी होता है
  • Cache को UI tree की ऊपरी layer या UI के बाहर ले जाया जा सकता है
    • Redux state को unify करने और समय के साथ atomic changes को coordinate करने वाली React state management library है
    • Redux ecosystem API data caching manage करने वाले tools और patterns के साथ expand हुआ है
    • इस तरह का उपयोग cache logic को centralize करने, updates coordinate करने और components के बीच cached results share करने के उद्देश्य से होता है
  • जैसे-जैसे caching layer बढ़ती है, यह rendering engine और user actions के हिसाब से data को efficiently संभालने वाले central storage system जैसी बन जाती है

Manual indexes और consistency का बोझ

  • फ्रंटएंड में भी server से मिले data को ID-keyed object में store करके fast lookup और modification किया जा सकता है
    • REST API इस्तेमाल करने वाले apps में data को batches में पढ़ना और जरूरी objects को अतिरिक्त रूप से enrich करना अक्सर होता है
    • Objects को ID के हिसाब से store करने पर API results को cache में merge करना आसान होता है
    • यह structure ID-based create, read, update, delete के लिए optimized होता है
  • कई items scan करने वाली filtering, सभी entries scan करने से बचने के लिए अलग index बनवाती है
    • createdAt के year/month/day हिस्से के आधार पर ENTRIES_BY_DATE structure बनाने से किसी खास तारीख की entries जल्दी मिल सकती हैं
    • इसके बदले CACHE और ENTRIES_BY_DATE के बीच consistency लगातार बनाए रखनी पड़ती है
    • date range queries के लिए कई accesses चाहिए होते हैं, और date-sorted array के साथ अधिक complex query/update logic बेहतर structure हो सकता है
  • Indexes बढ़ने पर हर index के लिए create, update और query logic चाहिए होता है
    • correctness verification tests और code review का बोझ बढ़ा देता है
    • किसी एक index में delete या update छूट जाए तो मुश्किल से पकड़ में आने वाले bugs हो सकते हैं
    • नई application features की तुलना में इस complexity को manage करने वाली infrastructure पर ज्यादा समय लग सकता है
  • असली database indexes सिर्फ data को दूसरे रूप में store करने से कहीं अधिक complex होते हैं
    • इनमें statistics collection, data versioning, transaction control, locking, और query optimization के साथ interaction जैसे elements शामिल होते हैं

Optimistic changes से जुड़ी consistency समस्याएं

  • Optimistic change server response से पहले किसी operation के effect को locally पहले simulate करता है
    • UI network delay न होने जैसा तुरंत respond कर सकता है
    • Server अगर expected से अलग decision ले या error हो जाए, तो UI को change rollback करके user से problem fix करने को कहना पड़ सकता है
    • जब client server result का अच्छा अनुमान लगा सके, errors client पर handle किए जा सकें, और logic closely synchronized हो, तब यह एक powerful tool बनता है
  • Optimistic update आमतौर पर चार steps में चलता है
    • UI write operation trigger करता है
    • Server सहमत होगा इस assumption पर local cache में change apply करके UI को तुरंत re-render किया जाता है
    • Change operation asynchronous तरीके से server को भेजा जाता है
    • Server response को local cache में merge करके पिछले optimistic change को overwrite किया जाता है, और जरूरत हो तो UI फिर render होता है
  • Server के साथ consistency बनाए रखने के लिए कई बोझ पैदा होते हैं
    • Results predict करने के लिए client और server में logic duplicate करना पड़ता है
    • Async errors या server mismatch handle करने के लिए हर in-flight change को track करना पड़ता है
    • बेहतर user experience के लिए optimistic cache हिस्से को durable बनाकर app restart के बाद changes reconcile करने पड़ सकते हैं
  • यह process development time और correctness verification cost बढ़ाता है, और data management user value या differentiating features के development से आगे निकल सकता है

Recursive cache invalidation की complexity

  • ज्यादा data वाले apps में वही information cache की कई जगहों पर दिखाई देती है
    • Example cache projects, tasks, users को साथ store करता है
    • Task पूरा होने के बाद project progress, user-assigned tasks, new task information जैसे कई हिस्से प्रभावित हो सकते हैं
  • Task पूरा होने के बाद cache को server से मिलाने के लिए कई round trips लग सकते हैं
    • Server को task completion की सूचना देना
    • Project progress बदल गया है, इसलिए project refresh करना
    • New task assignment है या नहीं, यह check करना
    • New assignment हो तो उस task को fetch करना
  • ज्यादा complex API से round trips की संख्या घटाई जा सकती है, लेकिन API या client logic का underlying data model से coupled होना बाकी रहता है
    • GraphQL इस समस्या के लिए एक approach है, लेकिन complete solution नहीं
  • ऐसा structure जिसमें UI को हर change के लिए पता होना चाहिए कि cache का कौन-सा हिस्सा relevant है, scale बढ़ने पर fragile हो जाता है
    • Data relationships और aggregations local cache के कई हिस्सों को प्रभावित कर सकते हैं
    • Engineering team बड़ी होने पर problem team boundaries पार कर सकती है, और बड़े software project के mutable global variables जैसी लग सकती है
  • Optimistic changes के साथ जुड़ने पर client server changes predict करने के लिए और ज्यादा backend logic replicate करने लगता है
    • उदाहरण में task 1 को user 1 से remove करके, total task count के आधार पर progress को नए ratio में calculate करने की कोशिश की जा सकती है
    • जितना अधिक nested changes को locally predict करने लायक बनाया जाता है, client उतना ही ज्यादा backend stack duplicate करता है

SQLSync द्वारा प्रस्तावित frontend database stack

  • SQLSync SQLite पर बना frontend-optimized database stack है
    • Synchronization engine Git और distributed systems के ideas पर आधारित है
    • इसे React, Vue, Next.js जैसे popular frontend frameworks के साथ smoothly integrate होने के लिए design किया गया है
    • लक्ष्य कठिन data management समस्याएं संभालना है ताकि developers application-specific features पर focus कर सकें
  • Example Todo app पूरी data layer को Rust की 60 lines और components में बिखरी कुछ SQL queries से implement करता है
    • SQLSync durable cache, SQLite के indexes/constraints/triggers/query optimization, optimistic changes, smart cache invalidation, और reactive queries प्रदान करता है
  • Local data एक या अधिक SQLite databases में store होता है
    • Indexes आसानी से बनाए जा सकते हैं और data के साथ automatically synchronized रहते हैं
    • Database backend की तरह indexes को automatically use करके queries को accelerate कर सकता है
    • SQL complex data queries express कर सकता है, और triggers, foreign keys, constraints, full-text search जैसी features भी use की जा सकती हैं
  • Optimistic changes reducer से handle होते हैं
    • Structure Redux के core concepts जैसा है
    • Reducer किसी भी ऐसी language में लिखा जा सकता है जिसे WebAssembly में compile किया जा सके
    • SQLSync client पर changes को optimistically execute करता है, और server पर globally consistent order में execute करता है
    • इसके बाद client Git Rebase जैसी operation से server के साथ synchronize करता है
  • इस architecture का फायदा यह है कि recursive cache invalidation की जरूरत खत्म हो जाती है
    • सभी data change logic ऐसे reducer में लिखा जाता है जिसे client और server में share करना आसान है
    • Change के दौरान हुई सभी data changes automatically दिखाई देती हैं
    • Synchronization Git Rebase की तरह काम करता है, इसलिए server अगर client से अलग changes करे, तब भी client के उसी consistent result तक पहुंचने की guarantee होती है

संबंधित कार्य

  • Riffle का “Building data-centric apps with a reactive relational database” UI state सहित सभी application state को एक reactive database में store करने के idea पर चर्चा करता है
    • Reactive queries साफ mental model देती हैं और React जैसे declarative systems के साथ अच्छी तरह fit होती हैं
    • Client app development की समस्याओं को database community के ideas से solve करता है
    • Relational data model और real indexes से state model करने के benefits पर चर्चा करता है
  • Instant.db के Stepan ने browser के अंदर database पर दो posts लिखीं
    • Database in the Browser, a Spec
    • A Graph-Based Firebase
    • दोनों posts frontend और backend stack के relationship पर ज्यादा focus करते हुए समान समस्या पर चर्चा करती हैं, और Firebase के graph-based successor के रूप में Instant.db बनने की motivation समझाती हैं
  • Matt Wonlaw का CR-SQLite एक SQLite extension है
    • यह conflict-free replicated data types (CRDT) और causal order event log का इस्तेमाल करके data को consistently merge करता है
    • Central coordinator के बिना peer-to-peer apps को SQLite में data store करने और collaborate करने देता है
    • यह browser में SQLite चलाने का example भी है
  • Matt Wonlaw related ideas भी explore कर रहे हैं

1 टिप्पणियां

 
GN⁺ 2023-12-02
Hacker News की राय
  • मैं इस प्रोजेक्ट को अच्छी तरह जानता हूँ, और इसे बनाने वाला मेरा दोस्त है, इसलिए मैं उसे यहाँ आकर सवालों के जवाब देने के लिए कहूँगा
    वह एक अनुभवी database architect है। उसने SQLsync इस तरह बनाया कि frontend developers remote database को ऐसे query और update कर सकें जैसे वह पूरी तरह browser के अंदर ही हो। वास्तव में यह लगभग वैसा ही है, और WASM की वजह से पूरा SQLite database browser तक भेजा जा सकता है। असली बात कई clients के बीच sync करने वाले एक चतुर लेकिन सरल reactive algorithm में है
    अगर मानें कि development काम का बड़ा हिस्सा data synchronization है, तो React और REST API को भी एक तरह की synchronization procedure माना जा सकता है, और यह approach नई संभावनाएँ खोलती है। API से लाकर cache की हुई object tree के रूप में एक और अजीब custom database बनाने की बजाय, relational database की ताकत के साथ local में ही सीधे update और query किया जा सकता है

    • मेरा मानना है कि पूरे query system को frontend में ले आना ही वह चीज़ है जो बहुत से frontend developers सच में चाहते हैं। वे data के लिए एक शक्तिशाली query system चाहते हैं, न कि transport layer, REST, GraphQL, या *RPC जैसी चीज़ों को बार-बार फिर से invent करना
      लेकिन पारंपरिक web companies में specialized backend/frontend teams की वजह से इसे अपनाना मुश्किल है। यह database, backend, transport, और authentication layers को हटाकर उन्हें एक block system से बदलने जैसा है, और ज़्यादातर system architects backend background से आते हैं इसलिए वे इस समस्या को ठीक से नहीं समझते। यह दोनों तरफ गहराई से असर डालता है, इसलिए मौजूदा systems में अच्छी तरह फिट नहीं बैठता, और आख़िरकार नए development पर ही ज़्यादा सूट करता है। Backend न तो कोई AWS या Azure service है, न ही Lambda-friendly, इसलिए जिन तरह के architects से मैं मिलता हूँ वे ज़्यादातर इसे छूना नहीं चाहेंगे
      यह तरीका कुछ हद तक पुरानी CouchDB+PouchDB तकनीक में पहले से मौजूद है। कुछ use cases में यह काफ़ी अच्छा बैठता है, लेकिन इसका query system आदर्श नहीं है, और authentication तथा data scoping का तरीका ज़्यादातर लोगों को अपरिचित लगता है। जब data पूरी तरह एक single user का हो और आप per-user database model जैसा का तैसा इस्तेमाल करें, तब यह सबसे आसान होता है। CRDT के साथ data को मज़बूती से partition करने पर conflict की समस्या भी काफ़ी कम हो जाती है
      लेकिन scalability की समस्या है। CouchDB में 10 हज़ार से 1 लाख users के connect होने पर CPU की मांग बहुत बढ़ जाती है, और तकनीक पुरानी होने के बावजूद maintain की जा रही है। System design के नज़रिए से, जैसे ही users के बीच data share करना शुरू करते हैं, complexity बहुत तेज़ी से बढ़ती है, इसलिए यह complexity को हल करने की बजाय बस उसकी जगह बदल देने जैसा हो जाता है, और इसकी उपयुक्तता कम हो जाती है
      यह approach भी शायद उसी लक्ष्य को साध रही है, लेकिन इसके भी ऐसी ही scalability समस्याओं से जूझने की संभावना काफ़ी है। आगे यह कैसे विकसित होती है, यह देखने की उत्सुकता है; यह एक पहला कदम लगती है
    • यह remote में sync होने वाले और फिर peers के साथ दोबारा sync होने वाले database को query/update करने की क्षमता वाले Couchbase से काफ़ी मिलता-जुलता लगता है। Server-side JavaScript plugins के ज़रिए authentication या business logic को भी आसानी से नियंत्रित किया जा सकता है
    • मैं सच में जानना चाहता हूँ कि सिर्फ़ संबंधित हिस्सों को LocalStorage / SessionStorage में cache कर देने से काम क्यों नहीं चल सकता
      मुझे याद है कि पहले Chrome ने browser में literally SQL database डालने की कोशिश की थी, लेकिन बात बनी नहीं और localStorage mainstream बन गया। मैं इसकी usefulness को कम करके नहीं आँक रहा; मैं बस आम तौर पर वही चुनता हूँ जो browser देता है। WASM और उसके ज़्यादा mature होने या features बढ़ने के साथ इसे browser में लाने की संभावना को लेकर मैं काफ़ी उत्साहित हूँ
  • जिस कंपनी में मैं पहले था, वहाँ project management software में changes के लिए checkout/checkin mechanism था। जब आप project checkout करते थे, तो local में edit करने के लिए उसकी एक copy डाउनलोड होती थी, और checkin करने पर वह फिर server पर upload हो जाती थी। Checkout state में project lock हो जाता था। Live update apps के दौर में यह सबको पुराना तरीका लगता था
    लेकिन 10 साल तक SPA webapps बनाने के बाद, अब लगता है कि data synchronization का वह तरीका अपने समय से आगे था

    • कई users के parallel live updates को support करना है, या एक समय में सिर्फ़ एक update होने देने के लिए lock करना है, यह आख़िरकार technical decision नहीं बल्कि business decision है। असली सवाल यह है कि क्या application के लिए उपयुक्त business rules simultaneous live updates को संभालने की अनुमति देते हैं
      बात आख़िर में इस पर आकर टिकती है कि क्या आप कई concurrent updates के बीच inconsistencies को consistently resolve करने की प्रक्रिया implement कर सकते हैं। कुछ मामलों में यह संभव है, कुछ में नहीं, और यह technical capability से ज़्यादा business rules पर निर्भर करता है
      अगर business rules के हिसाब से resolution mechanism implement नहीं किया जा सकता, तो concurrent updates को support करने की technical क्षमता होने पर भी एक समय में सिर्फ़ एक update के लिए lock ज़रूरी होगा
    • इस तरीके से जाएँ तो बहुत-सी समस्याएँ हल हो जाती हैं और implementation भी बहुत आसान हो जाता है
      लेकिन लोगों को यह समझाना मुश्किल है कि वास्तव में वे यही चाहते हैं। यह बड़े भ्रम में पड़ना आसान है कि सब कुछ हमेशा उपलब्ध होना चाहिए, जबकि हक़ीक़त में आम तौर पर एक समय में एक ही व्यक्ति बदलाव करता है, और अगर दो या उससे ज़्यादा लोगों को काम करना हो तो अक्सर उन्हें वैसे भी बात करके या communication से तालमेल बिठाना पड़ता है
      Git जैसे पूरी तरह distributed development में भी conflicts अपने-आप जादुई तरीके से resolve नहीं हो जाते। सही बदलाव चुनने के लिए अभी भी दूसरों से बात करनी पड़ती है और context समझना पड़ता है
    • यही वजह है कि लोग भले ही MySQL जैसे पारंपरिक relational database paradigm को NoSQL या MongoDB जैसे non-relational databases से कमतर बताएं, फिर भी वह बना रहता है। सिर्फ़ इसलिए कि कुछ तेज़ है या cool लगता है, उससे सब कुछ replace नहीं किया जा सकता
      कुछ चीज़ों के लिए tested solution ही चाहिए होता है
    • यह RCS जैसा लगता है https://en.wikipedia.org/wiki/Revision_Control_System
      मुझे याद है, जब कंपनी ने RCS से CVS पर migration किया था, तो मेरे एक सहकर्मी ने इस बात पर नाराज़गी जताई थी कि CVS lock-based checkout support नहीं करता
      https://en.wikipedia.org/wiki/Concurrent_Versions_System
    • मुझे यह approach पसंद है। SQLSync असल में यही काम लगातार करता है, लेकिन अगर explicit coordination हो तो ऐसा checkin/checkout तरीका भी संभव होना चाहिए
      मेरा मानना है कि single-owner lock strategy को भी SQLSync से simulate किया जा सकता है। हालाँकि, app के हिसाब से इसकी ज़रूरत न भी हो सकती है। अगर लक्ष्य offline work के बाद तैयार होने पर merge करना है, तो SQLSync यह pattern by default देता है। अगर लक्ष्य यह है कि सिर्फ़ एक client ही changes कर सके, तो central lock pattern चाहिए होगा, और संभव है कि इसे भी SQLSync के ज़रिए coordinate किया जा सके
  • यहाँ "जिस चीज़ को मापा जाता है, उसी को मैनेज किया जाता है" वाले सिद्धांत और sunk cost fallacy का मेल है।
    डेटाबेस की असली समस्या जटिलता है। हर अलग फीचर आम तौर पर सुरक्षित होता है, लेकिन जैसे ही reliability, caching और index एक-दूसरे से जुड़ते हैं, जटिलता विस्फोटक रूप से बढ़ जाती है, और आम तौर पर domain-specific DB को खुद implement करना तर्कसंगत नहीं होता।
    लेकिन जब कंपनी को यह एहसास होता है कि वह उन तीन फीचर्स को implement करने में पहले ही निवेश कर चुकी है और बहुत सारे resources झोंक चुकी है, तब राजनीतिक रूप से यह कहना मुश्किल हो जाता है कि इसे हटा दिया जाए, और technical debt को एक बार में हटाने की वास्तविक लागत भी बहुत बड़ी होती है।
    मुझे लगता है असली समस्या SQL syntax है। अगर एक basic relational database इस्तेमाल करने का अनुभव टूटी-फूटी अंग्रेज़ी की जगह परिचित C-style syntax जितना सहज होता, तो लोग खुद बनाने की बजाय DB इस्तेमाल करने के लिए ज़्यादा प्रेरित होते। NoSQL databases उस दिशा में अच्छा कदम थे, लेकिन ज़्यादातर ने रोज़मर्रा की उपयोगिता से ज़्यादा big data पर ध्यान दिया। Redis जैसी चीज़ों ने अपनी जगह बना ली है, और वह ठीक है।
    SQL को आसानी से चलाने लायक बनाना एक तर्कसंगत तरीका है, लेकिन अच्छे databases, जैसे कि मेरा पसंदीदा Postgres, में SQL ही मूल भाषा है, इसलिए उस भाषा का उपयोग किए बिना उसकी efficiency पाना मुश्किल है। सच में PostgresPostSQL जैसे डेटाबेस की ज़रूरत है, जो Postgres को पूरी तरह replicate करे लेकिन जिसका default parser बेहतर syntax वाली भाषा को support करे।

    • मुझे ठीक से समझ नहीं आता कि SQL में आखिर कठिन क्या है। मेरा मानना है कि हर developer को SQL आना चाहिए। SQL syntax भी अच्छा है और इतना परखा जा चुका है कि लंबे समय तक टिक पाया है। इसकी आलोचना करने से बेहतर है कि लोग इसे सच में सीखने में समय लगाएँ।
    • SQL की अक्सर आलोचना होती है, और मुझे भी लगता है कि उसके पीछे वाजिब कारण हैं, लेकिन फिर हम इससे बेहतर कुछ बना क्यों नहीं पाए?
      सामान्य programming में दर्जनों भाषाएँ इस्तेमाल होती हैं और वे लगातार विकसित होती रहती हैं। यहाँ तक कि JavaScript, जिसे browser चलाता है और जिसे बदलना कठिन है क्योंकि हम उपयोगकर्ता के browser को नियंत्रित नहीं कर सकते, वह भी transpilers और WebAssembly के ज़रिए विकसित हो रही है।
      लेकिन databases की दुनिया में practically सिर्फ SQL ही है। विकल्प हैं, लेकिन उपयोग के स्तर पर SQL के करीब कोई नहीं पहुँचता। शायद इसका मतलब यह है कि SQL उतना बुरा है ही नहीं।
      वजह यह हो सकती है कि relational model वास्तव में बहुत अच्छा है। इससे बाहर जाने की कोशिशें शायद केवल niche क्षेत्रों में ही काम करें। declarative style भी बहुत अच्छी है, और इससे हटकर बड़ी सफलता पाना मुश्किल है। आखिरकार अगर आप सिर्फ अलग syntax वाला SQL ही बनाते हैं, तो ज़्यादातर लोगों के लिए वह इतना बड़ा सुधार नहीं होगा कि वे अपना तरीका बदलें।
    • अच्छा होता अगर Postgres में SQL से ज़्यादा stable और lower-level API होता। वह शायद EXPLAIN से मिलने वाले query plan जैसी किसी चीज़ के रूप में हो सकता था।
      इस API को target करके लिखी गई application SQL DB को implement कर सकती है। बस SQL को parse करना होगा और ऐसा query planner बनाना होगा जो इस API के अनुरूप query plan निकाल दे।
    • मैं जानना चाहूँगा कि क्या आपने उन लोगों के लिए, जो सीधे SQL से बचना चाहते हैं, डेटाबेस के ऊपर semantic layer जोड़ने वाले तरीकों को देखा है।
  • मैं ही लेखक हूँ। मैंने ज़्यादातर सवालों पर बस सरसरी नज़र डाली है, और मैं समय-समय पर देखता रहूँगा कि कहीं कोई सवाल छूट तो नहीं गया। यह भी जानना चाहूँगा कि HN discussion को बेहतर तरीके से track करने का कोई तरीका किसी ने बनाया है या नहीं।
    अब तक की चर्चा बहुत अच्छी लगी। शुरुआती पोस्ट में मेरा फोकस SQLSync वास्तव में कैसे काम करता है, इस पर कम और frontend engineering की उस प्रेरणा पर ज़्यादा था, जिसने मुझे SQLSync बनाने तक पहुँचाया। अगली पोस्ट में मैं इसके काम करने का तरीका कवर करूँगा।

    • मैं जानना चाहूँगा कि क्या mobile support की कोई योजना है। यही वह जगह है जहाँ मैं इसे सबसे ज़्यादा आज़माना चाहूँगा।
  • उपयोगकर्ताओं को ऐसा mental model नहीं देना चाहिए जिसे वास्तविकता बहुत बुरी तरह, या चुपचाप, तोड़ सके।
    मुझे चिंता है कि client-server model की बजाय database sync करने का तरीका शायद ऐसा ही एक मामला हो। sync mechanism बस टूटकर बिखर सकता है, या उसके पीछे ऐसी गहरी assumptions हो सकती हैं जो वास्तव में पूरी नहीं होतीं।
    अगर तेज़ UI चाहिए, तो मुझे लगता है कि CRDT primitives का एक सेट बनाकर इस्तेमाल करना ज़्यादा सुरक्षित लगेगा, और बाकी चीज़ें form submission तक ही सीमित रखी जाएँ।

    • सहमत हूँ। SQLSync इस्तेमाल करने वाले developers के लिए mental model को आसानी से समझ में आने लायक बनाना हमारे लक्ष्यों में से एक है। मैं पक्षपाती हो सकता हूँ, लेकिन व्यक्तिगत रूप से मुझे rebase model, CRDT की तुलना में, काफ़ी अधिक समझने योग्य लगता है।
  • client और server के बीच state sync एक अभिशप्त समस्या है।
    अगर हम user experience में थोड़े समझौते के लिए तैयार हों और PHP/server-side rendering मॉडल के थोड़ा करीब लौट जाएँ, तो इस समस्या से पूरी तरह बचा जा सकता है। SPA अच्छी चीज़ है, लेकिन multipart form submission आज भी काम करती है। बहुत थोड़े JavaScript से भी अधिकांश खुरदरे हिस्सों को सुधारा जा सकता है।
    हाल की web products में client-side state बस third-party IdP के authentication claims, query parameters में first-party session ID, और current document तक सीमित रही है। पहला कहाँ store होता है, यह सच कहूँ तो मुझे भी नहीं पता। वह Microsoft की समस्या है, हमारी नहीं। बाकी सारी state server पर है।
    client को ऐसे संभालते हैं जैसे वह बस दिन भर input उगलने वाला एक बेवकूफ terminal हो। first-party cookies या local storage भी इस्तेमाल नहीं करते। इस तरीके ने iOS/Safari के लिए development experience को बहुत बेहतर बनाया है।
    इसलिए मैं पूछना चाहता हूँ कि आप वास्तव में कैसा अनुभव देना चाहते हैं, और ऐसा क्या है जो client और server state को अलग रखने को जायज़ ठहराता है?

    • consumer graphical interfaces में optimistic rendering बहुत आम flow है, और अगर आप वह कर रहे हैं, तो आप client/server state को संभाल ही रहे हैं। आज भी जगह-जगह loading spinner दिखते हैं, लेकिन वे आम तौर पर शुरुआती content load के लिए होते हैं। उदाहरण के लिए, Gmail किसी email को archive करते समय आपको इंतज़ार नहीं करवाता।
    • ElectricSQL ने इस समस्या को हल करने की दिशा में काफ़ी बड़ी प्रगति की है। यह client पर SQLite में लिखता है और Postgres के साथ sync की गारंटी देता है।
      संदर्भ: https://news.ycombinator.com/item?id=37584049
  • SQLite-आधारित offline/local-first इन दिनों काफ़ी चर्चा में लग रहा है। इस हफ़्ते मैं इसके बारे में तीसरी बार पढ़ रहा हूँ, और यह अच्छा लग रहा है
    लेकिन ElectricSQLhttps://electric-sql.com/ और PowerSynchttps://powersync.com/ की तुलना में यह कैसा है?

    • यह वाकई बहुत गरम क्षेत्र है। अलग-अलग approaches सामने आते देखना बहुत दिलचस्प है
      ElectricSQL और PowerSync दोनों partial replication जैसी बहुत कठिन समस्या से निपट रहे हैं। वे ऐसा सामान्य समाधान बनाने की कोशिश कर रहे हैं जिसमें पारंपरिक central database, client को केवल वही डेटा two-way sync करे जिसकी उसे ज़रूरत है, और साथ ही optimistic changes तथा उनसे जुड़े consistency/conflict handling को भी support करे
      इसकी कमी implementation complexity है। यह ठीक-ठीक track करना पड़ता है कि पूरे database में से कौन-सा subset हर client के पास है, ताकि changes सिर्फ़ उसी subset तक push किए जा सकें। साथ ही database state में से कौन-सा subset डाउनलोड करना है, यह बताने के लिए एक नया DSL भी चाहिए, और उसे अलग से सीखना व optimize करना पड़ता है। फिर भी यह अच्छा है कि वे इतनी कठिन समस्या हल कर रहे हैं, और जब SQLSync partial replication support करने के लिए तैयार होगा, तब तक best practices शायद पहले से व्यवस्थित हो चुकी होंगी
      दूसरी ओर, SQLSync अभी सिर्फ़ full DB sync support करता है। इससे सभी clients को पूरे database का एक consistent view मिलता है। इस पर तुरंत सवाल उठ सकता है कि क्या यह अच्छा विचार है, और कुछ apps के लिए यह उपयुक्त नहीं है। लेकिन अगर personal finance app को देखें, तो उसका मुख्य लक्ष्य device-to-device sync, cloud backup, offline capability वगैरह होता है, इसलिए हर device पर पूरा DB स्टोर होना शायद उल्टा वही तरीका हो जिसकी ज़रूरत है। Airtable जैसे document-oriented data model भी इसका उदाहरण हो सकते हैं। अगर हर Airtable को अलग database माना जाए, तो किस table पर ध्यान देना है यह client संभाल सकता है
      full DB sync पर ध्यान देने से sync engine, partial replication support करने वाले समाधान की तुलना में काफ़ी सरल हो जाता है। इसके फ़ायदों में से एक यह है कि backend बहुत हल्का हो सकता है। मौजूदा demo(https://sqlsync-todo.pages.dev) Cloudflare Durable Objects के भीतर बहुत कम storage और CPU time के साथ पूरी तरह चल रहा है
      SQLSync को ऐसे use cases संभव बनाने के लिए अभी बहुत काम बाकी है, और यह अब भी prototype के काफ़ी क़रीब है, लेकिन शुरुआती tests बहुत सकारात्मक रहे हैं
  • बड़े multi-tenant apps में, जहाँ individual datasets काफ़ी छोटे होते हैं, मैंने कई बार सोचा है: “क्यों न पूरा database ही client को भेज दिया जाए?” यह standards से इतना बाहर और किसी cursed architecture pattern जैसा लगा कि मैंने इसे ज़्यादा आगे नहीं बढ़ाया। अच्छा होगा अगर पता चले कि मैं ग़लत था

    • मैंने एक बार calorie counting app में ऐसा किया था। Database में सैकड़ों हज़ार foods होने के बावजूद app, ज़्यादातर media apps या games से काफ़ी कम जगह लेता था
    • मैं भी सोच रहा हूँ कि क्या यह सच में cursed तरीका है। अभी तक तो यह उम्मीद से कहीं बेहतर रहा है। https://sqlsync-todo.pages.dev जैसे apps इस pattern में लगभग मामूली हो जाते हैं
      इसे सही मायने में साबित करने के लिए अभी बहुत काम करना है, लेकिन इसे आगे बढ़ाकर देखना कि यह कहाँ तक जाता है, इसे लेकर मैं काफ़ी उत्साहित हूँ
    • सही जवाब है UI को वापस उस server पर ले जाना जहाँ database पहले से मौजूद है, और client-side HTML renderer यानी web browser को सिर्फ़ HTML भेजना। यह पूरा लेख ज़्यादा कुछ ऐसा लगता है: “frontend हद पार करके चट्टान से नीचे समुद्र में गिर गया है”
  • यह उन समस्याओं में से एक लगती है जो SPA छोड़ देने पर पूरी तरह गायब हो जाती हैं
    Hotwire या htmx जैसी solutions इस्तेमाल करें तो queries बस server queries बन जाती हैं, और ऐसी queries को तेज़ बनाना एक कहीं बेहतर समझी हुई समस्या है

    • यह सिर्फ़ websites की समस्या नहीं है। क्या mobile और desktop ecosystems को भी browser की तरह बड़े पैमाने पर thin clients की ओर बढ़ना चाहिए? क्या Apple Reminders या Google Tasks जैसे simple apps को latency या connection issues की वजह से GUI रोक देना चाहिए?
    • मैंने हाल ही में htmx इस्तेमाल किया, और इससे हटने वाली complexity तथा उसके बदले मिलने वाली productivity सच में अविश्वसनीय है। यह भी बहुत बड़ी नेमत है कि आप अपनी मनचाही stack वैसी की वैसी इस्तेमाल कर सकते हैं
      मैंने इसे ocaml + web components के साथ इस्तेमाल किया, और यह productivity 10/10 अनुभव था। बस एक build tool चाहिए जो पलक झपकने से भी तेज़ compile करे, और frontend व backend के बीच JSON mapping wiring की भी ज़रूरत नहीं पड़ती, इसलिए यह सच में बहुत productive है
    • सच कहूँ तो Hotwire या Livewire जैसे solutions, SPA जितने immediate नहीं होते
      व्यक्तिगत रूप से मैं InertiaJs https://inertiajs.com को पसंद करता हूँ। यह server के साथ state को “पुराने तरीके” से sync करने वाली एक तरह की frontend router system है
    • यह कुछ वैसा ही है जैसे कहना: “ज़्यादा interaction वाले webapps मत बनाओ” या “ऐसी service मत चलाओ जिसे एक से ज़्यादा VM चाहिए।” यह बेतुकी राय है
    • मेरी राय में समस्या SPA से दूर जाने पर नहीं, बल्कि उन high-interaction features से दूर जाने पर गायब होती है जो अक्सर अच्छे products में होते हैं
      ख़ासकर अगर product को ऐसे इलाक़ों में भी काम करना है जहाँ internet अस्थिर रहता है, तो और भी
  • अभी मैं “full-stack database” पर एक बहुत मिलता-जुलता लेख लिख रहा हूँ। इसमें उस पैटर्न पर बात की गई है जहाँ कई apps backend और database logic को frontend client code में फिर से बना देते हैं। हमारा सुझाया गया समाधान यह है कि ऐसा database चुना जाए जो server और client दोनों तरफ चल सके, और फिर दोनों के बीच sync किया जाए
    हम अपने product में SQLite का उपयोग नहीं करते, क्योंकि सच कहें तो application data को query करने के लिए SQL सही tool नहीं है। यह client code में चाही गई data structure के साथ आसानी से fit नहीं बैठता, और लगभग सभी SQL databases में query polling को बार-बार दोहराए बिना query changes को subscribe करने का तरीका नहीं होता
    अगर आपको client में एक पूरा database रखने का विचार पसंद है और आप TypeScript/JavaScript के साथ गहरा integration चाहते हैं, तो हमारे बनाए https://github.com/aspen-cloud/triplit को देखें

    • दरअसल Postgres, WAL के जरिए real-time change subscriptions के लिए एक शानदार तरीका देता है। इस open source library को भी मैं ही maintain करता हूँ
      https://github.com/cpursley/walex
    • SQLite वास्तव में update hook के माध्यम से changes detect करने के लिए जरूरी mechanism देता है। अफ़सोस है कि कई SQLite bindings इसे expose नहीं करते
      मैं इसे बहुत सरल तरीके से इस्तेमाल करता हूँ। जब base tables का data बदलता है, तो query अपने-आप फिर से run हो जाती है। यह results को incrementally update करने जितना efficient नहीं होगा, लेकिन SQLite queries आम तौर पर इतनी तेज़ होती हैं कि मुझे यह बड़ी समस्या नहीं लगती
    • मैं इस बात से सहमत नहीं हूँ कि यह client code में चाही गई data structure के साथ आसानी से fit नहीं बैठता। Data normalization frontend reactive applications के लिए महत्वपूर्ण है, और data को up-to-date रखने के लिए यह लगभग ज़रूरी है। सभी CRUD operations को संभालना भी इससे बहुत आसान हो जाता है
    • Realm Sync और Mongo + Kotlin Multiplatform के साथ server, web, mobile, desktop जैसे लगभग सभी platforms को cover किया जा सकता है। हाँ, इसकी cost आती है। alternatives में सचमुच दिलचस्पी है, और मैं जानना चाहता हूँ कि क्या यह बात भी लेख में शामिल है