Хранилище и ORM
В OxL три уровня хранения (§12.8): встроенное реплицируемое хранилище узлов кластера (Raft, шарды), транзакционный KV
std.kv и таблицы ORM std.orm над SQLite или PostgreSQL. Данные узла лежат в каталоге
--data-dir.
Таблицы#
data с @table("имя") — таблица; @pk — первичный ключ, @auto — значение выдаёт база. Запросы пишутся прямо в языке:
from n in db.Note order by n.id select n.id (§5.9) — компилятор переводит их в SQL и проверяет типы.
use std.orm { Db }
@table("notes")
data Note {
@pk @auto id: i64
text: str
}
fn main() io, db {
let db = Db.open("sqlite::memory:")!
db.insert(Note{ id: 0, text: "first" })
let a = db.lastInsertId()
db.insert(Note{ id: 0, text: "second" })
let b = db.lastInsertId()
print("{a} {b}")
print("{db.find(Note, 2)!.text} {db.count(Note)}")
let ids = from n in db.Note order by n.id select n.id
print("{ids}")
}1 2 second 2 [1, 2]
Схема меняется миграциями: oxl db ведёт oxl.db.lock и файлы migrations/NNNN_name.up.sql / .down.sql
(§12.8). Несколько записей в одну транзакцию собирает atomic(db) { … } (§11.2); конфликт перезапускает
тело, а события emit публикуются только после коммита.
Ключ-значение#
Kv.open("имя") открывает именованное пространство ключей; значения — bytes, у каждого ключа монотонная версия, поэтому
доступны сравнить-и-записать (putIf), префиксный просмотр (scan) и подписки (watch).
use std.kv { Kv }
use std.core { Utf8 }
fn main() io, db {
let kv = Kv.open("tour")!
kv.put("a/1", Utf8.encode("one"))!
kv.put("a/2", Utf8.encode("two"))!
kv.put("b/1", Utf8.encode("other"))!
let page = kv.scan("a/", 0)!
var keys: List<str> = []
for e in page { keys.push(e.key) }
print("{keys}")
let v = kv.version("a/1")!
// putIf записывает только если версия совпала (0 — ключа ещё нет)
print("{kv.putIf("a/1", Utf8.encode("uno"), v + 100)!} {kv.putIf("a/1", Utf8.encode("uno"), v)!}")
print("{kv.putIf("fresh", Utf8.encode("x"), 0)!} {kv.putIf("fresh", Utf8.encode("y"), 0)!}")
kv.delete("a/2")!
print("{kv.scan("a/", 0)!.len()}")
kv.close()
}["a/1", "a/2"] false true true false 1
Db.open(url) понимает sqlite:…, postgres://… и builtin://имя — таблицу во встроенном хранилище кластера: у каждой реплики
шарда своя SQLite-база, а Raft реплицирует изменения строк (§12.8). Тот же код работает со всеми адресами; адрес
обычно берут из конфигурации.