
SQLite VFS S3 से sub-100ms कोल्ड JOIN क्वेरीज़ के साथ + पेज-स्तरीय संपीड़न और एन्क्रिप्शन
turbolite एक Rust में SQLite VFS है जो सीधे S3 से sub-250ms कोल्ड लेटेंसी के साथ पॉइंट लुकअप और जॉइन प्रदान करता है।
यह रिपॉजिटरी दो क्रेट्स के साथ एक कार्गो वर्कस्पेस है:
turbolite — शुद्ध Rust लाइब्रेरी। पेज-स्तरीय संपीड़न, एन्क्रिप्शन और S3 टियरिंग के साथ SQLite VFS।turbolite-ffi — C FFI / लोडेबल एक्सटेंशन + भाषा बाइंडिंग (Python, Node.js, Go)।यह पृष्ठ-स्तरीय संपीड़न (zstd) और एन्क्रिप्शन (AES-256) भी प्रदान करता है ताकि आराम के समय दक्षता और सुरक्षा मिल सके, जिसका उपयोग S3 से अलग किया जा सकता है।
प्रायोगिक. turbolite सक्रिय विकास के तहत है और इसमें बग हैं। सावधान रहें।
ऑब्जेक्ट स्टोरेज तेज़ हो रहा है। S3 Express One Zone एकल-अंकीय मिलीसेकंड GET प्रदान करता है और Tigris भी अत्यंत तेज़ है. स्थानीय डिस्क और क्लाउड स्टोरेज के बीच का अंतर कम हो रहा है, और turbolite इसका लाभ उठाता है।
डिज़ाइन और नाम turbopuffer के दृष्टिकोण से प्रेरित हैं जो क्लाउड स्टोरेज की बाधाओं के आसपास निर्दयतापूर्वक आर्किटेक्ट करता है। परियोजना का प्रारंभिक लक्ष्य Neon के 500ms+ कोल्ड स्टार्ट को हराना था। लक्ष्य प्राप्त हुआ।
यदि आपके पास प्रति सर्वर एक डेटाबेस है, तो एक वॉल्यूम का उपयोग करें। turbolite यह पता लगाता है कि सैकड़ों या हजारों डेटाबेस (प्रति किरायेदार एक, प्रति कार्यक्षेत्र एक, प्रति उपकरण एक) कैसे रखें, प्रत्येक के लिए एक वॉल्यूम नहीं चाहते, और आप एकल लेखन स्रोत के साथ सहज हैं।
turbolite एक Rust लाइब्रेरी, SQLite लोडेबल एक्सटेंशन (.so/.dylib), और Python और Node.js के लिए भाषा पैकेज, साथ ही Go के लिए Github deps के रूप में उपलब्ध है। कोई भी S3-संगत स्टोरेज काम करता है (AWS S3, Tigris, R2, MinIO, आदि)। यह पृष्ठ स्तर पर काम करने वाला एक मानक SQLite VFS है, इसलिए अधिकांश SQLite सुविधाएँ काम करनी चाहिए: FTS, R-tree, JSON, WAL मोड, आदि।
turbolite व्यापक hadb पारिस्थितिकी तंत्र का हिस्सा है। स्टैंडअलोन turbolite एक सुरक्षित लेखक के साथ एक स्टोरेज VFS है; यदि आप HA लीडर चुनाव और निरंतर WAL प्रतिकृति चाहते हैं, तो इसे haqlite-turbolite के माध्यम से उपयोग करें, जो शीर्ष पर HaQLite और walrust को जोड़ता है। वह HA पथ अभी भी बहुत प्रयोगात्मक है।
यदि आप turbolite में योगदान देना चाहते हैं या बग ढूंढना चाहते हैं, तो कृपया एक पुल अनुरोध बनाएं या एक मुद्दा खोलें।
1M पोस्ट / 100K उपयोगकर्ता (~1.5GB संग्रहीत) बिना किसी कैश के, प्रत्येक बाइट S3 से। EC2 c5.2xlarge + S3 Express One Zone (समान AZ, ~4ms GET लेटेंसी)। Fly performance-8x + Tigris (~25ms GET लेटेंसी)। दोनों: 8 समर्पित vCPU, 16GB RAM, 7 प्रीफ़ेच वर्कर थ्रेड। देखें बेंचमार्किंग और स्टोरेज बैकएंड मायने रखता है।
बेंचमार्क कैश स्तर द्वारा व्यवस्थित किए गए हैं (जब क्वेरी चलती है तो स्थानीय डिस्क पर पहले से क्या है):
आंतरिक सबसे यथार्थवादी कोल्ड बेंचमार्क है: आंतरिक पेज कनेक्शन खुलने पर उत्सुकता से लोड होते हैं, इसलिए जब आप पहली क्वेरी चलाते हैं, तब तक वे कैश हो चुके होते हैं। इंडेक्स पेज पहले एक्सेस पर पृष्ठभूमि में आक्रामक रूप से प्रीफ़ेच करते हैं और अभी तैयार नहीं हो सकते हैं।
100K पंक्तियाँ, Fly.io performance-2x (समर्पित vCPU, NVMe, IAD):
पॉइंट लुकअप में प्रति-पेज सबसे अधिक ओवरहेड (~2x) होता है। बाकी सब कुछ समानता के करीब पहुंचता है या उसे पीछे छोड़ देता है। लॉक-फ्री कैश आर्किटेक्चर का मतलब है कि समवर्ती रीड कभी भी राइट को ब्लॉक नहीं करते।
| बाद | स्थानीय | S3 (समान-क्षेत्र RustFS) |
|---|---|---|
| 1K इंसर्ट | 19ms | 38ms |
| 10K बैच | 17ms | 114ms |
| 1K अपडेट | 9ms | 36ms |
राइट हमेशा स्थानीय-गति पर होते हैं। S3 की लागत केवल चेकपॉइंट पर होती है। समान Fly क्षेत्र में RustFS के साथ संख्याएँ (~2ms RTT)। S3 Express One Zone तुलनीय होगा।
pip install turbolite
custom_encryption.key
custom_decryptor.py```python
import turbolite
conn = turbolite.connect("my.db", mode="s3",
bucket="my-bucket",
endpoint="https://t3.storage.dev")
conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
conn.execute("INSERT INTO users VALUES (1, 'alice', '[email protected]')")
conn.commit()
alice = conn.cursor().execute("SELECT * FROM users").fetchone()
print(alice[1])
>>> "alice"
देखें Installation Node, Go, Rust, स्थानीय-मात्र मोड, और .so लोड करने योग्य एक्सटेंशन का सीधे उपयोग करने के लिए।
turbolite S3 की बाधाओं को फाइलसिस्टम बाधाओं पर प्राथमिकता देने के लिए डिज़ाइन किया गया है। हर निर्णय इस मॉडल से प्रवाहित होता है:
turbolite SQLite और S3 के बीच इंट्रोस्पेक्शन और इंडायरेक्शन परतें जोड़ता है जो कुशलतापूर्वक पेजों को समूहित, संपीड़ित, ट्रैक और लाता है।
SQLite एक B-tree इंडेक्स का उपयोग करता है और एक समय में एक पेज का अनुरोध करता है। यह जानता है कि पेज N बाइट ऑफ़सेट N * page_size पर है। और वे पेज कुशल रैंडम एक्सेस के लिए पेजमैप में बेतरतीब ढंग से वितरित होते हैं। लेकिन S3 पर, प्रति अनुरोध एक पेज लाने का मतलब होगा प्रति क्वेरी हजारों संभावित रैंडम GETs।
लेकिन पेज समान रूप से नहीं बनाए जाते। SQLite में विभिन्न प्रकार के पेज होते हैं। turbolite पेज समूहों को प्रकार द्वारा अलग करता है: आंतरिक B-tree, इंडेक्स लीफ, और डेटा लीफ पेज।
आंतरिक पेज हर क्वेरी पर लीफ पेजों तक लुकअप रूट करने के लिए छूए जाते हैं। turbolite उन्हें पहचानता है, उन्हें S3 में संपीड़ित बंडलों में संग्रहीत करता है, और VFS खुलने पर उन्हें उत्सुकता से लोड करता है। उसके बाद, हर B-tree ट्रैवर्सल कैश हिट होता है।
इंडेक्स लीफ पेजों को भी वही उपचार मिलता है: अलग बंडल, आलसी पृष्ठभूमि प्रीफ़ेच, निष्कासन के खिलाफ पिन किए गए। कोल्ड क्वेरी को केवल डेटा पेज लाने की आवश्यकता होती है।
turbolite B-tree इंट्रोस्पेक्शन का लाभ उठाता है ताकि यह समझ सके कि कोई पेज किस ट्री (एक टेबल या इंडेक्स) का हिस्सा है, और उन पेजों को बुद्धिमानी से S3 में पेज समूहों के रूप में संग्रहीत करता है: एक ही S3 ऑब्जेक्ट में कई पेज चंक किए जाते हैं। प्रीफ़ेच पर बैंडविड्थ को संतृप्त करने के लिए पर्याप्त बड़ा, पॉइंट क्वेरी के लिए पर्याप्त छोटा। डिफ़ॉल्ट: प्रति समूह 256 पेज, 64KB पेजों पर ~16MB।
एक ही टेबल/इंडेक्स को एक साथ संग्रहीत करने का मतलब है कि हम कोल्ड क्वेरी के लिए सबसे कम संभव GETs करते हैं।
turbolite एक मेनिफेस्ट फ़ाइल के साथ पेज लुकअप को इंडायरेक्ट करता है जो सत्य का स्रोत है कि प्रत्येक पेज कहाँ रहता है। यह SQLite के निहित offset = page * size को स्पष्ट पॉइंटर्स से बदल देता है। पुराने पेज समूह संस्करण कभी अधिलेखित नहीं होते; मेनिफेस्ट PUT परमाणु कमिट पॉइंट है। पुराने संस्करण कचरा बन जाते हैं, gc() द्वारा साफ किए जाते हैं।
SQLite डिफ़ॉल्ट रूप से फाइलसिस्टम डिस्क पेज साइज से मेल खाने के लिए 4KB पेज का उपयोग करता है। S3 पर, डिस्क पेज साइज अप्रासंगिक है। मायने यह रखता है कि अनुरोध संख्या को कम करना और B-tree फैन-आउट को अधिकतम करना। उत्तर है बड़े पेज: turbolite डिफ़ॉल्ट रूप से 64KB पेज का उपयोग करता है। कम पेज = लीफ तक पहुंचने के लिए कम S3 राउंड ट्रिप्स।
पॉइंट क्वेरी को तेज़ बनाने के लिए, turbolite सीक करने योग्य संपीड़न का उपयोग करता है: प्रत्येक पेज समूह को कई zstd फ्रेम (~4 पेज प्रति फ्रेम) के रूप में एन्कोड किया जाता है। मेनिफेस्ट प्रति फ्रेम बाइट ऑफ़सेट संग्रहीत करता है, इसलिए कैश मिस पर S3 रेंज GET के माध्यम से आवश्यक पेज के साथ केवल ~256KB सब-चंक लाया जाता है, पूरे समूह को नहीं।
प्रीफ़ेचिंग की दो परतें हैं: सक्रिय (क्वेरी-प्लान फ्रंटरनिंग) और प्रतिक्रियात्मक (अनुकूली मिस-आधारित)।
क्वेरी-प्लान फ्रंटरनिंग पहले चलता है। क्वेरी निष्पादित होने से पहले, turbolite EXPLAIN QUERY PLAN के माध्यम से SQLite क्वेरी प्लान को इंटरसेप्ट करता है, सटीक टेबल और इंडेक्स निकालता है जिन्हें क्वेरी छूएगी, और पहला पेज पढ़े जाने से पहले ही उनके सभी पेज समूहों को प्रीफ़ेच पूल में सबमिट करता है। एक पाँच-टेबल जॉइन जो अन्यथा पाँच अनुक्रमिक मिस-देन-फ़ेच चक्रों को ट्रिगर करेगा, इसके बजाय क्वेरी शुरुआत में सभी पाँच फ़ेच को समानांतर में फायर करता है। SCAN क्वेरी के लिए, इसका मतलब है कि पूरी टेबल अग्रिम रूप से प्रीफ़ेच की जाती है।
चेतावनी: SQLite प्रति कनेक्शन एक ट्रेस कॉलबैक का समर्थन करता है। यदि कोई अन्य एक्सटेंशन पहले स्लॉट का दावा करता है, तो फ्रंटरनिंग चुपचाप प्रतिक्रियात्मक प्रीफ़ेच पर वापस आ जाता है।
प्रतिक्रियात्मक प्रीफ़ेचिंग फ्रंटरनिंग के चूक को संभालता है और फ़ॉलबैक के रूप में कार्य करता है। कैश मिस पर, दो चीजें समवर्ती रूप से होती हैं:
मिस काउंटर प्रति B-tree, वैश्विक रूप से नहीं ट्रैक किए जाते हैं। एक प्रोफ़ाइल क्वेरी जो users (मिस 1) को हिट करती है फिर posts (मिस 1) को, प्रत्येक ट्री को 1 पर सही ढंग से ट्रैक करती है, 2 पर नहीं। यह एक मल्टी-टेबल जॉइन को गलती से प्रत्येक ट्री पर प्रीफ़ेच को बढ़ाने से रोकता है सिर्फ इसलिए कि वह कई को छूता है।
प्रत्येक लगातार मिस एक प्रीफ़ेच शेड्यूल के माध्यम से आगे बढ़ता है जो नियंत्रित करता है कि एक ही ट्री के समूहों का कितना अंश प्रीफ़ेच करना है। turbolite क्वेरी प्लान के आधार पर स्वचालित रूप से एक शेड्यूल चुनता है:
[0.3, 0.3, 0.4]: SEARCH ... USING INDEX क्वेरी के लिए जो इंडेक्स के अज्ञात भागों को स्कैन करती हैं। पहली मिस से आक्रामक क्योंकि हम नहीं जानते कि इंडेक्स का कितना हिस्सा स्कैन किया जाएगा।[0.0, 0.0, 0.0]: पॉइंट क्वेरी और इंडेक्स लुकअप के लिए जो प्रति ट्री 1-2 पेज हिट करते हैं। किसी भी प्रीफ़ेच से पहले तीन मुफ्त हॉप। शून्य-भारी शेड्यूल S3 Express और Tigris दोनों पर अर्ली-रैम्प से बेहतर प्रदर्शन करते हैं।आप ओपन टाइम पर TurboliteConfig पर prefetch.search / prefetch.lookup सेट करके प्रीफ़ेच शेड्यूल को ट्यून कर सकते हैं - आप अपेक्षित वर्कलोड आकार जानते हैं, इसलिए VFS को अनुमान लगाने की आवश्यकता नहीं है। Configuring prefetch देखें।
दोनों शेड्यूल B-tree इंट्रोस्पेक्शन का लाभ उठाते हैं: प्रत्येक प्रीफ़ेच किया गया समूह सही ट्री से पेज रखने की गारंटी है। एक उदाहरण: यदि SQLite users टेबल से एक पेज का अनुरोध करता है, फिर उसी टेबल से दूसरे का अनुरोध करता है, तो turbolite मान लेता है कि एक स्कैन आ रहा है और बाकी users टेबल को पृष्ठभूमि में प्रीफ़ेच करता है, और कुछ नहीं। B-tree इंट्रोस्पेक्शन के बिना, यह गलती से आधा users टेबल और आधा posts टेबल ले आएगा सिर्फ इसलिए कि डेटा डिस्क पर एक-दूसरे के बगल में रहता है।
इंडेक्स-लीफ लुकअहेड इंडेक्स किए गए SEARCH के लिए भी ऐसा ही करता है। एक इंडेक्स लीफ पहले से ही उन टेबल रोइड्स को सूचीबद्ध करता है जिनका SQLite अनुरोध करने वाला है — इसलिए turbolite उन्हें कैश किए गए आंतरिक पेजों के माध्यम से हल करता है और उन टेबल फ्रेमों को एक-एक करके नहीं बल्कि एक बैच में प्रीफ़ेच करता है, अनुरोध संख्या में कटौती करता है।
turbolite का अपना इन-मेमोरी पेज कैश है जो SQLite के बिल्ट-इन पेज कैश को बदलता है। SQLite का पेजर आंतरिक रूप से पेजों को कैश करता है और कैश किए गए पेजों के लिए VFS से कभी पुनः नहीं पढ़ता। यह एकल-लेखक डेटाबेस के लिए ठीक है, लेकिन रीड रेप्लिका (HA फॉलोअर्स, मेनिफेस्ट-पोलिंग रीडर्स) के लिए, SQLite का कैश तब स्टेल हो जाता है जब अंतर्निहित डेटा प्रतिकृति के माध्यम से बदलता है।
turbolite का कैश मेनिफेस्ट-जागरूक है: जब set_manifest() फायर होता है (प्रतिकृति से नया डेटा), यह डिस्क कैश और इन-मेमोरी कैश दोनों में प्रभावित पेजों को अमान्य करता है। लेखन भी इन-मेमोरी कैश में अपने पेजों को अमान्य करते हैं। यह प्रतिकृति या लेखन के बाद ताजा रीड की गारंटी देता है।
आर्किटेक्चर:``` SQLite (PRAGMA cache_size=0) -> turbolite VFS xRead -> in-memory page cache (64MB default, AtomicPtr, zero-lock reads) -> disk cache (NVMe pread) -> S3 (on miss)
**कॉन्फ़िगरेशन:**
- `cache.mem_budget` `TurboliteConfig` पर (बाइट्स में)। डिफ़ॉल्ट: 64MB।
- `TURBOLITE_MEM_CACHE_BUDGET` env var (जैसे, `128MB`, `1GB`)।
- मेमोरी कैश को पूरी तरह से अक्षम करने के लिए `0` पर सेट करें।
`turbolite.connect()` (Python/Go/TypeScript) स्वचालित रूप से SQLite के पेज कैश को अक्षम करता है और उसके स्थान पर turbolite का उपयोग करता है। Rust उपभोक्ता जो सीधे `Connection::open_with_flags_and_vfs` का उपयोग करते हैं, उन्हें समान व्यवहार पाने के लिए `PRAGMA cache_size=0` सेट करना चाहिए।
### एन्क्रिप्शन और कम्प्रेशन
#### कम्प्रेशन
सभी डेटा स्टोरेज से पहले zstd-संपीड़ित किया जाता है। पेज समूह सीकेबल मल्टी-फ्रेम एन्कोडिंग का उपयोग करते हैं जो प्रत्येक फ्रेम (~4 पेज, ~256KB) को स्वतंत्र रूप से संपीड़ित करता है, ताकि एक पॉइंट लुकअप पूरे पेज समूह के बजाय केवल प्रासंगिक फ्रेम को डीकंप्रेस करे। कस्टम zstd शब्दकोश संपीड़न अनुपात को और बेहतर बना सकते हैं।
स्थानीय (गैर-S3) मोड भी zstd के साथ पेज स्तर पर संपीड़ित करता है। शब्दकोश प्रशिक्षण उपकरणों के लिए CLI देखें।
#### एन्क्रिप्शन
यदि एन्क्रिप्शन सक्षम है, turbolite सब कुछ एन्क्रिप्ट करता है: S3 ऑब्जेक्ट, स्थानीय कैश, WAL, मेटाडेटा। S3 डेटा प्रति फ्रेम यादृच्छिक नॉन्स (प्रमाणित, छेड़छाड़-पता लगाने वाला) के साथ AES-256-GCM का उपयोग करता है। स्थानीय डेटा शून्य आकार ओवरहेड के साथ AES-256-CTR का उपयोग करता है। एन्क्रिप्शन संपीड़न के बाद होता है: `plaintext → zstd → encrypt → S3`.
**कुंजी रोटेशन:** `rotate_encryption_key(config, new_key)` बिना डीकंप्रेस किए सभी S3 डेटा पर एन्क्रिप्शन को पुनः एन्क्रिप्ट, जोड़ या हटाता है। `Some` से `Some` कुंजियाँ घुमाता है, `Some` से `None` एन्क्रिप्शन हटाता है, `None` से `Some` इसे जोड़ता है। क्रैश-सुरक्षित: पुरानी वस्तुओं को कभी अधिलेखित नहीं किया जाता है, मैनिफेस्ट अपलोड परमाणु प्रतिबद्ध बिंदु है, और एक सत्यापन चरण प्रतिबद्ध करने से पहले नए डेटा की पठनीयता की पुष्टि करता है। आंशिक रन से अनाथों को `gc()` द्वारा साफ किया जाता है।
## ताकत और सीमाएँ
### turbolite कहाँ तेज़ है
**पॉइंट लुकअप सबसे अच्छे हैं।** कैश स्तर `index` पर, एक पॉइंट लुकअप S3 रेंज GET (~100KB प्रत्येक) के माध्यम से 1-2 उप-खंड लाता है। आंतरिक और इंडेक्स पेज पहले से कैश्ड हैं। कैश स्तर `none` पर, आंतरिक पुनर्प्राप्ति + पहले डेटा पेज के लिए ~120ms जोड़ें। यह किसी भी मशीन आकार पर काम करता है।
**पर्याप्त कोर के साथ स्कैन।** प्रीफ़ेच पूल S3 बैंडविड्थ को प्रति-ट्री अनुकूली शेड्यूलिंग के साथ संतृप्त करता है। खोज क्वेरी पहली चूक से प्रीफ़ेच को आक्रामक रूप से बढ़ाती है; योजना-जागरूक SCAN क्वेरी पूरी तालिका को अग्रिम रूप से बल्क-प्रीफ़ेच करती है। पर्याप्त थ्रेड 2-3 प्रीफ़ेच बैचों में मल्टी-GB डेटाबेस को सेकंड में सिंक कर सकते हैं।
### turbolite कहाँ धीमा है
**छोटी मशीनों पर स्कैन।** 1 प्रीफ़ेच थ्रेड के साथ, 1.46GB पर एक स्कैन में सेकंड लगते हैं, मिलीसेकंड नहीं। बाधा S3 राउंड ट्रिप है: प्रत्येक चरण समूहों को क्रमिक रूप से लाता है। यदि आपकी पहली क्वेरी 1-vCPU मशीन पर पूर्ण स्कैन है, तो स्टार्टअप दर्दनाक हो सकता है।
**खराब थ्रेड ट्यूनिंग।** बहुत कम प्रीफ़ेच थ्रेड और स्कैन S3 की प्रतीक्षा में रुक जाते हैं। बहुत अधिक और अग्रभूमि SQLite कार्य डाउनलोड के साथ प्रतिस्पर्धा करने लगता है। डिफ़ॉल्ट (`max(num_cpus - 1, 1)`) अग्रभूमि कार्य के लिए एक कोर छोड़ता है, लेकिन बड़े डेटाबेस पर स्कैन-भारी वर्कलोड को अभी भी पर्याप्त CPU की आवश्यकता होती है।
**पहली क्वेरी दंड।** कैश स्तर `none` पर पहली क्वेरी आंतरिक पेज लोडिंग और कम से कम एक डेटा फ़ेच के लिए ~50-200ms का भुगतान करती है। यदि क्वेरी को बैकग्राउंड प्रीफ़ेच समाप्त होने से पहले एक इंडेक्स पेज की आवश्यकता होती है, तो यह इनलाइन रेंज GET पर वापस आ जाती है।
### वर्तमान सीमाएँ
- **स्टैंडअलोन turbolite एकल-लेखक है।** एक ही प्रीफ़िक्स पर सीधे लिखने वाली दो मशीनें मैनिफेस्ट को दूषित कर देंगी।
- **HA/फेलओवर मोड प्रायोगिक है और `haqlite-turbolite` में रहता है।** यह स्टैक HaQLite लीज़, turbolite पेज टियरिंग और walrust निरंतर WAL प्रतिकृति को जोड़ता है। यह एक turbolite प्रीफ़िक्स पर सीधे मल्टी-राइटर एक्सेस के लिए नहीं, बल्कि मल्टी-नोड परिनियोजन के लिए इच्छित पथ है।
- **WAL शिपिंग प्रायोगिक है।** `wal` फीचर फ़्लैग + walrust की आवश्यकता है। [स्थायित्व](#durability) देखें।
SQLite सुविधाएँ जो **काम** करती हैं: FTS, R-tree, JSON, WAL मोड, DELETE जर्नल मोड, VACUUM, ऑटोवैक्यूम।
## ट्यूनिंग
### सामान्य पैरामीटर
| पैरामीटर | यह क्या नियंत्रित करता है | डिफ़ॉल्ट |
|-----------|-----------------|---------|
| `prefetch.threads` | समानांतर S3 फ़ेच के लिए वर्कर थ्रेड | max(num_cpus - 1, 1) |
| `cache.pages_per_group` | प्रति S3 ऑब्जेक्ट पेज, बड़ा = कम PUTs, प्रति फ़ेच अधिक बाइट्स | 256 |
| `cache.gc_enabled` | चेकपॉइंट के बाद पुराने पेज समूह संस्करण हटाएं | true |
| `sync_mode` | चेकपॉइंट स्थायित्व: `Durable` (चेकपॉइंट में S3 अपलोड) या `LocalThenFlush` (अपलोड स्थगित करें) | Durable |
### प्रीफ़ेच शेड्यूल
क्वेरी-प्लान फ्रंटरनिंग (वास्तुकला देखें) मुख्य प्रीफ़ेच तंत्र है। नीचे दिए गए प्रतिक्रियाशील शेड्यूल तब फ़ॉलबैक के रूप में कार्य करते हैं जब फ्रंटरनिंग उपलब्ध नहीं है या जब क्वेरी ऐसे पेजों तक पहुँचती है जो योजना में नहीं थे।
| रणनीति | कब | डिफ़ॉल्ट शेड्यूल | क्या होता है |
|----------|------|-----------------|--------------|
| **SCAN** (फ्रंटरन) | EQP कहता है `SCAN table` | सभी समूह अग्रिम रूप से | पहले पढ़ने से पहले पूरी तालिका का बल्क प्रीफ़ेच। कोई हॉप शेड्यूल आवश्यक नहीं। |
| **SEARCH** (प्रतिक्रियाशील) | EQP कहता है `SEARCH ... USING INDEX` | `[0.3, 0.3, 0.4]` | पहली चूक से आक्रामक प्रीफ़ेच; अज्ञात इंडेक्स भागों को स्कैन करता है। |
| **Lookup** (प्रतिक्रियाशील) | पॉइंट क्वेरीज़, कोई EQP जानकारी नहीं | `[0.0, 0.0, 0.0]` | तीन मुफ्त हॉप्स, शून्य प्रीफ़ेच। पॉइंट क्वेरीज़ को प्रीफ़ेच से शायद ही लाभ होता है। |
प्रत्येक तत्व लगातार प्रति-ट्री कैश चूक पर Nth चूक पर प्रीफ़ेच करने के लिए सिबलिंग समूहों का अंश है। जब चूक सरणी की लंबाई से अधिक हो जाती है, तब अंश=1.0 (सभी शेष)।
**दो प्रतिक्रियाशील शेड्यूल क्यों?** SEARCH क्वेरी इंडेक्स/टेबल के अज्ञात भागों को स्कैन करती हैं और उन्हें आक्रामक वार्मअप की आवश्यकता होती है। लुकअप प्रति ट्री 1-2 पेज हिट करते हैं और शायद ही प्रीफ़ेच की आवश्यकता होती है। प्रति-ट्री चूक काउंटर स्वतंत्र ट्रैकिंग सुनिश्चित करते हैं: एक प्रोफ़ाइल क्वेरी जो उपयोगकर्ताओं (चूक 1) और फिर पोस्ट (चूक 1) को हिट करती है, प्रत्येक ट्री को अलग-अलग ट्रैक करती है।
### प्रीफ़ेच कॉन्फ़िगर करना
VFS निर्माण पर `TurboliteConfig` पर `prefetch.search` और `prefetch.lookup` सेट करें:
[ध्यान दें: कोड ब्लॉक का अंतिम भाग अधूरा दिख रहा है, लेकिन इसे वैसे ही रखा गया है जैसा इनपुट में है।]```rust
use turbolite::tiered::{TurboliteConfig, PrefetchConfig};
let config = TurboliteConfig {
prefetch: PrefetchConfig {
search: vec![0.4, 0.3, 0.3],
lookup: vec![0.0, 0.0, 0.2],
query_plan: true,
..Default::default()
},
..Default::default()
};
प्रति-क्वेरी पुनर्ट्यूनिंग के लिए कनेक्शन को पुनः खोले बिना, turbolite_config_set SQL फ़ंक्शन (Phase Cirrus c) का उपयोग करें। प्रत्येक पुश कॉलिंग कनेक्शन के हैंडल तक सीमित होता है और जब तक आप इसे पुनः नहीं बदलते तब तक प्रभावी रहता है:```sql
SELECT turbolite_config_set('prefetch_search', '0.5,0.5,0.0');
SELECT turbolite_config_set('prefetch_lookup', '0.0,0.0,0.0');
SELECT * FROM posts WHERE created_at > ?; -- runs with the new schedule
### इंडेक्स-लीफ लुकअहेड
जब कोई क्वेरी तालिका की पंक्तियों (`SEARCH ... USING INDEX`) को खोजने के लिए इंडेक्स का उपयोग करती है, तो SQLite द्वारा पढ़ा गया इंडेक्स लीफ उन तालिका रोआइडी (rowids) को पहले ही नामित कर देता है जिन्हें वह प्राप्त करने वाला है। लुकअहेड उन रोआइडी को पार्स करता है, उन्हें कैश की गई आंतरिक पृष्ठों के माध्यम से उनके टेबल-लीफ फ्रेम में हल करता है, और फ्रेम को एक बैच में प्रीफ़ेच करता है — ताकि तालिका की पंक्तियाँ एक-एक करके S3 राउंड ट्रिप के बजाय एक साथ आएँ।
यह **डिफ़ॉल्ट रूप से चालू** होता है और केवल अनुक्रमित `SEARCH` के लिए संलग्न होता है जो किसी तालिका में जाता है। स्कैन, पॉइंट-बाय-रोआइडी रीड और पूरी तरह से गर्म क्वेरीज़ सामान्य पथ पर अप्रभावित रहती हैं, इसलिए इसे बंद करने का कोई कारण शायद ही हो। इसे क्वेरी-प्लान प्रीफ़ेच की आवश्यकता है (`plan_aware`, डिफ़ॉल्ट true)।
एक मामला जहाँ इसे अक्षम करना उचित है, वह पूरी तरह से गर्म, CPU-संवेदनशील वर्कलोड है, जहाँ प्रत्येक इंडेक्स लीफ को पार्स करने में थोड़ा खर्च होता है और कुछ भी प्रीफ़ेच नहीं होता क्योंकि पृष्ठ पहले से कैश्ड हैं:```sql
SELECT turbolite_config_set('lookahead', 'false');
या खोलने के समय TurboliteConfig पर lookahead / TURBOLITE_LOOKAHEAD env var सेट करें।
Rust कॉलर turbolite::tiered::settings::set के माध्यम से उसी पथ को लागू कर सकते हैं।
नोट: प्रीफ़ेच प्रति-कनेक्शन है। प्रत्येक नया कनेक्शन ठंडे प्रति-ट्री मिस काउंटर के साथ शुरू होता है। कैश साझा किया जाता है, इसलिए दूसरा कनेक्शन पहले द्वारा कैश किए गए पृष्ठों से लाभान्वित होता है।
इष्टतम प्रीफ़ेच शेड्यूल आपके S3 बैकएंड के विलंबता-बैंडविड्थ ट्रेडऑफ़ पर निर्भर करते हैं। हमने S3 Express (~4ms GET) और Tigris (~25ms GET) दोनों पर 6 प्रश्नों में 10 शेड्यूल जोड़ियों का परीक्षण किया:
S3 Express पर, off/off (बिल्कुल भी प्रीफ़ेच नहीं) बिंदु प्रश्नों के लिए आश्चर्यजनक रूप से प्रतिस्पर्धी है क्योंकि प्रत्येक उप-चंक रेंज GET केवल ~4ms है। "कोई प्रीफ़ेच नहीं" और "इष्टतम प्रीफ़ेच" के बीच का अंतर छोटा है (बिंदु लुकअप के लिए 23%) क्योंकि व्यक्तिगत GET सस्ते हैं। Tigris पर, उसी प्रश्न को प्रीफ़ेच से अधिक लाभ होता है (idx-filter पर 39% तक) क्योंकि प्रत्येक बर्बाद राउंड ट्रिप में 25ms लगता है।
व्यावहारिक प्रभाव: उच्च-विलंबता वाले बैकएंड पर, खोज शेड्यूल को अधिक प्रयास करें और लुकअप शेड्यूल को अधिक अग्रणी शून्य के साथ रखें। S3 Express पर, डिफ़ॉल्ट अच्छी तरह से काम करते हैं और ट्यूनिंग छोटे लाभ प्रदान करती है। पूर्ण स्कैन प्रदर्शन दोनों बैकएंड पर शेड्यूल-असंवेदनशील है क्योंकि क्वेरी-प्लान फ्रंट-रनिंग पूरी तालिका को अग्रिम रूप से बल्क-प्रीफ़ेच करता है।
अपने विशिष्ट बैकएंड और प्रश्नों के लिए इष्टतम शेड्यूल खोजने के लिए tiered-tune (नीचे देखें) का उपयोग करें।
tiered-tune एक मौजूदा turbolite डेटाबेस से जुड़ता है और आपके वास्तविक प्रश्नों के विरुद्ध प्रीफ़ेच शेड्यूल को स्वीप करता है। शेड्यूल का अनुमान लगाने के बजाय, अपना वास्तविक कार्यभार चलाएं और टूल को सबसे अच्छी जोड़ी खोजने दें:```bash
cargo run --release --features cloud,zstd --bin tiered-tune --
--prefix "databases/tenant-123"
--query "SELECT * FROM users WHERE id = ?1"
--query "SELECT p.*, u.name FROM posts p JOIN users u ON p.user_id = u.id WHERE p.id = ?1"
--iterations 10
cargo run --release --features cloud,zstd --bin tiered-tune --
--prefix "databases/tenant-123"
--query "SELECT * FROM orders WHERE user_id = ?1 ORDER BY created_at DESC LIMIT 20"
--search-schedules "0.3,0.3,0.4;0.5,0.5;1.0"
--lookup-schedules "0;0,0,0.1;0,0,0,0.1,0.2"
--iterations 10
आउटपुट एक प्रति-क्वेरी तुलना तालिका है (जैसे `tiered-bench --matrix`) जिसमें प्रत्येक शेड्यूल जोड़ी के लिए p50, p90, GET काउंट और बाइट्स दिखाई जाते हैं। उपकरण एक शेड्यूल सुझाता है और इसे लागू करने के लिए `TurboliteConfig` असाइनमेंट प्रिंट करता है।
## स्थायित्व (Durability)
turbolite एक स्टोरेज लेयर है, कोई रेप्लिकेशन सिस्टम नहीं। स्थायित्व इस बात पर निर्भर करता है कि डेटा S3 तक कब पहुँचता है।
**चेकपॉइंट के बाद**: पेज ग्रुप + मैनिफेस्ट S3 में होते हैं। S3 11 9s का स्थायित्व प्रदान करता है। यह डेटा मशीन के खो जाने पर भी बचा रहता है।
**चेकपॉइंट के बीच**: राइट्स केवल स्थानीय WAL में स्थानीय डिस्क पर रहती हैं। यदि अगले चेकपॉइंट से पहले मशीन खत्म हो जाती है, तो वे राइट्स खो जाती हैं।
चेकपॉइंट आवृत्ति व्यापार-नियंत्रण को नियंत्रित करती है: अधिक बार चेकपॉइंट = छोटा डेटा-एट-रिस्क विंडो लेकिन अधिक S3 PUTs। डिफ़ॉल्ट SQLite का ऑटो-चेकपॉइंट है (प्रति 1000 WAL फ्रेम)।
### चेकपॉइंट मोड
turbolite `TurboliteConfig` में `sync_mode` के माध्यम से दो चेकपॉइंट मोड का समर्थन करता है:
**`SyncMode::Durable`** (डिफ़ॉल्ट)। चेकपॉइंट SQLite के EXCLUSIVE लॉक को धारण करते हुए पेज ग्रुप को S3 पर अपलोड करता है। सरल, प्रत्येक चेकपॉइंट पर पूरी तरह से टिकाऊ। जब तक अपलोड पूरा नहीं हो जाता, कोई राइट या रीड आगे नहीं बढ़ सकता। अधिकांश कार्यभार के लिए अच्छा है।
**`SyncMode::LocalThenFlush`**. चेकपॉइंट केवल स्थानीय डिस्क कैश में लिखता है (~1ms लॉक होल्ड), फिर लॉक जारी करता है। कॉलर अलग से `flush_to_s3()` के माध्यम से S3 पर अपलोड करता है, इस दौरान रीड और राइट सामान्य रूप से जारी रहते हैं। यह राइट-हैवी कार्यभार के लिए उपयोगी है जहाँ S3 अपलोड की अवधि के लिए रीडर्स को ब्लॉक करना अस्वीकार्य है।
चेकपॉइंट और फ्लश के बीच, डेटा केवल स्थानीय डिस्क कैश में मौजूद होता है। प्रक्रिया क्रैश ठीक है (डेटा स्थानीय डिस्क पर है, और स्टेजिंग लॉग अपलोड के लिए सटीक पेज सामग्री कैप्चर करते हैं)। फ्लश से पहले मशीन का नुकसान उन राइट्स के खो जाने का मतलब है। कैश निकासी सुरक्षित है: turbolite लंबित पृष्ठों को निकासी से स्वचालित रूप से सुरक्षित करता है।
**क्रैश रिकवरी**: यदि प्रक्रिया चेकपॉइंट और फ्लश के बीच क्रैश हो जाती है, तो स्टेजिंग लॉग डिस्क पर बचे रहते हैं। अगली `TurboliteVfs::new()` कॉल पर, वे स्वचालित रूप से पुनर्प्राप्त हो जाते हैं और अगली `flush_to_s3()` कॉल के लिए कतारबद्ध हो जाते हैं। रीड फ्लश की प्रतीक्षा किए बिना तुरंत स्थानीय कैश से परोसे जाते हैं।
### WAL शिपिंग (प्रायोगिक)
`wal` फीचर फ्लैग सक्षम होने पर, turbolite WAL फ्रेम्स को [walrust](https://github.com/russellromney/walrust) के माध्यम से S3 पर भेजता है, जो व्यक्तिगत राइट्स और चेकपॉइंट्स के बीच स्थायित्व अंतर को बंद कर देता है।```toml
# Cargo.toml
turbolite = { version = "0.5", features = ["cloud", "zstd", "wal"] }
% उपयोगकर्ताओं को Gmail खातों पर ब्रूट फोर्स करने की क्षमता देता है
% रैंडम यूज़र-एजेंट का उपयोग करता है
% लॉगिन को log.txt (user:pass प्रारूप) में सहेजता है```rust let config = TurboliteConfig { wal_replication: true, // enable WAL shipping ..Default::default() };
turbolite और walrust `manifest.change_counter` में संग्रहीत रीप्ले कर्सर के माध्यम से सिंक्रोनाइज़ रहते हैं। इम्पोर्ट/चेकपॉइंट पथ SQLite के फ़ाइल चेंज काउंटर से उस कर्सर को सीड करते हैं; डायरेक्ट पेज रीप्ले इसे नवीनतम प्रतिबद्ध चेंजसेट अनुक्रम तक आगे बढ़ा सकता है। कोल्ड स्टार्ट पर, turbolite पेज समूहों से डेटाबेस को मटेरियलाइज़ करता है, फिर walrust txid > `change_counter` वाले WAL सेगमेंट को रीप्ले करके अंतिम चेकपॉइंट के बाद हुए राइट्स को पुनर्प्राप्त करता है।
**WAL शिपिंग के साथ स्थायित्व मॉडल**: प्रत्येक प्रतिबद्ध लेन-देन को सिंक अंतराल (डिफ़ॉल्ट 100ms) के भीतर WAL सेगमेंट के रूप में S3 पर भेजा जाता है। यदि मशीन डाउन हो जाती है, तो अधिकतम एक सिंक अंतराल के राइट्स खो जाते हैं। चेकपॉइंट के बाद, txid <= `change_counter` वाले WAL सेगमेंट स्वचालित रूप से गार्बेज कलेक्ट हो जाते हैं।
WAL शिपिंग SyncMode का पूरक है: SyncMode नियंत्रित करता है कि चेकपॉइंट S3 तक कैसे पहुँचते हैं, WAL शिपिंग चेकपॉइंट से पहले व्यक्तिगत राइट्स को स्थायी बनाता है।
### संगति मॉडल
एकल लेखक, स्नैपशॉट पाठक। एक प्रक्रिया लिखती है; पाठक वह अंतिम प्रतिबद्ध मेनिफेस्ट देखते हैं जो उन्होंने खोलते समय देखा था। turbolite एक वितरित डेटाबेस नहीं है और एकाधिक लेखकों के बीच समन्वय नहीं करता है।
## स्थानीय मोड (S3 के बिना)
turbolite पूरी तरह से स्थानीय संपीड़ित/एन्क्रिप्टेड VFS के रूप में भी काम करता है:
संपीड़न: zstd (डिफ़ॉल्ट), lz4, snappy, gzip. zstd के साथ, आप कस्टम संपीड़न शब्दकोशों को प्रशिक्षित और एम्बेड कर सकते हैं और अधिक कुशल संपीड़न के लिए स्वचालित रूप से रोटेट कर सकते हैं। बड़े पेज आकार बेहतर संपीड़ित होते हैं। प्रशिक्षण उपकरणों के लिए CLI देखें।
एन्क्रिप्शन: प्रति पेज AES-256-GCM।
पेज-स्तरीय संचालन का मतलब है कि अधिकांश SQLite सुविधाएँ अभी भी काम करती हैं: FTS, R-tree, JSON, WAL मोड। अधिकांश अन्य SQLite संपीड़न/एन्क्रिप्शन एक्सटेंशन फ़ाइल स्तर पर काम करते हैं या कस्टम बिल्ड की आवश्यकता होती है।
## स्थापना
यह रिपॉजिटरी एक Cargo वर्कस्पेस है। `turbolite` क्रेट वर्कस्पेस रूट पर शुद्ध Rust लाइब्रेरी है। भाषा बाइंडिंग और लोड करने योग्य एक्सटेंशन `turbolite-ffi/` में रहते हैं।
**Python**: `pip install turbolite` — देखें [turbolite-ffi/packages/python/](https://github.com/russellromney/turbolite/blob/main/turbolite-ffi/packages/python)```python
import turbolite
# Local compressed (no S3 needed)
conn = turbolite.connect("my.db")
# S3 cloud
conn = turbolite.connect("my.db", mode="s3", bucket="my-bucket", endpoint="https://t3.storage.dev")
# Manual extension loading for full control
import sqlite3
conn = sqlite3.connect(":memory:")
turbolite.load(conn)
conn.close()
conn = sqlite3.connect("file:my.db?vfs=turbolite", uri=True) # local
# For S3, prefer turbolite.connect(..., mode="s3", bucket=..., prefix=...).
# It registers a per-database VFS so multiple S3 volumes can share one process.
Node.js: npm install turbolite — देखें turbolite-ffi/packages/node/
Rust:```toml [dependencies] turbolite = "0.5" # local VFS turbolite = { version = "0.5", features = ["cloud"] } # + S3 storage turbolite = { version = "0.5", features = ["encryption"] } # + encryption
**Go** (cgo, साझा लाइब्रेरी को लिंक करता है)```bash
make lib-bundled # build libturbolite.{so,dylib}
// #cgo LDFLAGS: -L/path/to/target/release -lturbolite
// #include <stdlib.h>
// extern int turbolite_register_local_file_first(const char* name, const char* db_path, int level);
// extern void* turbolite_open(const char* path, const char* vfs_name);
// extern int turbolite_exec(void* db, const char* sql);
// extern char* turbolite_query_json(void* db, const char* sql);
// extern void turbolite_close(void* db);
import "C"
अनुशंसित turbolite_register_local_file_first(name, db_path, level) उपयोगकर्ता-सामने वाले डेटाबेस पथ पर आधारित है।
निम्न-स्तरीय turbolite_register_local(name, cache_dir, level) अभी भी उन एम्बेडर्स के लिए निर्यात किया गया है जो स्वयं कैश निर्देशिका प्रबंधित करना चाहते हैं। पूर्ण HTTP सर्वर उदाहरण के लिए examples/go/ देखें।
SQLite के load_extension के साथ किसी भी भाषा के लिए लोड करने योग्य एक्सटेंशन बनाएँ:```bash
make ext # produces target/release/turbolite.{so,dylib}
---```c
sqlite3_enable_load_extension(db, 1);
sqlite3_load_extension(db, "path/to/turbolite", NULL, NULL);
// "turbolite" VFS (local) is always registered
// "turbolite-s3" is a single-volume convenience VFS when TURBOLITE_BUCKET is set
फ़ाइल-प्रथम उपयोगकर्ता कहानी के लिए, एक प्रति-डेटाबेस VFS पंजीकृत करें जो कॉलर के app.db का स्वामी हो:```sql
SELECT turbolite_register_file_first_vfs('app', '/data/app.db');
-- now open /data/app.db via vfs=app; turbolite stores its sidecar
-- metadata at /data/app.db-turbolite/.
### Node.js
विस्तार लोड समय पर फ़ाइल-प्रथम मोड के लिए डिफ़ॉल्ट `"turbolite"` VFS कॉन्फ़िगर करने के लिए, विस्तार लोड करने से पहले वातावरण में `TURBOLITE_DATABASE_PATH=/data/app.db` सेट करें। साइडकार तब `/data/app.db-turbolite/` होता है और निचले-स्तर का `TURBOLITE_CACHE_DIR` नॉब अनदेखा किया जाता है।```bash
npm install turbolite
इनपुट:```js const { connect } = require("turbolite");
// File-first: /data/app.db is the local page image. // /data/app.db-turbolite/ holds hidden implementation state. const db = connect("/data/app.db"); db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"); db.prepare("INSERT INTO users VALUES (?, ?)").run(1, 'alice');
const rows = db.prepare("SELECT id, name FROM users").all(); // [{ id: 1, name: 'alice' }] db.close();
`db` एक मानक better-sqlite3 डेटाबेस है। `connect()` आपके लिए प्रति-डेटाबेस फ़ाइल-फ़र्स्ट VFS पंजीकृत करता है। एक स्टॉक SQLite फ़ाइल का निर्यात करने के लिए (उदाहरण के लिए `sqlite3` CLI के साथ निरीक्षण करने के लिए), better-sqlite3 बैकअप API का उपयोग करें: `await db.backup('export.sqlite')`। पूर्ण दस्तावेज़ के लिए [turbolite-ffi/packages/node/](https://github.com/russellromney/turbolite/blob/main/turbolite-ffi/packages/node) देखें।
### Rust (स्थानीय, फ़ाइल-फ़र्स्ट)```rust
use turbolite::tiered::{TurboliteVfs, TurboliteConfig};
// `app.db` is the user-visible local page image.
// `app.db-turbolite/` holds hidden implementation state.
let config = TurboliteConfig::for_database_path("/data/app.db");
let vfs = TurboliteVfs::new_local(config)?;
turbolite::tiered::register("turbolite", vfs)?;
let conn = rusqlite::Connection::open_with_flags_and_vfs(
"/data/app.db",
rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE | rusqlite::OpenFlags::SQLITE_OPEN_CREATE,
"turbolite",
)?;
निचला-स्तरीय रूप आपको कैश निर्देशिका को सीधे चुनने देता है:```rust let config = TurboliteConfig { cache_dir: "/path/to/data".into(), // turbolite owns this dir ..Default::default() };
उस स्थिति में स्थानीय इमेज `/path/to/data/data.cache` है, न कि कॉलर-नामित `app.db`। नए एम्बेडर्स को फ़ाइल-प्रथम रूप को प्राथमिकता देनी चाहिए।
### Rust (S3 cloud)```rust
use turbolite::tiered::{TurboliteVfs, TurboliteConfig};
use hadb_storage::StorageBackend;
let config = TurboliteConfig::for_database_path("/data/app.db");
let storage: Arc<dyn StorageBackend> = /* your S3 backend */;
let vfs = TurboliteVfs::with_backend(config, storage, tokio::runtime::Handle::current())?;
turbolite::tiered::register("turbolite", vfs)?;
let conn = rusqlite::Connection::open_with_flags_and_vfs(
"/data/app.db",
rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE | rusqlite::OpenFlags::SQLITE_OPEN_CREATE,
"turbolite",
)?;
app.db turbolite का संपीड़ित पृष्ठ छवि है। इसे सीधे स्टॉक sqlite3 द्वारा खोले जाने का वादा नहीं किया गया है। एक सामान्य SQLite फ़ाइल के लिए (जैसे sqlite3 CLI के लिए), SQLite ऑनलाइन बैकअप API या बाइंडिंग-विशिष्ट निर्यात सहायक (conn.iterdump() Python में, db.backup() Node में) का उपयोग करें।
turbolite बिना Rust लिखे turbolite डेटाबेस का निरीक्षण, प्रबंधन और उनके साथ इंटरैक्ट करने के लिए एक CLI प्रदान करता है।```bash cargo install turbolite --features cloud,zstd
### कमांड्स```bash
# Inspect a database manifest
turbolite info --db my.db
turbolite info --db my.db --bucket my-bucket --endpoint https://t3.storage.dev
# Interactive SQLite shell (with turbolite VFS)
turbolite shell --db my.db
turbolite shell --db my.db --bucket my-bucket --read-only
# Download entire database from S3 into local cache
turbolite download --db my.db --bucket my-bucket --threads 8
# Export to plain SQLite (for migration or backup)
turbolite export --db my.db --output plain.db
# Import a plain SQLite file into turbolite S3 format
turbolite import --input plain.db --bucket my-bucket --prefix databases/my-db
All S3 commands accept --bucket, --prefix, --endpoint, and --region flags, or read from TURBOLITE_BUCKET, TURBOLITE_PREFIX, AWS_ENDPOINT_URL, and AWS_REGION environment variables.
There are many projects in the SQLite-over-network space. turbolite borrows ideas from all of them.
The most common approach: put an unmodified .db file on S3 or a CDN and issue HTTP Range GETs when SQLite reads a page.
.dbi index file that pre-collects B-tree interior nodes for prefetching - the same idea as turbolite's interior page bundles. Designed to compose with sqlite_zstd_vfs.These are all read-only and fetch uncompressed pages from the raw file. A point lookup transfers a raw 4KB (or 64KB) page per request.
These treat object storage as the source of truth and replicate individual pages or changesets, enabling partial replicas and offline-first / edge deployments.
orbitinghail/graft): A transactional storage engine for lazy, partial, strongly consistent replication over S3. The libgraft SQLite extension implements a VFS that reads and writes 4KB pages through Graft volumes. Uses framed zstd compression and splinter-based changesets. Closest architectural cousin to turbolite in the "replicate pages, not WAL frames" space, with a focus on multi-writer edge sync rather than cold-read latency.These replicate local writes to S3 for backup or restore.
wa-sqlite. Same one-object-per-page model, adapted to WASM / client-side use.All benchmarks live in benchmark/. See benchmark/README.md for deployment scenarios (local, Fly.io, EC2).
The tiered-bench binary generates a social media dataset (users, posts, likes, friendships) and benchmarks queries at each cache level against S3.
A separate benchmark/bench_s3vfs.py harness runs the same queries against sqlite-s3vfs for head-to-head comparison. It is deployed via benchmark/fly-s3vfs.toml and uses the same deterministic dataset generator as tiered-bench.```bash
TIERED_TEST_BUCKET=my-bucket AWS_ENDPOINT_URL=https://t3.storage.dev
cargo run --features zstd,cloud --bin tiered-bench --release --
--sizes 100000
cargo run --features zstd,cloud --bin tiered-bench --release --
--sizes 1000000 --prefetch-threads 8 --queries post --modes interior
cargo run --example quick-bench --features encryption --release
मुख्य फ़्लैग: `--sizes` (पंक्ति गणना), `--ppg` (प्रति समूह पृष्ठ), `--prefetch-threads`, `--prefetch-search` (SEARCH अनुसूची), `--prefetch-lookup` (लुकअप अनुसूची), `--grouping` (स्थितीय या btree), `--queries` (पोस्ट/प्रोफ़ाइल/किसने पसंद किया/पारस्परिक), `--modes` (कोई नहीं/आंतरिक/इंडेक्स/डेटा), `--skip-verify` (छोटी मशीनों पर COUNT(*) छोड़ें), `--iterations`, `--plan-aware` (आगे देखने वाली प्रीफ़ेच सक्षम करें), `--matrix` (स्वीप अनुसूची युग्म)। प्रति क्वेरी अनुसूचियाँ: `--post-prefetch`/`--post-lookup`, `--profile-prefetch`/`--profile-lookup`, आदि। (खोज और लुकअप प्रति क्वेरी स्वतंत्र हैं)।```bash
# Matrix mode: test 10 schedule pairs x 6 queries at cold level
cargo run --features zstd,cloud --bin tiered-bench --release -- \
--sizes 1000000 --import auto --plan-aware --matrix --iterations 10
# Tune schedules for your own database and queries
cargo run --features zstd,cloud --bin tiered-tune --release -- \
--prefix "databases/my-db" \
--query "SELECT * FROM users WHERE id = ?1" --param 42 \
--plan-aware --iterations 10
cargo test --features zstd # local VFS tests cargo test --features zstd,cloud # + S3 integration tests cargo test --features zstd,encryption # + encryption tests
## नोट्स
turbolite को पहले `sqlite-compress-encrypt-vfs` नाम से जाना जाता था, जिसे `sqlces` भी कहा जाता है।
### सुरक्षा मॉडल विवरण
S3 डेटा प्रति फ्रेम अद्वितीय यादृच्छिक नॉन्स के साथ AES-256-GCM का उपयोग करता है (प्रमाणित, छेड़छाड़ का पता लगाने वाला)। स्थानीय फ़ाइलें नियतात्मक नॉन्स (पृष्ठ संख्या / बाइट ऑफ़सेट) के साथ AES-256-CTR का उपयोग करती हैं, जो डिस्क-पर-आराम पर हमलावरों के खिलाफ गोपनीयता प्रदान करती हैं। CTR के नियतात्मक नॉन्स का मतलब है कि बहु-स्नैपशॉट हमलावर पुन: उपयोग किए गए ऑफ़सेट पर प्लेनटेक्स्ट के XOR को पुनर्प्राप्त कर सकते हैं, जो SQLite के अपने SEE एक्सटेंशन ट्रेडऑफ़ से मेल खाता है। स्थानीय कैश क्षणिक है और S3 से पुन: निर्माण योग्य है।
## लाइसेंस
Apache-2.0
| क्वेरी | प्रकार | कोल्ड (S3 Express) | कोल्ड (Tigris) |
|---|
| Post + user | पॉइंट लुकअप + जॉइन | 86ms | 172ms |
| Profile | मल्टी-टेबल जॉइन (5 JOINs) | 251ms | 479ms |
| Who-liked | इंडेक्स सर्च + जॉइन | 206ms | 302ms |
| Mutual friends | मल्टी-सर्च जॉइन | 19ms | 49ms |
| Indexed filter | कवर्ड इंडेक्स स्कैन | 79ms | 88ms |
| Full scan + filter | फुल टेबल स्कैन | 476ms | 532ms |
| कैश स्तर | क्या कैश किया गया है | S3 से क्या लाया गया है | ऐसा कब होता है |
|---|
| कोई नहीं | कुछ नहीं | सब कुछ | नई शुरुआत, खाली कैश |
| आंतरिक | आंतरिक B-ट्री पेज | इंडेक्स + डेटा पेज | कनेक्शन खुलने के बाद पहली क्वेरी |
| इंडेक्स | आंतरिक + इंडेक्स पेज | केवल डेटा पेज | सामान्य turbolite संचालन |
| डेटा | सब कुछ | कुछ नहीं | स्थानीय SQLite के समान |
| संचालन | SQLite | turbolite | ओवरहेड |
|---|
| पॉइंट लुकअप | 145K/s | 73K/s | 2.0x |
| रेंज स्कैन | 8.8K/s | 8.3K/s | समानता |
| फुल टेबल स्कैन | 56/s | 60/s | समानता |
| INSERT | 19K/s | 23K/s | समानता |
| PK द्वारा UPDATE | 40K/s | 27K/s | 1.5x |
| बैच INSERT (txn में) | 685K/s | 740K/s | समानता |
| S3 बाधा | प्रभाव |
|---|
| राउंड ट्रिप्स धीमे होते हैं | अनुरोध संख्या कम करें। लेखन को बैच करें, पढ़ने को आक्रामक रूप से प्रीफ़ेच करें। |
| बैंडविड्थ एक अड़चन है | बैंडविड्थ उपयोग को अधिकतम करें। |
| PUT और GET प्रति-संचालन शुल्क लेते हैं | एक 64KB GET की लागत एक 16MB GET के समान होती है। अनुरोध संख्या को अनुकूलित करें, बाइट दक्षता को नहीं। |
| ऑब्जेक्ट अपरिवर्तनीय होते हैं | स्थान पर कभी अपडेट न करें। नए संस्करण लिखें, एक पॉइंटर स्वैप करें। कोई आंशिक-लेखन भ्रष्टाचार नहीं। |
| स्टोरेज सस्ता है | स्थान के लिए अनुकूलित न करें। अधिक-प्रावधान करें, पुराने संस्करण रखें, बाद में GC साफ करने दें। |
| कार्यभार | कॉन्फ़िग | कारण |
|---|
| मिश्रित OLTP | डिफ़ॉल्ट | योजना-जागरूक स्कैन को संभालता है, खोज शेड्यूल इंडेक्स को गर्म करता है, लुकअप शेड्यूल रूढ़िवादी रहता है। |
| बिंदु-भारी (agent DBs) | prefetch.lookup: vec![0.0, 0.0, 0.0] | लुकअप को लगभग कभी प्रीफ़ेच की आवश्यकता नहीं होती। |
| स्कैन-भारी एनालिटिक्स | prefetch.search: vec![0.5, 0.5], prefetch.query_plan: true | आक्रामक खोज वार्मअप और योजना-जागरूक बल्क प्रीफ़ेच। |
| रूढ़िवादी (bursty serverless) | prefetch.search: vec![0.1, 0.2, 0.3], prefetch.lookup: vec![0.0, 0.0, 0.1] | न्यूनतम प्रीफ़ेच शोर। |
| बैकएंड | GET विलंबता | सर्वश्रेष्ठ बिंदु लुकअप | सर्वश्रेष्ठ प्रोफ़ाइल | ट्यूनिंग लाभ |
|---|
| S3 Express | ~4ms | 74ms (off/off: 96ms) | 188ms (off/off: 212ms) | 5-23% बिना प्रीफ़ेच की तुलना में |
| Tigris | ~25ms | 192ms (off/off: 231ms) | 524ms (off/off: 616ms) | 8-34% बिना प्रीफ़ेच की तुलना में |
| turbolite | Raw-file range GETs | Litestream VFS | sqlite_web_vfs + zstd_vfs | mvsqlite | Graft | sqlite-s3vfs |
|---|
| Reads from S3 | seekable range GETs on compressed page groups | range GETs on raw pages | range GETs on LTX files | range GETs on compressed outer DB | KV lookups on FoundationDB | lazy fetch of 4KB pages / changesets | one GetObject per page |
| Writes to S3 | checkpoint (one PUT per group) | no | no | no | yes (MVCC) | yes (async changeset replication) | one PUT per page |
| Compression | seekable multi-frame zstd | none | none | zstd (nested DB) | zstd delta encoding | framed zstd | none |
| Encryption | AES-256-GCM per page | none | none | none | none | none listed | none |
| Prefetch | look-ahead + hop schedule | none or basic readahead | LRU cache | adaptive consolidation | client buffers | lazy / on-demand | none |
| Interior page optimization | detected, pinned, bundled separately | none | page index from LTX trailers | optional .dbi file | none | none listed | none |
| Bytes per point lookup (cache: index) | ~100KB (one compressed frame) | 4-64KB (one raw page) | varies | varies | varies | 4KB (one page) | 4KB (one page) |
| Write cost per 4096 pages | ~$0.000005 (one PUT) | n/a | n/a | n/a | FoundationDB ops | batched changesets | ~$0.02 (4096 PUTs) |