OxL Справочник OxL
РусскийEnglish

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