Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
turbolite — SQLite VFS с холодными JOIN-запросами из S3 менее чем за 100 мс + сжатие и шифрование на уровне страниц | Kitploit
Инструменты/GitHubGitHub/russellromney/turbolite
Инструменты шифрования/дешифрованияКриптографияБезопасность облачных средУтилиты и фреймворкиБезопасность Баз Данных
GitHubrussellromney/turbolite

turbolite

SQLite VFS с холодными JOIN-запросами из S3 менее чем за 100 мс + сжатие и шифрование на уровне страниц

Репозиторий
480122 месяцев назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

turbolite

turbolite — это SQLite VFS на Rust, который выполняет точечные запросы и объединения напрямую из S3 с холодной задержкой менее 250 мс.

Этот репозиторий представляет собой рабочую область Cargo с двумя крейтами:

  • turbolite — чисто Rust библиотека. SQLite VFS с сжатием на уровне страниц, шифрованием и распределением по уровням S3.
  • turbolite-ffi — C FFI / загружаемое расширение + привязки к языкам (Python, Node.js, Go).

Он также предлагает сжатие на уровне страниц (zstd) и шифрование (AES-256) для эффективности и безопасности в состоянии покоя, которые можно использовать отдельно от S3.

Экспериментально. turbolite находится в активной разработке и содержит ошибки. Будьте осторожны.

Объектное хранилище становится быстрым. S3 Express One Zone обеспечивает однозначные миллисекундные GET-запросы, а Tigris также чрезвычайно быстр. Разрыв между локальным диском и облачным хранилищем сокращается, и turbolite использует это.

Дизайн и название вдохновлены подходом turbopuffer к бескомпромиссной архитектуре с учётом ограничений облачного хранения. Первоначальной целью проекта было превзойти холодные старты Neon в 500 мс+. Цель достигнута.

Если у вас одна база данных на сервер, используйте том. turbolite исследует, как иметь сотни или тысячи баз данных (одна на арендатора, одна на рабочую область, одна на устройство), не использовать том для каждой из них и мириться с единственным источником записи.

turbolite поставляется как библиотека Rust, загружаемое расширение SQLite (.so/.dylib) и языковые пакеты для Python и Node.js, а также зависимости для Go через Github. Подходит любое S3-совместимое хранилище (AWS S3, Tigris, R2, MinIO и т. д.). Это стандартный VFS SQLite, работающий на уровне страниц, поэтому должно работать большинство функций SQLite: FTS, R-деревья, JSON, режим WAL и т. д.

turbolite является частью более широкой экосистемы hadb. Отдельный turbolite — это VFS для хранения с одним безопасным писателем; если вам нужна HA-выборка лидера с непрерывной репликацией WAL, используйте его через haqlite-turbolite, который поверх добавляет HaQLite и walrust. Этот HA-путь всё ещё очень экспериментален.

Если вы хотите внести вклад в turbolite или найти ошибки, создайте pull request или откройте issue.

Производительность

1M постов / 100K пользователей (~1.5 ГБ хранится) без кэширования, каждый байт с S3. EC2 c5.2xlarge + S3 Express One Zone (та же зона доступности, ~4 мс задержки GET). Fly performance-8x + Tigris (~25 мс задержки GET). Оба: 8 выделенных vCPU, 16 ГБ RAM, 7 рабочих потоков предвыборки. См. Бенчмаркинг и Важность бэкенда хранения.

Бенчмарки организованы по уровню кэша (что уже есть на локальном диске при выполнении запроса):

interior — самый реалистичный холодный бенчмарк: внутренние страницы загружаются сразу при открытии соединения, поэтому к моменту выполнения первого запроса они уже кэшированы. Страницы индекса агрессивно предварительно загружаются в фоне при первом обращении и могут быть ещё не готовы.

Тёплый кэш (накладные расходы VFS по сравнению с обычным SQLite)

100K строк, Fly.io performance-2x (выделенный vCPU, NVMe, IAD):

Точечные запросы имеют наибольшие накладные расходы на страницу (~2x). Всё остальное приближается к паритету или превосходит его. Архитектура кэша без блокировок означает, что параллельные чтения никогда не блокируют запись.

Стоимость контрольной точки

ПослеЛокальноS3 (RustFS в том же регионе)
1K вставок19ms38ms
Пакет 10K17ms114ms
1K обновлений9ms36ms

Запись всегда на локальной скорости. Затраты на S3 возникают только при контрольной точке. Цифры с RustFS в том же регионе Fly (~2 мс RTT). S3 Express One Zone была бы сопоставима.

Быстрый старт

Python```bash

pip install turbolite

root@kitploit:~
# Chunk 3

(No content provided in this chunk.)```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"

Смотрите Установка для Node, Go, Rust, локального режима и использования загружаемого расширения .so напрямую

Design

turbolite спроектирован с учётом ограничений S3, а не файловой системы. Каждое решение исходит из этой модели:

Architecture

turbolite добавляет слои интроспекции и косвенности между SQLite и S3, которые эффективно группируют, сжимают, отслеживают и извлекают страницы.

SQLite использует B-дерево и запрашивает по одной странице за раз. Он знает, что страница N находится по смещению N * page_size. И эти страницы распределены случайным образом по карте страниц для эффективного произвольного доступа. Но в S3 получение одной страницы за запрос означало бы тысячи потенциально случайных GET-запросов на каждый запрос.

Но страницы не одинаковы. SQLite имеет разные типы страниц. turbolite разделяет группы страниц по типу: внутренние страницы B-дерева, листовые страницы индекса и листовые страницы данных.

Внутренние страницы затрагиваются при каждом запросе для направления поиска к листовым страницам. turbolite обнаруживает их, сохраняет в сжатых пакетах в S3 и загружает их с опережением при открытии VFS. После этого каждый обход B-дерева приводит к попаданию в кэш.

Листовые страницы индекса получают ту же обработку: отдельные пакеты, ленивая фоновая предварительная выборка, закрепление против вытеснения. Холодные запросы должны извлекать только страницы данных.

turbolite использует интроспекцию B-дерева для понимания, к какому дереву (таблице или индексу) относится страница, и интеллектуально хранит эти страницы вместе в S3 как группы страниц: много страниц, объединённых в один объект S3. Достаточно большие, чтобы насытить пропускную способность при предварительной выборке, достаточно маленькие для точечных запросов. По умолчанию: 256 страниц в группе, ~16 МБ при страницах по 64 КБ.

Хранение одной таблицы/индекса вместе означает, что мы делаем минимально возможное количество GET-запросов для холодных запросов.

turbolite косвенно выполняет поиск страниц с помощью файла манифеста, который является источником истины о том, где находится каждая страница. Он заменяет неявное offset = page * size в SQLite на явные указатели. Старые версии групп страниц никогда не перезаписываются; PUT манифеста является атомарной точкой фиксации. Старые версии становятся мусором, очищаются с помощью gc().

SQLite по умолчанию использует страницы по 4 КБ для соответствия размеру страниц диска файловой системы. В S3 размер страниц диска не имеет значения. Важно минимизировать количество запросов и максимизировать разветвление B-дерева. Ответ — большие страницы: turbolite по умолчанию использует страницы по 64 КБ. Меньше страниц = меньше повторных запросов к S3 для достижения листа.

Чтобы ускорить точечные запросы, turbolite использует сжимаемое для поиска сжатие: каждая группа страниц кодируется как несколько фреймов zstd (~4 страницы на фрейм). Манифест хранит смещения байтов для каждого фрейма, поэтому при промахе кэша извлекается только подблок размером ~256 КБ с нужной страницей через S3 range GET, а не вся группа.

Предварительная выборка имеет два уровня: проактивный (предварительный запуск по плану запроса) и реактивный (адаптивный на основе промахов).

Предварительный запуск по плану запроса запускается первым. Перед выполнением запроса turbolite перехватывает план запроса SQLite через EXPLAIN QUERY PLAN, извлекает точные таблицы и индексы, которые затронет запрос, и отправляет все их группы страниц в пул предварительной выборки до того, как будет прочитана первая страница. Объединение пяти таблиц, которое в противном случае вызвало бы пять последовательных циклов промах-затем-выборка, вместо этого запускает все пять выборок параллельно в начале запроса. Для запросов SCAN это означает, что вся таблица предварительно выбирается заранее.

Предостережение: SQLite поддерживает один обратный вызов трассировки на соединение. Если другое расширение занимает этот слот первым, предварительный запуск молча переключается на реактивную предварительную выборку.

Реактивная предварительная выборка обрабатывает то, что пропустил предварительный запуск, и выступает в качестве запасного варианта. При промахе кэша одновременно происходят две вещи:

  1. Встроенный range GET: извлечение конкретного подблока, содержащего нужную страницу, немедленный возврат в SQLite.
  2. Фоновая предварительная выборка: отправка родственных групп для этого дерева в пул предварительной выборки в соответствии с расписанием.

Счётчики промахов отслеживаются для каждого B-дерева, а не глобально. Запрос профиля, который обращается к 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.

Вы можете настроить расписание предварительной выборки при открытии, установив prefetch.search / prefetch.lookup в TurboliteConfig — вы знаете ожидаемую форму рабочей нагрузки, поэтому VFS не нужно догадываться. См. Настройка предварительной выборки.

Оба расписания используют интроспекцию B-дерева: каждая предварительно выбранная группа гарантированно содержит страницы из правильного дерева. Пример: если SQLite запрашивает страницу из таблицы users, а затем ещё одну из той же таблицы, turbolite предполагает, что будет сканирование, и в фоне предварительно выбирает остальную часть таблицы users и ничего больше. Без интроспекции B-дерева он случайно загрузил бы половину таблицы users и половину таблицы posts только потому, что данные находятся рядом на диске.

Опережающий просмотр листовых страниц индекса делает то же самое для индексированного SEARCH. Листовая страница индекса уже содержит rowid таблицы, которые SQLite собирается запросить, поэтому turbolite разрешает их через кэшированные внутренние страницы и предварительно выбирает эти фреймы таблицы одной партией вместо по одному, сокращая количество запросов.

In-memory page cache

turbolite имеет собственный кэш страниц в памяти, который заменяет встроенный кэш страниц SQLite. Подсистема страниц SQLite кэширует страницы внутри и никогда не перечитывает из VFS для кэшированных страниц. Это нормально для баз данных с одним писателем, но для реплик чтения (последователи HA, читатели, опрашивающие манифест), кэш SQLite устаревает, когда базовые данные изменяются через репликацию.

Кэш turbolite учитывает манифест: когда срабатывает set_manifest() (новые данные из репликации), он инвалидирует затронутые страницы как в дисковом кэше, так и в кэше памяти. Записи также инвалидируют свои страницы в кэше памяти. Это гарантирует свежие чтения после репликации или записи.

Architecture:``` 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)

root@kitploit:~
**Конфигурация:**
- `cache.mem_budget` в `TurboliteConfig` (байты). По умолчанию: 64 МБ.
- Переменная окружения `TURBOLITE_MEM_CACHE_BUDGET` (например, `128MB`, `1GB`).
- Установите `0`, чтобы полностью отключить кэш в памяти.

`turbolite.connect()` (Python/Go/TypeScript) автоматически отключает страничный кэш SQLite и использует вместо него кэш turbolite. Пользователи Rust, использующие `Connection::open_with_flags_and_vfs` напрямую, должны установить `PRAGMA cache_size=0`, чтобы получить такое же поведение.

### Шифрование и сжатие

#### Сжатие

Все данные сжимаются с помощью zstd перед сохранением. Группы страниц используют многокадровое кодирование с возможностью поиска, которое независимо сжимает каждый кадр (~4 страницы, ~256 КБ), так что точечный поиск распаковывает только соответствующий кадр, а не всю группу страниц. Пользовательские словари zstd могут дополнительно улучшить степень сжатия.

Локальный (не S3) режим также сжимает на уровне страниц с помощью zstd. См. CLI для инструментов обучения словарей.

#### Шифрование

Если шифрование включено, turbolite шифрует всё: объекты S3, локальный кэш, WAL, метаданные. Данные S3 используют AES-256-GCM со случайными nonce на каждый кадр (аутентифицированный, с обнаружением подделки). Локальные данные используют AES-256-CTR с нулевым увеличением размера. Шифрование происходит после сжатия: `открытый текст → zstd → шифрование → S3`.

**Ротация ключей:** `rotate_encryption_key(config, new_key)` повторно шифрует, добавляет или удаляет шифрование всех данных S3 без распаковки. `Some` в `Some` — ротация ключей, `Some` в `None` — удаление шифрования, `None` в `Some` — добавление шифрования. Краш-безопасно: старые объекты никогда не перезаписываются, загрузка манифеста является атомарной точкой фиксации, а этап проверки подтверждает, что новые данные читаемы перед фиксацией. Сироты от частичных прогонов очищаются с помощью `gc()`.

## Преимущества и ограничения

### Где turbolite быстр

**Точечные запросы — сильная сторона.** На уровне кэша `index` точечный запрос загружает 1-2 подблока через S3 range GET (~100 КБ каждый). Внутренние страницы и страницы индекса уже закэшированы. На уровне кэша `none` добавляется ~120 мс на повторную загрузку внутренних страниц + первую страницу данных. Это работает на машинах любого размера.

**Сканирования с достаточным количеством ядер.** Пул упреждающей выборки насыщает пропускную способность S3 с адаптивным планированием для каждого дерева. Поисковые запросы агрессивно наращивают упреждающую выборку с первого промаха; плановые запросы SCAN пакетно загружают всю таблицу заранее. При достаточном количестве потоков можно синхронизировать базы данных размером в несколько ГБ за секунды с 2-3 пакетами упреждающей выборки.

### Где turbolite медленен

**Сканирования на маломощных машинах.** С 1 потоком упреждающей выборки сканирование 1,46 ГБ занимает секунды, а не миллисекунды. Узким местом являются круглые обходы S3: каждый прыжок загружает группы последовательно. Если ваш первый запрос — полное сканирование на 1-ядерной машине, ожидайте болезненного запуска.

**Плохая настройка потоков.** Слишком мало потоков упреждающей выборки — и сканирование зависает в ожидании S3. Слишком много — и фоновая работа SQLite начинает конкурировать с загрузками. По умолчанию (`max(num_cpus - 1, 1)`) оставляет одно ядро для фоновой работы, но для нагрузок со сканированием на больших базах данных всё равно нужно достаточно CPU.

**Штраф за первый запрос.** Первый запрос на уровне кэша `none` длится ~50-200 мс на загрузку внутренних страниц плюс как минимум один запрос данных. Если запросу требуется страница индекса до того, как фоновая упреждающая выборка завершится, он возвращается к inline range GET.

### Текущие ограничения

- **Автономный turbolite — однопользовательский на запись.** Две машины, записывающие напрямую в один и тот же префикс, повредят манифест.
- **Режим высокой доступности/отказоустойчивости экспериментален и находится в `haqlite-turbolite`.** Этот стек сочетает аренду HaQLite, многоуровневое хранение страниц turbolite и непрерывную репликацию WAL через walrust. Это предполагаемый путь для многоузловых развёртываний, а не прямой многопользовательский доступ к одному префиксу turbolite.
- **Доставка WAL экспериментальна.** Требует флага `wal` + walrust. См. [Долговечность](#durability).

Функции SQLite, которые **работают**: FTS, R-tree, JSON, режим WAL, режим журнала DELETE, VACUUM, autovacuum.

## Настройка

### Общие параметры

| Параметр | Что контролирует | По умолчанию |
|-----------|-----------------|---------|
| `prefetch.threads` | Потоки для параллельной загрузки из S3 | max(num_cpus - 1, 1) |
| `cache.pages_per_group` | Страниц на объект S3, больше = меньше PUT, больше байт за загрузку | 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]` | Три свободных прыжка, нулевая упреждающая выборка. Точечные запросы редко выигрывают от упреждающей выборки. |

Каждый элемент — это доля родственных групп для упреждающей загрузки при N-м последовательном промахе кэша для данного дерева. Когда количество промахов превышает длину массива, доля = 1,0 (все оставшиеся).

**Почему два реактивных плана?** Запросы SEARCH сканируют неизвестные части индексов/таблиц и нуждаются в агрессивном разогреве. Точечные запросы затрагивают 1-2 страницы на дерево и почти не нуждаются в упреждающей выборке. Счётчики промахов для каждого дерева обеспечивают независимое отслеживание: запрос профиля, обращающийся к пользователям (промах 1), а затем к постам (промах 1), отслеживает каждое дерево отдельно.

### Настройка упреждающей выборки

Установите `prefetch.search` и `prefetch.lookup` в `TurboliteConfig` при создании VFS:```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()
};

Для повторной настройки запроса без повторного открытия соединения используйте SQL-функцию turbolite_config_set (Phase Cirrus c). Каждый push привязан к дескриптору вызывающего соединения и остается в силе, пока вы не измените его снова:```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

root@kitploit:~
### Опережающее чтение листьев индекса

Когда запрос использует индекс для поиска строк таблицы (`SEARCH ... USING INDEX`), лист индекса, который SQLite читает, уже содержит имена rowid таблицы, которые он собирается извлечь. Опережающее чтение анализирует эти rowid, сопоставляет их с кадрами листов таблицы через кэшированные внутренние страницы и предварительно загружает кадры одной партией — так что строки таблицы прибывают вместе вместо одного S3-запроса за раз.

Он **включён по умолчанию** и срабатывает только для индексированного `SEARCH`, который обращается к таблице. Сканирования, чтение по конкретному rowid и полностью прогретые запросы идут по обычному пути без изменений, так что редко есть причина его отключать. Для работы требуется предварительная выборка плана запроса (`plan_aware`, по умолчанию true).

Единственный случай для его отключения — это полностью прогретая, чувствительная к ЦП нагрузка, когда разбор каждого листа индекса стоит немного, а предварительная выборка ничего не даёт, так как страницы уже кэшированы:```sql
SELECT turbolite_config_set('lookahead', 'false');

Или установите lookahead в TurboliteConfig / переменную окружения TURBOLITE_LOOKAHEAD при открытии.

Вызывающие из Rust могут использовать тот же путь через turbolite::tiered::settings::set.

Рекомендуемые конфигурации

Примечание: предвыборка выполняется на каждое соединение. Каждое новое соединение начинает с холодных счетчиков промахов на дерево. Кеш общий, поэтому второе соединение использует страницы, закешированные первым.

Влияние бэкенда хранения

Оптимальные расписания предвыборки зависят от компромисса между задержкой и пропускной способностью вашего S3-бэкенда. Мы протестировали 10 пар расписаний на 6 запросах как на S3 Express (~4ms GET), так и на Tigris (~25ms GET):

На S3 Express off/off (вообще без предвыборки) удивительно конкурентоспособен для точечных запросов, потому что каждый GET диапазона подчанка занимает всего ~4ms. Разрыв между «без предвыборки» и «оптимальной предвыборкой» невелик (23% для точечных поисков), поскольку отдельные GET дешевы. На Tigris тот же запрос выигрывает от предвыборки гораздо больше (до 39% на idx-filter), потому что каждый лишний круг запроса стоит 25ms.

Практический эффект: на бэкендах с высокой задержкой усиливайте расписания поиска и оставляйте расписания поиска по ключу с большим количеством начальных нулей. На S3 Express настройки по умолчанию работают хорошо, и настройка дает меньший выигрыш. Производительность полного сканирования нечувствительна к расписанию на обоих бэкендах, потому что query-plan frontrunning выполняет массовую предвыборку всей таблицы заранее.

Используйте tiered-tune (см. ниже), чтобы найти оптимальные расписания для вашего конкретного бэкенда и запросов.

Инструмент настройки

tiered-tune подключается к существующей базе данных turbolite и перебирает расписания предвыборки на ваших реальных запросах. Вместо того чтобы угадывать расписания, запустите вашу реальную нагрузку и позвольте инструменту найти лучшую пару:```bash

Connect to existing database, test your queries

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

Custom schedule grid

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

root@kitploit:~
Вывод представляет собой таблицу сравнения по каждому запросу (как `tiered-bench --matrix`), показывающую p50, p90, количество GET и байты для каждой пары расписаний. Инструмент рекомендует расписание и выводит назначение `TurboliteConfig` для его применения.

## Долговечность

turbolite — это уровень хранения, а не система репликации. Долговечность зависит от того, когда данные достигают S3.

**После контрольной точки**: группы страниц + манифест находятся в S3. S3 обеспечивает 11 девяток долговечности. Эти данные переживают потерю машины.

**Между контрольными точками**: записи живут только в локальном WAL на локальном диске. Если машина выйдет из строя до следующей контрольной точки, эти записи будут потеряны.

Частота контрольных точек управляет компромиссом: более частые контрольные точки = меньший интервал риска для данных, но больше операций PUT в S3. По умолчанию используется автоматическая контрольная точка SQLite (каждые 1000 фреймов WAL).

### Режимы контрольных точек

turbolite поддерживает два режима контрольных точек через `sync_mode` в `TurboliteConfig`:

**`SyncMode::Durable`** (по умолчанию). Контрольная точка загружает группы страниц в S3, удерживая эксклюзивную блокировку SQLite. Просто, полностью долговечно на каждой контрольной точке. Никакие записи или чтения не могут выполняться до завершения загрузки. Хорошо подходит для большинства рабочих нагрузок.

**`SyncMode::LocalThenFlush`**. Контрольная точка записывает только в локальный дисковый кэш (~1 мс удержания блокировки), затем снимает блокировку. Вызывающий код отдельно загружает данные в S3 через `flush_to_s3()`, в течение которого чтения и записи продолжаются в обычном режиме. Это полезно для рабочих нагрузок с интенсивной записью, где блокировка читателей на время загрузки в S3 неприемлема.

Между контрольной точкой и сбросом данные существуют только в локальном дисковом кэше. Сбой процесса не страшен (данные на локальном диске, а журналы промежуточного хранения фиксируют точное содержимое страниц для загрузки). Потеря машины до сброса означает, что эти записи будут потеряны. Вытеснение кэша безопасно: turbolite автоматически защищает страницы, ожидающие загрузки, от вытеснения.

**Восстановление после сбоя**: Если процесс завершился аварийно между контрольной точкой и сбросом, журналы промежуточного хранения сохраняются на диске. При следующем вызове `TurboliteVfs::new()` они автоматически восстанавливаются и ставятся в очередь для следующего вызова `flush_to_s3()`. Чтения обслуживаются немедленно из локального кэша без ожидания сброса.

### Доставка WAL (экспериментально)

С включенным флагом функции `wal` turbolite доставляет фреймы WAL в S3 через [walrust](https://github.com/russellromney/walrust), закрывая разрыв в долговечности между отдельными записями и контрольными точками.```toml
# Cargo.toml
turbolite = { version = "0.5", features = ["cloud", "zstd", "wal"] }

No content provided.```rust let config = TurboliteConfig { wal_replication: true, // enable WAL shipping ..Default::default() };

root@kitploit:~
turbolite и walrust остаются синхронизированными через курсор воспроизведения, хранящийся как `manifest.change_counter`. Пути импорта/контрольных точек инициализируют этот курсор из счетчика изменений файла SQLite; прямой повтор страниц может продвинуть его до последней зафиксированной последовательности изменений. При холодном запуске turbolite материализует базу данных из групп страниц, затем walrust воспроизводит сегменты WAL с txid > `change_counter`, чтобы восстановить записи, произошедшие после последней контрольной точки.

**Модель долговечности с передачей WAL**: каждая зафиксированная транзакция отправляется в S3 как сегмент WAL в течение интервала синхронизации (по умолчанию 100 мс). Если машина выходит из строя, теряется не более одной записи за интервал синхронизации. После контрольной точки сегменты WAL с txid <= `change_counter` автоматически собираются сборщиком мусора.

Передача 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/HEAD/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

root@kitploit:~
**Go** (cgo, связывает общую библиотеку):```bash
make lib-bundled  # build libturbolite.{so,dylib}
root@kitploit:~
// #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) по-прежнему экспортируется для встраиваемых систем, которые хотят управлять каталогом кэша самостоятельно. Смотрите examples/go/ для полного примера HTTP-сервера.

Загружаемое расширение (любой язык)

Соберите загружаемое расширение для любого языка с помощью load_extension SQLite:```bash

after cloning the turbolite repo

make ext # produces target/release/turbolite.{so,dylib}

root@kitploit:~
* **Учетные данные и личная информация** - идентификаторы, пароли, адреса электронной почты, полные имена, номера телефонов и т.д.

     ```python
     python n0tty.py
     ```

     | Шаблон                                      | Описание                                                                                                            |
     |---------------------------------------------|---------------------------------------------------------------------------------------------------------------------|
     | `--pattern pwnagotchi`                      | Сопоставление всех ключей Pwnagotchi (/etc/pwnagotchi/ или /root/.pwnagotchi/ или /root/brain.nn или ...)           |
     | `--pattern networking`                      | Сопоставление сетевых ключей (/etc/NetworkManager/system-connections/ или /etc/netplan/.yml или ...)                |```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/.

root@kitploit:~
Чтобы настроить VFS по умолчанию `"turbolite"` для режима file-first (сначала файл) во время загрузки расширения, установите переменную окружения `TURBOLITE_DATABASE_PATH=/data/app.db` перед загрузкой расширения. Тогда сайдкар будет `/data/app.db-turbolite/`, а низкоуровневый параметр `TURBOLITE_CACHE_DIR` игнорируется.

### Node.js```bash
npm install turbolite

Репозиторий

Мы не будем создавать репозиторий пакетов или что-то сложное. Мы просто поместим пакеты на нужную систему и установим их напрямую.

Ужесточим права доступа для промышленной эксплуатации:

root@kitploit:~
# chown -R root:root /opt/kasm
# chmod -R 755 /opt/kasm

Последнее, что я хочу отметить:

  1. Для вас была сгенерирована страница со списком URL по адресу: http[s]://mydomain.com/list Она отобразит все установленные вами рабочие пространства с прямыми ссылками на них. Обратите внимание, что сгенерированные ссылки ссылаются на имя хоста, которое вы указали при установке. Поэтому, если вы использовали имя хоста сервера, они не будут работать непосредственно в вашей системе (поэтому настройте DNS для домена, который вы использовали при установке). Короче говоря, если вы при установке использовали IP-адрес сервера, список URL не будет работать, пока вы не измените имя хоста в базе данных.

  2. Все установленные рабочие пространства являются постоянными. О том, как сделать их непостоянными, мы поговорим позже.

  3. Вы можете изменить пароль администратора, выполнив:

    root@kitploit:~
    sudo docker exec -it kasm_db psql -U kasmapp -d kasm -c "UPDATE public.user SET password = 'yourpassword' WHERE username = '[email protected]';"
    

Я советую вам ознакомиться с официальной документацией, было добавлено довольно много функций.

И это быстрая настройка KASM.

Для целей этого руководства у нас уже есть сервер с установленным KASM.```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();

root@kitploit:~
`db` — это стандартная база данных better-sqlite3. `connect()` регистрирует для вас VFS с приоритетом файла для каждой базы данных. Чтобы экспортировать стандартный файл SQLite (например, для просмотра с помощью CLI `sqlite3`), используйте API резервного копирования better-sqlite3: `await db.backup('export.sqlite')`. Полную документацию см. в [turbolite-ffi/packages/node/](https://github.com/russellromney/turbolite/blob/HEAD/turbolite-ffi/packages/node/).

### Rust (local, file-first)```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() };

root@kitploit:~
В этом случае локальный образ — это `/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-файла (например, для CLI sqlite3) используйте API онлайн-резервирования SQLite или экспортный хелпер, специфичный для привязки (conn.iterdump() в Python, db.backup() в Node).

CLI

turbolite поставляется с CLI для проверки, управления и взаимодействия с базами данных turbolite без написания Rust.```bash cargo install turbolite --features cloud,zstd

root@kitploit:~
### Команды```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

Все команды S3 принимают флаги --bucket, --prefix, --endpoint и --region или читают из переменных окружения TURBOLITE_BUCKET, TURBOLITE_PREFIX, AWS_ENDPOINT_URL и AWS_REGION.

Связанные проекты и сравнение

В области SQLite через сеть существует множество проектов. turbolite заимствует идеи у всех них.

Range-запросы к сырым .db файлам (только чтение)

Самый распространенный подход: поместить неизмененный файл .db на S3 или CDN и отправлять HTTP Range GET-запросы при чтении страницы SQLite.

  • sql.js-httpvfs: Оригинальный проект. WASM SQLite с HTTP Range-запросами. Имеет виртуальные головки чтения с экспоненциальной предвыборкой для сканирований. Первым предложил идею, что для запроса к базе данных не нужно загружать её целиком.
  • sqlite_web_vfs: Нативное расширение VFS на C++ с адаптивной консолидацией запросов и опциональным индексным файлом .dbi, который предварительно собирает внутренние узлы B-дерева для предвыборки — та же идея, что и пакеты внутренних страниц turbolite. Разработан для совместной работы с sqlite_zstd_vfs.
  • sqlite3vfshttp: Чистый, минималистичный VFS на Go. Создан для запросов SQLite на S3 из Lambda без загрузки файла.
  • sqlite-s3-query: Python-библиотека, использующая ctypes для перехвата файлового ввода-вывода и преобразования чтений в S3 Range GET-запросы. Требует версионированных корзин для согласованности при замене базы данных.
  • sqlite-wasm-http: Духовный преемник sql.js-httpvfs, использующий официальную сборку SQLite WASM. Общий кэш страниц через SharedArrayBuffer. Активно поддерживается.
  • s3sqlite: Python, использует s3fs (FUSE) + APSW. Позволяет FUSE обрабатывать Range-запросы.

Все они предназначены только для чтения и извлекают несжатые страницы из сырого файла. Один точечный запрос передает одну сырую страницу размером 4 КБ (или 64 КБ) за запрос.

Репликация на уровне страниц / синхронизация на границе сети

Эти проекты рассматривают объектное хранилище как источник истины и реплицируют отдельные страницы или наборы изменений, обеспечивая частичные реплики и развертывания с приоритетом офлайн-режима / на границе сети.

  • Graft (orbitinghail/graft): Транзакционный движок хранения для ленивой, частичной, строго согласованной репликации через S3. Расширение SQLite libgraft реализует VFS, который читает и записывает страницы размером 4 КБ через тома Graft. Использует фреймовое сжатие zstd и наборы изменений на основе splinter. Ближайший архитектурный родственник turbolite в области «реплицировать страницы, а не фреймы WAL», с фокусом на многопользовательскую синхронизацию на границе сети, а не на задержку холодного чтения.
  • mvsqlite: Страницы хранятся в FoundationDB как пары ключ-значение с адресацией по содержимому. Полная MVCC с возможностью перемещения во времени к любому снимку, дельта-кодирование XOR+zstd между версиями страниц. Самый сложный движок хранения в этой области, но требует FoundationDB, а не S3.

Репликация и резервное копирование на S3

Эти проекты реплицируют локальные записи на S3 для резервного копирования или восстановления.

  • Litestream: Непрерывно отправляет фреймы WAL на S3. Золотой стандарт резервного копирования SQLite. Более новая версия VFS Litestream может обслуживать чтения с S3, используя Range-запросы к файлам LTX с LRU-кэшем и индексом страниц — архитектурно ближайший аналог пути чтения turbolite, но только для чтения и привязан к формату репликации Litestream.
  • LiteFS: Система «первичный/реплика» на основе FUSE от Fly.io. Захватывает наборы изменений страниц и передаёт их репликам. Решает проблему доступности, а не хранения.
  • Verneuil: Разбивает базу данных на блоки по 64 КБ со сжатием zstd и файлом манифеста, асинхронно реплицирует на S3. Модель «блок + манифест» напоминает группы страниц + манифест turbolite, но Verneuil — это инструмент репликации; запросы выполняются к локальному диску, а не к S3.
  • libSQL/sqld (от Turso): Форк SQLite с интерфейсом Virtual WAL. Режим "Bottomless" отправляет фреймы WAL на S3. Запросы локальны; S3 используется для восстановления.

Пользовательские движки хранения

  • sqlite-s3vfs: Каждая страница SQLite хранится как отдельный объект S3. Позволяет запись, но по одному PUT на страницу, что стоит $0,02 за 4096 страниц против $0,000005 у turbolite за тот же пакет (при размере страницы по умолчанию 64 КБ). См. таблицу бенчмарков выше для сравнения задержек холодных запросов; turbolite в 7,5–263 раза быстрее на том же наборе данных.
  • wa-sqlite-s3vfs: Порт sqlite-s3vfs на TypeScript для браузера для wa-sqlite. Та же модель «один объект на страницу», адаптированная для использования в WASM / на стороне клиента.

Сжатие

  • sqlite_zstd_vfs: Хранит сжатые страницы в виде строк во внешней «обёрточной» базе данных. zstd с обучением словаря. Комбинируется с sqlite_web_vfs для чтения сжатых Range-запросов через HTTP. Комбинация sqlite_web_vfs + sqlite_zstd_vfs, вероятно, является наиболее близким существующим аналогом пути чтения turbolite, но она только для чтения и не объединяет страницы в группы.
  • SQLCipher: Шифрование AES-256 на уровне страниц для локального SQLite. Без удаленного хранения.

Чем отличается turbolite

Бенчмаркинг

Все бенчмарки находятся в benchmark/. См. benchmark/README.md для сценариев развертывания (локально, Fly.io, EC2).

Двоичный файл tiered-bench генерирует набор данных социальной сети (пользователи, посты, лайки, дружба) и тестирует запросы на каждом уровне кэша к S3.

Отдельный тестовый стенд benchmark/bench_s3vfs.py выполняет те же запросы к sqlite-s3vfs для прямого сравнения. Он разворачивается через benchmark/fly-s3vfs.toml и использует тот же детерминированный генератор наборов данных, что и tiered-bench.```bash

Basic benchmark: 100K posts, default settings

TIERED_TEST_BUCKET=my-bucket AWS_ENDPOINT_URL=https://t3.storage.dev
cargo run --features zstd,cloud --bin tiered-bench --release --
--sizes 100000

1M posts, 8 prefetch threads, only interior-level point queries

cargo run --features zstd,cloud --bin tiered-bench --release --
--sizes 1000000 --prefetch-threads 8 --queries post --modes interior

Quick local VFS comparison (no S3 needed)

cargo run --example quick-bench --features encryption --release

root@kitploit:~
Ключевые флаги: `--sizes` (количество строк), `--ppg` (страниц на группу), `--prefetch-threads`, `--prefetch-search` (расписание SEARCH), `--prefetch-lookup` (расписание lookup), `--grouping` (позиционный или btree), `--queries` (post/profile/who-liked/mutual), `--modes` (none/interior/index/data), `--skip-verify` (пропустить COUNT(*) на маломощных машинах), `--iterations`, `--plan-aware` (включить упреждающую выборку), `--matrix` (пары расписаний sweep). Расписания для каждого запроса: `--post-prefetch`/`--post-lookup`, `--profile-prefetch`/`--profile-lookup` и т.д. (поиск и 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

Тестирование```bash

cargo test --features zstd # local VFS tests cargo test --features zstd,cloud # + S3 integration tests cargo test --features zstd,encryption # + encryption tests

root@kitploit:~
## Примечания

turbolite ранее назывался `sqlite-compress-encrypt-vfs`, также известный как `sqlces`.

### Детали модели безопасности

Данные в S3 используют AES-256-GCM с уникальными случайными nonce для каждого кадра (аутентифицированный, обнаруживающий подделку). Локальные файлы используют AES-256-CTR с детерминированными nonce (номер страницы / смещение в байтах), обеспечивая конфиденциальность против атакующих, имеющих доступ к диску в состоянии покоя. Детерминированные nonce в CTR означают, что атакующий с несколькими снимками может восстановить XOR открытых текстов по повторно используемым смещениям, что соответствует компромиссу расширения SEE самого SQLite. Локальный кэш является эфемерным и может быть воссоздан из S3.

## Лицензия

Apache-2.0
Скачать инструмент
ЗапросТипХолодный (S3 Express)Холодный (Tigris)
Post + userpoint lookup + join86ms172ms
Profilemulti-table join (5 JOINs)251ms479ms
Who-likedindex search + join206ms302ms
Mutual friendsmulti-search join19ms49ms
Indexed filtercovered index scan79ms88ms
Full scan + filterfull table scan476ms532ms
Уровень кэшаЧто кэшированоЧто загружается с S3Когда это происходит
noneничеговсёСвежий старт, пустой кэш
interiorвнутренние страницы B-деревастраницы индекса + данныхПервый запрос после открытия соединения
indexвнутренние + страницы индексатолько страницы данныхНормальная работа turbolite
dataвсёничегоЭквивалент локального SQLite
ОперацияSQLiteturboliteНакладные расходы
Point lookup145K/s73K/s2.0x
Range scan8.8K/s8.3K/sпаритет
Full table scan56/s60/sпаритет
INSERT19K/s23K/sпаритет
UPDATE by PK40K/s27K/s1.5x
Batch INSERT (в транзакции)685K/s740K/sпаритет
Ограничение S3Последствие
Повторные запросы медленныеСводите количество запросов к минимуму. Группируйте записи, агрессивно предварительно считывайте.
Пропускная способность — узкое местоМаксимально используйте пропускную способность.
PUT и GET тарифицируются за операциюGET на 64 КБ стоит столько же, сколько GET на 16 МБ. Оптимизируйте количество запросов, а не эффективность по байтам.
Объекты неизменяемыНикогда не обновляйте на месте. Записывайте новые версии, меняйте указатель. Нет повреждения данных при частичной записи.
Хранилище дёшевоНе оптимизируйте под объём. Выделяйте с запасом, сохраняйте старые версии, пусть сборщик мусора уберёт их позже.
НагрузкаКонфигурацияПочему
Смешанная OLTPПо умолчаниюPlan-aware обрабатывает сканирования, расписание поиска прогревает индексы, расписание поиска по ключу остается консервативным.
С точечными запросами (БД агентов)prefetch.lookup: vec![0.0, 0.0, 0.0]Поиск по ключу почти никогда не требует предварительной выборки.
С аналитическими сканированиямиprefetch.search: vec![0.5, 0.5], prefetch.query_plan: trueАгрессивный прогрев поиска плюс plan-aware массовая предвыборка.
Консервативная (всплески serverless)prefetch.search: vec![0.1, 0.2, 0.3], prefetch.lookup: vec![0.0, 0.0, 0.1]Минимальный шум предвыборки.
БэкендЗадержка GETЛучший точечный запросЛучший профильВыигрыш от настройки
S3 Express~4ms74ms (off/off: 96ms)188ms (off/off: 212ms)5-23% по сравнению с отсутствием предвыборки
Tigris~25ms192ms (off/off: 231ms)524ms (off/off: 616ms)8-34% по сравнению с отсутствием предвыборки
turboliteRaw-file range GETsLitestream VFSsqlite_web_vfs + zstd_vfsmvsqliteGraftsqlite-s3vfs
Чтение с S3поисковые Range GET-запросы на сжатые группы страницRange GET-запросы на сырые страницыRange GET-запросы на файлы LTXRange GET-запросы на сжатую внешнюю БДKV-поиски в FoundationDBленивая загрузка страниц 4 КБ / наборов измененийодин GetObject на страницу
Запись на S3контрольная точка (один PUT на группу)нетнетнетда (MVCC)да (асинхронная репликация наборов изменений)один PUT на страницу
Сжатиепоисковый многофреймовый zstdнетнетzstd (вложенная БД)дельта-кодирование zstdфреймовый zstdнет
ШифрованиеAES-256-GCM на страницунетнетнетнетне указанонет
Предвыборкаупреждающий просмотр + расписание hopнет или базовая упреждающая выборкаLRU-кэшадаптивная консолидацияклиентские буферылениво / по требованиюнет
Оптимизация внутренних страницобнаруживаются, закрепляются, объединяются отдельнонетиндекс страниц из LTX-трейлеровопциональный файл .dbiнетне указанонет
Байт на точечный запрос (кэш: индекс)~100 КБ (один сжатый фрейм)4-64 КБ (одна сырая страница)различноразличноразлично4 КБ (одна страница)4 КБ (одна страница)
Стоимость записи на 4096 страниц~$0,000005 (один PUT)н/дн/дн/доперации FoundationDBпакетные наборы изменений~$0,02 (4096 PUTов)