हर Durable Object में zero-latency SQLite स्टोरेज
(simonwillison.net)- Cloudflare का Durable Object key/value स्टोरेज से SQLite-आधारित relational स्टोरेज में बदल रहा है, जिससे application logic और data एक ही physical host पर चलने वाली संरचना बनती है
- scaling एक object को बड़ा करने के बजाय document, user, database shard जैसी logical state units के हिसाब से Durable Object को बाँटने के तरीके से होती है, और एक single object पर single machine·single thread की सीमा लागू रहती है
- flight reservation जैसे सिस्टम में, जहाँ state unit स्पष्ट होती है, हर flight के लिए dedicated Durable Object और उसका अपना SQLite database रखा जा सकता है, जिससे हर airline के लिए रोज़ हज़ारों नए database बन सकते हैं
- durability को WAL entry streaming, object storage में सेव करना, और नज़दीकी data center replication के संयोजन से बनाया गया है; batching हर 16MB या 10 सेकंड पर होती है और अधिकतम 30 दिनों की point-in-time recovery को support करती है
- JavaScript API ने तेज़ single-thread persistence operations के लिए blocking approach चुना है, और Durable Object की location बनने के बाद fixed रहती है, हालांकि आगे dynamic relocation की योजना है
SQLite में बदला Durable Object डिज़ाइन
- Cloudflare का Durable Object key/value स्टोरेज से upgrade होकर SQLite-आधारित relational system बन गया है
- मुख्य विचार यह है कि application logic को उस data के उसी स्थान पर रखा जाए जिसे वह logic संभालती है
- एक Durable Object उस code से बना होता है जो उसके द्वारा इस्तेमाल किए जाने वाले SQLite database के उसी physical host पर चलता है
- लक्ष्य यह है कि reads और writes को network round-trip के बिना संभाला जाए, जिससे latency कम करना आसान हो
- बड़े पैमाने पर processing के लिए एक object की throughput बढ़ाने के बजाय अधिक objects बनाने का तरीका अपनाया जाता है
- एक single object single machine के single thread पर चलता है, इसलिए throughput की एक मूलभूत सीमा होती है
- document, user, database shard जैसी अलग-अलग logical state units को अलग objects सौंपने से scale करना आसान होता है
- flight reservation system में हर flight को dedicated Durable Object और उसके अपने SQLite database से map किया जा सकता है
- इस संरचना में हर airline के लिए रोज़ हज़ारों नए database बन सकते हैं
- हर Durable Object का एक unique नाम होता है, और Cloudflare network request को global network के भीतर उस location तक route करता है जहाँ वह object मौजूद है
WAL streaming और replication से मिलने वाली durability
- Durable Object-आधारित system, Litestream से प्रेरित होकर, हर object की WAL entries sequence को लगातार object storage में stream करता है
- batching हर 16MB पर या हर 10 सेकंड पर की जाती है
- रिकॉर्ड किए गए transactions को replay करके अधिकतम 30 दिनों तक point-in-time recovery संभव है
- 10-सेकंड विंडो के भीतर durability को बेहतर बनाने के लिए, write commit होते ही नज़दीकी अलग data center में मौजूद 5 replicas तक भेजी जाती है
- इनमें से 3 की confirmation मिलने पर ही write response approve किया जाता है
- Durable Objects का underlying system Storage Relay Service है, और यह Cloudflare के अलग D1 SQLite system को एक साल से अधिक समय से चला रहा है
API और object location का व्यवहार
- JavaScript API async नहीं बल्कि blocking approach का इस्तेमाल करती है
- डिज़ाइन का उद्देश्य तेज़ single-thread persistence operations देना है
- example code जानबूझकर N+1 query pattern का उपयोग करता है, जिसमें पहले documents की list ली जाती है और फिर हर document के author name को अलग query से लाया जाता है
- इसे ऐसे मामले के रूप में पेश किया गया है जहाँ SQLite इस pattern को संभालने के लिए उपयुक्त है
- फिलहाल Durable Object बनने के बाद अपनी location नहीं बदलता
- default रूप से, इसे उस data center के पास instantiate किया जाता है जहाँ पहली
get()request की गई हो - किसी दूसरी location पर manually create करने के लिए
get()में optionallocationHintparameter दिया जा सकता है - मौजूदा Durable Object की dynamic relocation आगे की योजना में शामिल है
- default रूप से, इसे उस data center के पास instantiate किया जाता है जहाँ पहली
- where.durableobjects.live एक साइट है जो ट्रैक करती है कि Cloudflare network में नया Durable Object कहाँ create होता है
- उदाहरण में, Half Moon Bay से page load करने पर दिखाया गया कि San Jose के worker ने San Jose में Durable Object create किया
1 टिप्पणियां
Hacker News की राय
कुछ और दिलचस्प बातें भी हैं: write API synchronous है, लेकिन उसमें छिपा हुआ asynchronous wait है, इसलिए अगला response output करते समय अगर write fail हो जाए तो runtime response को HTTP failure में बदल देता है
इसलिए user को error या wait को स्पष्ट रूप से handle किए बिना runtime writes को अपने-आप batch कर सकता है और success को optimistically assume कर सकता है
read transactions नहीं हैं, जबकि किसी खास समय के snapshot pointer को पाने में वे उपयोगी हो सकते थे
हर runtime instance 128MB RAM तक सीमित है
WebSocket hibernate हो सकता है और सोए हुए समय के लिए billing नहीं होती, इसलिए DO के सोए रहने पर भी clients को connected रखा जा सकता है
एक automatic RPC जैसी capability भी है जिससे दूसरे DO या Worker को सामान्य JS call की तरह treat किया जा सकता है, जबकि वास्तव में वह किसी दूसरे data center को call कर सकता है और serialization व parsing runtime handle करता है
लेख में जिस Litestream का उल्लेख है, वह भी मिलती-जुलती technique सुझाता है
सामान्य SQLite में कई processes एक ही database को साथ-साथ access कर सकते हैं, इसलिए read transactions उपयोगी होते हैं
यहां सिर्फ एक process ही database access कर सकता है, इसलिए सभी reads को एक synchronous function के अंदर perform करके या process-level locking खुद implement करके वही effect हासिल किया जा सकता है
WAL entries को object storage में हर 16MB या हर 10 seconds पर batch stream किया जाता है, इसका मतलब लगता है कि globally writes को reliably पढ़ने में अधिकतम 10 seconds लग सकते हैं
समझ नहीं आता कि यह continent-level पर millisecond responses देने वाले regional database clusters की जगह कैसे ले सकता है
stream का इस्तेमाल है, यह पता है, लेकिन उसका target सिर्फ 5 followers हैं और Cloudflare के पास सैकड़ों data centers हैं
अगर सभी SQLite instances हमेशा connected नहीं हैं, तो seconds-level reads को physically guarantee नहीं किया जा सकता, और connected होने पर भी packet delay problem पैदा कर सकता है
write करके तुरंत read करें तो वह तुरंत reflect होता है। क्योंकि writes और reads current process की memory state को भी साथ-साथ update करते हैं
अगर कोई दूसरा process, जैसे कोई दूसरा DO या Worker, data access करना चाहे, तो उसे data रखने वाले DO से होकर जाना होगा, इसलिए वह RPC या HTTP request भेजेगा और latest info पाएगा
hibernation कुछ समय की inactivity के बाद होता है, इसलिए उम्मीद के उलट write unavailable होने की स्थिति शायद केवल तब होगी जब DO या Worker write के तुरंत बाद crash हो जाए
Cloudflare blog post इस हिस्से को ज्यादा detail में cover करता है। इसलिए writes तेज रहते हुए भी durability मिलती है
उस machine के पास अपने SQLite database का हमेशा consistent view होता है
objects अरबों में बनाए जा सकते हैं, लेकिन हर object का अलग database होता है
जिस machine पर DO चल रहा है, उससे अलग किसी machine से उस database को सीधे read करने का कोई तरीका नहीं है
हर DO globally unique होता है, और किसी खास ID का DO कहीं एक ही जगह चलता है तथा उस data center के local storage पर SQLite चलाता है
Durable Objects में जो बात अभी समझ नहीं आती, वह है भौतिक लोकेशन
जिज्ञासा है कि क्या यह उस रीजन में स्थित होता है जिसने पहली बार इसे बनाने वाली API call को host किया था
अगर ऐसा है, तो जब यह साफ हो जाए कि DO North America में बना था, लेकिन बाद में उसका सारा read/write traffic Australia से आ रहा है, तो क्या DO को अपने-आप किसी दूसरी लोकेशन पर ले जाने का कोई mechanism है
locationHintspecify किया जा सकता है। Australia के लिए"oc"इस्तेमाल करें: https://developers.cloudflare.com/durable-objects/reference/...यह भी ध्यान रखें कि “मौजूदा Durable Objects का dynamic relocation भविष्य की योजना है”
Cloudflare PoP में से सिर्फ करीब 10~11% ही Durable Objects host करते हैं
DO बनाने के लिए किसी दूसरे PoP पर आने वाली request, इन्हें host करने वाले नज़दीकी PoP में से किसी एक को forward कर दी जाती है
जहाँ तक याद है, Orleans(https://www.microsoft.com/en-us/research/wp-content/uploads/...) actors को machines के बीच move कर सकता है, और यह model DO को locations के बीच move करने के तरीके से अच्छी तरह match करता लगता है
हालांकि Durable Object को move कराने वाली कोई API call नहीं है
कोई connection नहीं होना चाहिए, और अगले या पहले connection पर यह सबसे नज़दीकी datacenter में फिर से बनाया जाता है
उस समय memory खत्म हो जाती है, लेकिन storage बचा रहता है
हाल में hibernation से जुड़े कुछ detailed features आए हैं, इसलिए यह explanation थोड़ी पुरानी हो सकती है
क्या किसी और को भी ऐसी नई cloud technologies समझना मुश्किल लगता है
मुझे web development में 15+ साल का experience है और मैं Laravel / Postgres / Redis stack इस्तेमाल करता आया हूँ, लेकिन ऐसे articles पढ़कर बस लगता है कि यह मेरे लिए नहीं है
तुरंत दिमाग में आने वाले real-time collaboration apps हैं Google Docs/Sheets, Notion, Miro, Figma जैसे apps
ये सभी global scale collaboration apps हैं, और मुझे पक्का नहीं कि Laravel stack ऐसे use cases support कर पाएगा या नहीं
Google को शायद ज्यादातर चीजें खुद implement करनी पड़ी होंगी और CRDT के उपयोग में वह आगे रहा होगा
लेकिन जैसे-जैसे pattern साफ होते हैं और components SaaS में बदलते हैं, large-scale real-time collaboration अब कोई बहुत बड़ा engineering problem नहीं रह जाता और ज्यादा दिलचस्प products संभव हो जाते हैं
Durable Object का design सच में पसंद आया। खासकर यह कि अंदर यह कैसे काम करता है, इसे समझना आसान है
real-time data के लिए design किए गए कई दूसरे solutions के उलट, Durable Objects में Redis और Italian food जैसी simplicity है। सारी ingredients दिखती हैं
अगर पर्याप्त time, resources और datacenters हों, तो कोई competent programmer DO docs पढ़कर कुछ वैसा ही फिर से implement कर सकता है
इसलिए इससे जुड़े trade-offs को judge करना आसान है
हालांकि चिंता यह है कि DO तेज और low-overhead real-time experiences, जैसे पाँच लोगों द्वारा किसी document को real-time में edit करने की सुविधा बनाने के लिए अच्छा है, लेकिन analytics और overviews बनाना बहुत मुश्किल कर सकता है
जैसे पिछले हफ्ते किस group ने कौन सा document edit किया था, और अगर data SQLite के अंदर डाल दें तो यह और मुश्किल हो सकता है
आपको किसी तरह बहुत सारे छोटे SQLite instances query करके results merge करने होंगे, इसलिए जिज्ञासा है कि क्या DO में इसके लिए कुछ है
आखिरकार मैं बार-बार Postgres पर लौट आता हूँ, क्योंकि इसे core app features के लिए भी, overviews और BI वगैरह के लिए भी इस्तेमाल किया जा सकता है
डिज़ाइन वाकई दिलचस्प है, लेकिन ऐसे स्मार्ट सिस्टम मुझे निजी तौर पर हमेशा अजीब-सी uncanny valley में लगते हैं
इसकी ज़रूरत ठीक दो ही मामलों में पड़ती है: या तो बहुत high-load सिस्टम को समझदारी से scale करना हो, या मज़े के लिए कोई toy project बनाना हो
toy project हो तो कुछ भी इस्तेमाल कर लेना ठीक है
लेकिन production या कामकाजी इस्तेमाल के लिए कुछ proven चाहिए
अगर आपको पता नहीं है कि इसकी ज़रूरत है, तो शायद ज़रूरत नहीं है; कोई boring Postgres database और VM जैसी चीज़ें इस्तेमाल कर लें
उल्टा, अगर आपको पता है कि इसकी ज़रूरत है, तो मुश्किल हो जाती है। यह अभी नया है और पर्याप्त mature नहीं है, इसलिए अजीब edge cases बहुत मिलने की संभावना है, और आप उन्हें debug करना या झेलना नहीं चाहेंगे
आखिर ऐसे सिस्टम किसके लिए हैं, समझ नहीं आता
यह इतना niche है कि बहुत-से serious players के इस्तेमाल से आसानी से mature होना मुश्किल है, और 99.9% कंपनियों के लिए यह बहुत जटिल है और इसमें बहुत सारे trade-offs भी हैं
इसका साफ़ target audience बस वे developers लगते हैं जो चमकदार चीज़ देखकर कंपनी बना लेते हैं, या उससे भी बुरा, दूसरों की कंपनी उसके ऊपर बना देते हैं, फिर जल्द ही पछताकर किसी अधिक boring चीज़ पर shift होते हैं
अगर आप कोई और Figma या Google Docs जैसी चीज़ बनाना चाहते हैं, तो Durable Objects का programming model बहुत सुविधाजनक है
यह लेख इसे और विस्तार से कवर करता है: https://digest.browsertech.com/archive/browsertech-digest-cl...
यह पुराना लेख भी काफ़ी relevant है: http://ithare.com/scaling-stateful-objects/
जिन लोगों ने Figma multiplayer वाला लेख पढ़कर सोचा था “मुझे लगभग यही चाहिए”, उनके लिए Durable Objects अच्छी तरह fit हो सकता है: https://www.figma.com/blog/rust-in-production-at-figma/
दूसरे approaches भी हैं। पहले मैंने WebRTC के ऊपर CRDT इस्तेमाल करके देखा था, और वह सचमुच future tech जैसा लगा
लेकिन cloud में कहीं मौजूद WebSocket और एक single class instance वाले आधार से वह कहीं ज़्यादा जटिल है
data और behavior को एक ही जगह रखना सच में complexity को quantifiable तरीके से घटाता है
यह latency और bandwidth की चिंता हटाता है, इसलिए operational और development दोनों तरह की चिंताएँ कम करता है। मशहूर N+1 problem का असर भी काफ़ी घट जाता है
आप यह तर्क दे सकते हैं कि networked Postgres दूसरे कारणों से बेहतर है, और हो सकता है सच में हो
लेकिन SQLite boring और predictable होने के मामले में सबसे भरोसेमंद चीज़ों में है, और इसकी known strengths भी साफ़ हैं
इसलिए server side पर भी इसकी popularity बढ़ रही है
हालांकि Durable Objects जैसे बहुत सारे छोटे databases बनाने का तरीका मुझे खास पसंद नहीं है
यह NoSQL nightmare की याद दिलाता है, और relational databases के important invariants को तोड़ सकता है
मेरे हिसाब से SQLite को Cloudflare के D1 product की तरह single database के रूप में इस्तेमाल करना कहीं बेहतर है
Cloudflare को अच्छी customer success stories चाहिए, और बहुत संभव है कि उनके बेहतरीन engineers success story पाने के बदले आपके साथ मिलकर देखें कि यह कैसे काम करता है, कैसे मदद करता है, और bugs कैसे handle किए जाएँ
validate हो जाने पर यह service relationship में बदल जाएगा, लेकिन शुरुआती दौर में यह partnership के ज़्यादा करीब होगा
internal tools जैसी कई services होती हैं जिन्हें performance tuning या गहरी internal observability की ज़रूरत नहीं होती
ऐसी जगहों पर serverless frameworks deployment में लगने वाला समय बहुत घटा देते हैं, इसलिए वे अच्छी तरह fit होते हैं
तेज़ होना अच्छा है, लेकिन वह शायद ही core requirement होता है
आम तौर पर core requirement यह होती है कि चीज़ जल्दी बन सके और maintenance कम हो
Cloudflare यहाँ अच्छा developer experience दे सकता है, लेकिन यह उनका मुख्य marketing point नहीं है, और ऐसी development को तेज़ बनाने का दावा करने वाली competing services भी बहुत हैं
इसके उलट, जहाँ high debuggability और internal observability चाहिए, वहाँ मुझे ऐसी services उतनी fit नहीं लगतीं
कौन-से metrics मिल सकते हैं, कुछ Durable Objects धीमे हों तो क्यों हैं यह समझने की जानकारी है या नहीं, उसे ठीक किया जा सकता है या नहीं, logging कैसी है और cost कितनी है—ये सब मायने रखते हैं
ऐसी services किसी startup के लिए पहले दिन से smart distributed system बनाकर scaling की चिंता टालने में अच्छी हो सकती हैं
लेकिन scale-up size की company शायद किसी ऐसी चीज़ पर जाना चाहेगी जिसमें अंदर तक ज़्यादा देखा जा सके, और वह transition मुश्किल होगा
जैसा दूसरों ने कहा, use case multiplayer है, क्योंकि app अच्छा महसूस हो इसके लिए सभी को changes जितनी जल्दी हो सके दिखने चाहिए
व्यापक रूप से देखें तो storage industry लंबे समय से ऐसी चीज़ बनाने की कोशिश कर रही है जो consistent हो, low-latency हो, और multi-user के लिए suited हो
physics के स्तर पर भी consistency और latency के बीच आम तौर पर trade-off होता है, इसलिए यह बहुत कठिन है
इसलिए कई models आज़माए जा रहे हैं, और उन experiments में से काफ़ी SQLite के इर्द-गिर्द हो रहे हैं। सभी नहीं; Yugabyte या Cockroach जैसे exceptions भी हैं
DO design देखकर बार-बार प्रभावित होना पड़ता है
इस तरीके को देखकर reflexive reaction आ सकता है कि इसमें कुछ गलत है, लेकिन मुझे लगता है कि असल में कई real products implicit रूप से इसी तरह बने होते हैं
हर atomic unit, जिसे transactional consistency चाहिए, उसके लिए बहुत low scale पर कई complex operations किए जाते हैं
पीछे मुड़कर देखें तो Framer में multiplayer support project के लिए हमने जो बनाया था, वह भी आज DO जो कर रहा है उसका एक अधिक applied version था
edits 60 FPS पर replicate होते थे और सभी clients पर सही order में apply होते थे, और आखिर में हमने JSON object editing के लिए WAL जैसी चीज़ भी बना ली थी
इसलिए अगर कोई project instance crash भी हो जाए, तो backup ऐसे takeover कर सकता था जैसे कुछ हुआ ही न हो
भले ही विशाल project data object में JSON patches commit करने का समय न मिला हो, फिर भी यहाँ बताए तरीके की तरह N updates या M seconds पर processing होती थी
यह मूर्खतापूर्ण सवाल हो सकता है, लेकिन ऐसी configuration में schema migration कैसे handle किया जाता है, यह जानना चाहता हूँ
मेरी समझ के मुताबिक लक्ष्य tenant-per-database, या उससे भी ज्यादा granular संरचना जैसा लगता है
जानना चाहता हूँ कि schema migration को समझदारी से handle करने का कोई तरीका है या फिर expectation यह है कि ऐसे databases ज्यादा short-lived होते हैं और delete होने तक कई DB/DO versions को support करना पड़ता है
दिमाग में user-per-DO वाला bookmark service बनाना मजेदार लगता है
लेकिन जैसे ही मौजूदा table में नया field जोड़ना चाहेंगे, वह change हर individual DO पर apply करना पड़ेगा—काफी tricky समस्या
शायद यह example बहुत long-lived data वाला है, और यह design अधिक temporary use cases के लिए हो सकता है
अगर किसी ने वास्तव में ऐसा किया है, तो वे इसे कैसे कर रहे हैं, जानना चाहूँगा
SQLite के लिए Python में लिखा हुआ एक version है, लेकिन पता नहीं यह Durable Objects में चल पाएगा या नहीं। WASM और PyOdide के जरिए संभव हो सकता है
वरना इसे JavaScript में port करना होगा
https://github.com/simonw/sqlite-migrate
आजकल लगता है Cloudflare developers को Worker की बजाय हर जगह DO इस्तेमाल करने के लिए push कर रहा है
Worker के WebSocket connections भी लगभग 30 सेकंड बाद timeout हो जाते हैं, और recommended तरीका DO इस्तेमाल करना है
original chat demo भी 2020 से DO और WebSocket का इस्तेमाल करता था: https://github.com/cloudflare/workers-chat-demo
क्या इसका मतलब है कि DO के लिए SQLite, DO failure की स्थिति में अधिकतम 10 सेकंड का data खो सकता है?
यहाँ लगता है Simon “beyond” नहीं बल्कि within कहना चाहते थे