- OpenRun आंतरिक टूल्स के लिए एक web app deployment platform है, जो static files, app code और configuration files को file system की जगह SQLite में स्टोर करता है और deployment state को database-केंद्रित तरीके से मैनेज करता है
- कई files साथ में बदलने वाले app updates को एक transaction में प्रोसेस करना इसका मुख्य उद्देश्य है, ताकि version switch के दौरान टूटी हुई web pages serve न हों
- compression से पहले के SHA256 hash को primary key के रूप में इस्तेमाल करके यह app versions के बीच, और staging·preview·production apps के बीच duplicate file storage को कम करता है
- SQLite storage approach rollback, backup, ETag के लिए hash storage, और Brotli compression storage को सरल बनाती है, और ज़रूरत होने पर GZip या uncompressed data को भी column जोड़कर साथ में संभाला जा सकता है
- अभी यह single node पर चलता है, और multi-node support आने पर shared Postgres और local SQLite file cache को साथ इस्तेमाल करके latency कम करने की योजना है
OpenRun की file storage पद्धति
- OpenRun code-first आंतरिक टूल्स के लिए एक open source deployment platform है, जो single node या Kubernetes cluster पर GitOps तरीके से web apps deploy करता है
- आम web servers की तरह static content को file system में रखने के बजाय, OpenRun static files, app code और configuration files जैसे app data को SQLite में स्टोर करता है
- क्योंकि app metadata dynamically generate होता है, इसलिए database storage स्वाभाविक है, और files को भी उसी storage layer में संभालने से deployment state को साथ में मैनेज करना आसान हो जाता है
- app creation और updates के समय files को GitHub या local disk से SQLite database में upload किया जाता है
- development mode में ही local file system का उपयोग होता है
SQLite चुनने का कारण
- transactional updates इसका सबसे बड़ा फायदा है
- कई file changes को एक transaction में बांधकर प्रोसेस किया जा सकता है
- isolation की वजह से update के दौरान टूटा हुआ web app serve नहीं होता
- deployment error होने पर database transaction unit के आधार पर rollback किया जा सकता है
- कई apps एक साथ update हो रहे हों तब भी उन्हें एक बार में वापस किया जा सकता है
- file system में बदली हुई files खोजकर साफ करने के तरीके से यह अधिक सरल है
- OpenRun सभी updates का अपने-आप version management करता है, और file data निम्न schema वाली table में स्टोर होता है
CREATE TABLE files (sha text, compression_type text, content blob, create_time datetime, PRIMARY KEY(sha));
- compression से पहले के content के SHA256 hash को primary key के रूप में इस्तेमाल करने से एक ही file content कई versions में सिर्फ एक बार स्टोर होता है
- हर production app का एक staging app होता है, और उसके कई preview apps हो सकते हैं, इसलिए file duplication हो सकती है
- SQLite-आधारित storage apps के बीच भी एक ही content वाली files को duplicate store होने से रोकती है
backup, caching और compression handling
- पूरे system state, metadata और files का backup SQLite backup tool Litestream आदि से लिया जा सकता है
- browser caching के लिए ETag header में जरूरी content SHA को file upload के समय एक बार स्टोर कर देने से बाद में उसे फिर से calculate करने की जरूरत नहीं होती
- file content को SQLite table में Brotli compressed form में स्टोर किया जाता है
- database approach में
files table में column जोड़कर GZip compressed data या uncompressed data भी स्टोर किया जा सकता है
performance और multi-node योजना
- OpenRun में SQLite database approach अच्छी performance देती है
- file system-आधारित equivalent implementation न होने की वजह से direct benchmark नहीं किया गया
- SQLite team के benchmark के अनुसार कुछ workloads में SQLite, सीधे file system उपयोग से बेहतर performance दे सकता है
- OpenRun अभी single node पर चलता है
- भविष्य में multi-node support जुड़ने पर metadata और file data storage के लिए local SQLite की जगह shared Postgres database उपयोग करने की योजना है
- इस approach में latency issue आ सकता है
- Postgres access latency से बचने के लिए local SQLite database को file cache के रूप में उपयोग करने की योजना है
file system approach ज्यादा आम क्यों है
- ज़्यादातर web servers के file system उपयोग करने का एक कारण सुविधा है
- rsync, tar जैसे मौजूदा file system tools से files को copy और update किया जा सकता है
- दूसरा कारण ऐतिहासिक पृष्ठभूमि है
- अच्छे in-process relational database आने से पहले से file systems का उपयोग होता रहा है
- database को file storage के रूप में इस्तेमाल करने के लिए file upload के लिए API interface चाहिए, और यह हमेशा व्यावहारिक नहीं होता
1 टिप्पणियां
Hacker News की राय
मैंने कुछ साल पहले इस विचार के साथ प्रयोग किया था, और इसका कुछ हिस्सा “35% Faster Than The Filesystem” लेख से प्रेरित था: https://www.sqlite.org/fasterthanfs.html
उस समय के नोट्स यहाँ हैं: https://simonwillison.net/2020/Jul/30/fun-binary-data-and-sq...
मैंने Datasette के लिए SQLite से static files serve करने वाला एक plugin बनाया था, https://datasette.io/plugins/datasette-media, और यह अच्छी तरह काम करता है, लेकिन सच कहूँ तो इसे बनाने के बाद से मैंने इसका बहुत ज्यादा इस्तेमाल नहीं किया
इससे जुड़ा एक और कॉन्सेप्ट SQLite से map tiles serve करना है, और https://datasette.io/plugins/datasette-tiles वही काम करता है। बाद में पता चला कि MBTiles format असल में PNG से भरा हुआ एक SQLite database था
अगर आप file serving के लिए SQLite के साथ प्रयोग करना चाहते हैं, तो database के शुरुआती setup के लिए “sqlite-utils insert-files” CLI tool काम का हो सकता है: https://sqlite-utils.datasette.io/en/stable/cli.html#inserti...
content hash सिर्फ file upload के समय एक बार बनाना पड़ता है, और web server के हर restart पर इसे दोबारा बनाने या असली filename बदलने वाला build step रखने की ज़रूरत नहीं होती। इसे filesystem files पर dynamic तरीके से भी लागू किया जा सकता है (https://github.com/benbjohnson/hashfs के embedFS implementation को देखें), लेकिन database इसे थोड़ा और आसान बना देता है
अगर मुझे सही याद है, तो requests-cache SQLite में requests को
(date, URI)के आधार पर cache करता है: https://github.com/requests-cache/requests-cache/blob/main/r...pyfilesystem SQLite search: https://www.google.com/search?q=pyfilesystem+sqlite
sendfile mmap SQLite search: https://www.google.com/search?q=sendfile+mmap+sqlite
https://github.com/adamobeng/wddbfs एक “webdavfs provider जो sqlite database की सामग्री पढ़ सकता है” है
शायद Unix file permissions और xattrs extended file attribute permissions को SQLite के ऊपर रखकर filesystem implement करने का भी कोई अच्छा तरीका हो सकता है
क्या SQLite, मसलन ngx_http_memcached_module.c से तेज़ या ज़्यादा सुविधाजनक होगा? यह भी जानना दिलचस्प होगा कि क्या SQLite में cell-level ACL भी है
static files पढ़ते समय हर request पर file को open, read, और close करना पड़ता है, इसलिए भले ही filesystem layer file content को cache कर ले, फिर भी context switch ज़्यादा होते हैं। अगर इसे तेज़ करना लक्ष्य है, तो सब कुछ database में बदलने के बजाय एक caching frontend लगाना ज़्यादा उचित है। वह SQLite से तेज़ होगा और maintenance व troubleshooting भी आसान होंगी
इसमें पूरी तरह user space में चलने वाले filesystems भी शामिल हैं। FUSE इसमें शामिल नहीं है, क्योंकि उसके calls kernel से होकर जाते हैं
“ट्रांज़ैक्शन अपडेट” को मुख्य फ़ायदा कहना सीमित है। सर्वर SQLite इस्तेमाल करे या file system, सिर्फ उससे अपडेट के दौरान टूटे हुए web app को रोका नहीं जा सकता
ब्राउज़र का हर page अलग HTTP request से लाई गई resources की एक tree होता है, इसलिए यह server-side transaction/atomic update system के दायरे में नहीं आता। सर्वर पर सभी resources को transaction में बदल देने पर भी ब्राउज़र पुराने और नए resources का मिला-जुला संयोजन देख सकता है
आम समाधान यह है कि page की सभी child resources (JavaScript bundle, stylesheet, media आदि) को content hash या version वाले नाम (URL) दिए जाएँ। अगर root HTML document version X लोड करता है, तो उसकी सभी child resources भी उसी के अनुरूप version X लोड करें
साथ ही X से Y में update करते समय, Y page serve करना शुरू करने के बाद भी कुछ समय तक X child resources उपलब्ध कराते रहना चाहिए। जब तक यह भरोसा न हो जाए कि कोई ब्राउज़र अभी भी X page लोड नहीं कर रहा है, उन्हें बनाए रखना होगा, वरना X page टूट सकता है
इसलिए अगर आप root HTML और child resources को एक ही atomically replaced bundle में रखना चाहते हैं, तो उल्टा समस्या हो सकती है। क्योंकि पुरानी child resources अभी भी refer हो सकती हैं, लेकिन आप उन्हें हटा देंगे
कुछ मामलों में media files जैसी कुछ child resources को HTML document से अलग version करना बेहतर हो सकता है। JavaScript chunks या stylesheets जैसे app-structure elements का cache पूरी तरह invalidate किए बिना update करने के लिए page build system में भी इसका ध्यान रखना पड़ सकता है
एक बड़ी company में इस पर experiment किया गया था (उस समय web के बड़े हिस्से को देखा गया था), और ज़्यादातर users (80% से अधिक) लगभग 2~3 दिन तक web app में बने रहे। संभव है कि weekend भर tab खुला छोड़ देने वाले users की वजह से bias आया हो
95% point लगभग 2 हफ्ते था, और 100% लगभग 600 दिन। यानी कोई user लगभग 2 साल तक tab खुला छोड़ कर बैठा था
अगर 100% को target करना है, तो काफ़ी लंबा इंतज़ार करना पड़ेगा। ये आँकड़े पूरी तरह याददाश्त पर आधारित हैं, और अब मैं उस company में काम नहीं करता
user के लंबे समय तक एक page पर रहने और फिर broken link पाने वाला scenario, SPA की समस्या के ज़्यादा करीब है
कुल मिलाकर सहमत हूँ, लेकिन transaction updates सिर्फ update-संबंधी समस्याओं की एक category को रोकते हैं। app level की दूसरी समस्याएँ भी broken experience पैदा कर सकती हैं
content hash से referenced static content के पुराने versions serve करते रहना संभव है, लेकिन Clace में यह अभी implement नहीं किया गया है
मुख्य तरकीब यह है कि HTML changes से पहले non-HTML changes upload किए जाएँ, ताकि किसी file को उसके मौजूद होने से पहले refer न किया जाए। अगर आप app को जितना संभव हो उतना जटिल बनाना चाहते हैं, तो uploads पर depth-first traversal लागू कर सकते हैं। लेकिन अगर मानसिक संतुलन ज़्यादा महत्वपूर्ण है, तो समस्या को कम करके app में asset-first upload चुनना बेहतर है
2011/2012 में जब मैं एक छोटी game development company में काम करता था, तब मैंने सुझाया कि 100KB से छोटे assets सब sqlite3 DB में ले जाएँ, और “pak files” बनाकर उन files के offsets sqlite3 DB में store करें
यह फ़ैसला Richard Hipp की एक postmortem talk से प्रभावित था, जिसमें उन्होंने पीछे मुड़कर कहा था कि बेहतर होता अगर BLOBs को inode की तरह माना जाता, उन्हें database के भीतर और पीछे के offsets पर रखा जाता, और BLOBs को file में append किया जाता
asset loading बहुत तेज़ हो गई थी। यह एक mobile game था, इसलिए DB में न होने वाले assets बहुत कम थे। बाद में लोगों को इस तरीके को और अपनाते देखना भी दिलचस्प है
एक और आसानी से छूट जाने वाला फ़ायदा यह है कि content के साथ लगभग असीमित metadata जोड़ा जा सकता है, जिससे “मिलती-जुलती” files को database query से ढूँढा जा सकता है
हमने DB में बहुत सारा metadata डाला था, और अंतिम pak file शायद 200MB की थी, जबकि database लगभग 20MB का था। फिर से कहूँ तो यह mobile game था
client side पर सबसे बुरा अनुभव एक double inner join था, जिसे server-side complexity की वजह से कम नहीं किया जा सका। यह निराशाजनक था कि हम server implementation नहीं कर पाए, क्योंकि जिस टीम के साथ काम कर रहे थे वह software development में बहुत कमज़ोर थी और backend spec को बिना बताए बदल देती थी, जिससे build अचानक टूट जाता था
game replay के लिए भी हमने अलग sqlite3 database इस्तेमाल किया, और match खत्म होने के बाद पूरा game replay करके देखा जा सकता था कि हर opponent ने क्या किया। यह automation testing के लिए भी बहुत अच्छा था
lix change control system में भी file system और git को संभालने की बजाय files को SQLite में रखने की दिशा में गए। यह लेख उन समस्याओं पर है जिनका हमने सामना किया: https://opral.substack.com/i/150054233/breaking-git-compatib...
file locking, concurrency जैसी समस्याएँ SQLite संभाल लेता है
SQLite इस्तेमाल करने पर platform-specific file system API की जगह SQL से files को query किया जा सकता है
SQL queries को Kysely https://kysely.dev/ के साथ ORM के बिना भी type-safe तरीके से लिखा जा सकता है
बस यह ध्यान रखना चाहिए कि SQLite database vacuum किए बिना छोटा नहीं होता। मूल रूप से यह data को अलग file में copy करके original को delete करने जैसा काम है
यह ऐसा काम है जिसे application के भीतर सही समय पर manually करना पड़ता है, इसलिए अगर binary data लिखने और मिटाने के अंदाज़ में इस्तेमाल हो रहा है तो disk usage पर ध्यान देना चाहिए
दिलचस्प बात यह है कि मेरा बनाया static site generator CMS यहाँ बताए गए तरीके के बिल्कुल उलट काम करता है
website को develop/update करते समय सभी pages और posts SQLite database की entries होते हैं, और उन्हें एक web interface से manage किया जाता है जो website का editable version दिखाता है
उसके बाद website को static pages के रूप में file system में dump किया जाता है, ताकि उसे सीधे deploy किया जा सके, या zip के रूप में download करके किसी और जगह upload किया जा सके, जिसमें पूरी तरह static hosting service भी शामिल है
SQLite के “Appropriate Uses For SQLite” https://www.sqlite.org/whentouse.html के अनुसार, SQLite कितना web traffic संभाल सकता है यह इस बात पर निर्भर करता है कि साइट database का कितना भारी उपयोग करती है
आम तौर पर, जिन साइटों पर दिन में 100K hits से कम आते हैं, वे SQLite पर अच्छी तरह चलनी चाहिए। 100K/दिन एक conservative estimate है, कोई सख्त upper limit नहीं। ऐसे उदाहरण भी हैं जहाँ SQLite ने इससे 10 गुना traffic संभाला है
SQLite वेबसाइट(https://www.sqlite.org/) भी स्वाभाविक रूप से SQLite का ही उपयोग करती है, और 2015 के अनुसार वह रोज़ लगभग 400K~500K HTTP requests संभालती थी, जिनमें से 15~20% dynamic pages थे जो database को छूते थे। dynamic content में प्रति webpage लगभग 200 SQL statements इस्तेमाल होते थे
यह setup एक single VM पर चलता था, जो physical server को 23 अन्य VM के साथ साझा करता था, और फिर भी अधिकांश समय load average 0.1 से नीचे रहता था। संदर्भ: https://news.ycombinator.com/item?id=33975635
static file serving जैसी read-heavy workload में SQLite इससे कहीं ज़्यादा संभाल सकता है। अगर content caching headers सेट हों, तो browser content को cache कर लेता है, इसलिए server requests सिर्फ नए clients के लिए ज़रूरी रह जाते हैं
ज़्यादातर use cases में SQLite bottleneck बनेगा, ऐसा नहीं लगता
सिर्फ 2017 के “35% Faster Than The Filesystem” पेज के आधार पर static content को SQLite से serve करने का विचार, अच्छा कहें तो भी, अधपका लगता है
Nginx जैसे modern web servers static files को संभालने के लिए optimized strategies इस्तेमाल करते हैं। यह sendfile से शुरू होकर io_uring और splice operations तक जाता है, और epoll, kqueue, eventport में से जरूरत के मुताबिक आधार पर बने अच्छे thread pool के भीतर काम करता है
इसके विपरीत, SQLite मूल रूप से सबसे अच्छा जो दे सकता है वह memory-mapped I/O support(https://www.sqlite.org/mmap.html) जैसा कुछ है
यह तरीका local-hosted webapp जैसी single-client services के लिए ठीक बैठ सकता है(https://github.com/electron/asar भी देखें)। लेकिन बड़े websites में, जैसा दूसरे comments में कहा गया, यह ऐसी समस्या हल करने जैसा है जो है ही नहीं
मैं high-performance scientific computing बहुत करता हूँ, और खासकर जब parallel में data access करना होता है, तब अक्सर सबसे flexible और fastest तरीका RAM disk पर read-only SQLite database निकलता है
यह काफ़ी hacky लगता है, लेकिन अब तक जो भी तरीके मिले हैं उनमें यह सबसे आसान और तेज़ setup है
astronomy में एक दोस्त से देखा था कि उसका मानना है कि scientific fields के बहुत से लोगों को databases की आदत डालनी चाहिए। नहीं तो वे अंत में बहुत बड़ी मेहनत लगाकर, बिना समझे, एक बेहद खराब खुद का database बना लेते हैं
यह approach ज़्यादा आम क्यों नहीं है, इसका कारण यह है कि file systems फ़ाइलों को संभालने में बहुत अच्छे होते हैं
अगर atomic updates चाहिए हों, तो नई directory में checkout करके symbolic link बदल दीजिए
मैंने database को file system की तरह इस्तेमाल करने के कई versions देखे हैं; उनमें अच्छी बातें भी हैं, लेकिन जब चीज़ें बिगड़ती हैं तो वे दुःस्वप्न जैसे हो जाते हैं
तब आप btrfs जैसी चीज़ का इस्तेमाल करके file system layer पर deduplication भी कर सकते हैं
app update के समय बहुत-सी files बदल सकती हैं, इसलिए database का उपयोग करने से सभी changes को transaction के रूप में atomically process किया जा सकता है और version change के दौरान टूटी हुई webpages serve होने से बचाया जा सकता है — इस दावे में समस्या है
कारण यह है कि SQLite file serializable isolation हासिल करने के लिए writes के दौरान reads को lock कर देती है। इससे निष्कर्ष यह निकलता है कि database का काम offline file पर करना और फिर production में पुरानी files को नई files से replace करना बेहतर है
अंततः इसका मतलब लगभग यही है कि tar file का उपयोग करें, या नई content से replace करने के लिए अलग directory रखें
static files को static तरीके से serve करना कहीं आसान है। उन्हें ऐसे program से serve करने की ज़रूरत नहीं जो live SQLite connections संभाले और कोई अजीब “update concurrency” जादू हासिल करने की कोशिश करे। यह समस्या बिल्कुल कठिन नहीं है
CMS को SQLite database में manage करना ठीक है, लेकिन अगर content static है और उसे live serve करना है, तो static files का उपयोग करना बेहतर है