12.8 Встроенное хранилище и таблицы
- Хранилище на каждом узле роли
journal, репликация — Raft на шард. Данные — в каталоге--data-dir. Два локальных движка под Raft: LSM-дерево (ключ-значение) для системных данных иstd.kvи встроенная SQLite для таблиц ORM. - LSM хранит: состояние акторов, журналы саг и надёжных пайплайнов, записи идемпотентности, топики, outbox, буферы возобновляемых стримов, данные координатора, динамический конфиг.
- Прямой доступ —
std.kv(транзакционный KV с префиксными подписками). Запись (put,delete), исход которой неизвестен (лидер шарда мог зафиксировать её и отказать до ответа, ответ потерян), завершаетсяTimeout(«запись могла быть применена, а могла и нет»). Рантайм НЕ ДОЛЖЕН повторять такую запись сам: повтор слепой записи после подтверждённых записей других клиентов нарушает линеаризуемость. Клиент повторяет её с собственной идемпотентностью (CAS, идентификаторы). Определённые отказы («не лидер», «вытеснено», «не отправлено») повторяются автоматически; чтения, сканы и сообщения акторов (их дедуплицирует движок) тоже (D-8052; D-7472, Jepsen). - Таблицы ORM:
dataс@table("имя")— таблица;@pk— первичный ключ,@auto— генерация,@index(поля…), связиref<T>иmany<T>.Db.open(url)поддерживаетpostgres://,mysql://,sqlite://,builtin://.db.T—Table<T>:insert,update(T, id) { … },delete,find(T, id), запросы §5.9. - Таблицы во
builtin://: у каждой реплики шарда своя база SQLite. Raft реплицирует не текст SQL, а детерминированные изменения строк (набор изменений транзакции), поэтомуrandom(), время и порядок выполнения на репликах не влияют на результат. Запросы §5.9 переводятся в SQL и исполняются SQLite на лидере шарда (чтение линеаризуемо); запрос без ключа шардирования выполняется на всех шардах с объединением результатов поorder by/take.atomic(db)— транзакция внутри одного шарда. Снапшот шарда — согласованная копия его базы SQLite. Возможности SQL ограничены диалектом SQLite. @shard(by: .поле, count: n)на таблице — шардирование (во встроенном хранилище — по шардам кластера; для внешних баз — по списку URL).countзадаёт число шардов таблицы; если в базе шардов меньше, число шардов таблицы ограничивается числом шардов базы и выдаётся предупреждение. Безcountберётся число шардов базы. Число шардов таблицы сохраняется вместе с данными (таблица_oxl_table_metaвстроенного хранилища); открытие базы с другим числом (изменённыйcountилиshards=) — ошибка с указанием на перенос данных, а не молчаливая потеря видимости строк (D-8320). Перешардирование не реализовано.- Миграции —
oxl db diffиoxl db migrate(часть III).
Определяется реализацией. Миграции
builtin://(D-8065; DIV-022; D-8240, D-8241). Эта реализация:oxl db migrate|rollback|status --db builtin://имяработает с файлами шардов остановленного узла (каждая миграция выполняется на каждом шарде в своей транзакции, учёт — таблица_oxl_migrationsна шард; расхождение шардов отвергается); онлайн-миграция работающего или реплицируемого через Raft хранилища не реализована (необязательная возможность) — там DDL выполняет сама программа черезdb.execute(ddl).
Определяется реализацией. Повреждение журнала LSM (D-8066; D-8310). Испорченная запись в середине журнала (за ней есть валидные записи) не принимается за оборванный хвост: по умолчанию открытие хранилища завершается ошибкой
Corruptionс именем журнала и смещением, на диске ничего не меняется; режимkPointInTimeвосстанавливает записи до порчи, отбрасывает остаток и сохраняет повреждённые журналы как<n>.log.corrupt(потеря сообщается в stderr,oxl_store_wal_corruption_total). Испорченная последняя запись (за ней ничего валидного) — оборванный хвост: эта запись не подтверждалась. Манифест при порче всегда отвергается. Выбор режима из программы пока недоступен (только через API C++).
Связанные диагностики
query-not-sql, shard-count-clamped, table-schema
См. также
Исходник: docs/spec/oxl-standard.md