kevy
kevy 6.3.0 · 中に組み込む

ストアを、
そのものの中へ

サーバーもソケットもネットワークもありません。エンジンは、呼び出せる struct であり、481 KB の WebAssembly モジュールであり、OS のないチップの上の no_std ライブラリです——そして 3 つとも、同じコマンドを持つ、同じエンジンです。

なぜ向いているのか

オフラインで動く必要のあるアプリケーションは、どれも結局ストレージ層を書くことになります。デスクトップアプリは SQLite と、誰も望まなかったスキーマを手に入れます。Web アプリは localStorage を使い、5 MB の上限と、同期でしかも文字列しか扱えないという事実を知り、次に IndexedDB とその上の抽象を手に入れます。デバイスは、フラッシュ上に手書きのリングバッファを手に入れます。

3 つとも同じ問題であり、同じ解になりえます。kevy はプロセス境界なしで組み込め、本物の TTL と pub/sub を持ったままブラウザに届き、固定 arena とアロケータなしで Cortex-M でも起動します。最後のひとつは、CI が push のたびに証明しています。

Rust のプログラムの中で

ストアは、呼び出せる struct です——ソケットも、シリアライズも、2 つ目のプロセスもありません。永続化され、開くときにログを再生します。

追加する
# Cargo.toml
kevy-embedded = "4.0"
開いて、書いて、読む
let db = Db::open("data/")?;
db.set(b"session:7f3a", b"{\"user\":\"ada\"}", Some(Duration::from_secs(3600)))?;
assert_eq!(db.get(b"session:7f3a")?.is_some(), true);
あとで redis-cli が要るなら、リスナーを開く
db.listen("127.0.0.1:6379")?;

他のプロセスが、同じストアに RESP で届くようになります。上のコードは、何ひとつ変わりません。

コストと制約 組み込みのストアは、共有されません。データディレクトリを所有するのは 1 つのプロセスです。2 つ目のプロセスがデータを必要とするなら、そのためにあるのが上のリスナー——あるいはフルのサーバー——です。ストアはプロセス内でも RAM 予算を取れます(with_tier_budget)。コールドな値はディスクへ退避し、あなたのプロセスの中で戻ります——正直な注記として、プロセス内のコールド読み取りは、その読み取りの間ストアのロックを保持します。退避できる値の最大が既定で 256 KiB に制限されているのは、そのためです。詳細はティアリングのガイドにあります。

ブラウザのタブで

gzip 後 481 KB。ブラウザ自身のファイルシステムに永続化され、リロードに耐え、タブをまたいで pub/sub を話します。

永続化つきで開く
import { open } from "@goliapkg/kevy";

const db = await open({ persist: { name: "app" } });
本物の TTL で書き、リロードのあとに読む
db.set("cart:u881", JSON.stringify(items), { ttlMs: 86_400_000 });
db.get("cart:u881");        // still there after a reload
db.pttl("cart:u881");       // the engine expires it, not your code
他のタブの声を聞く
db.subscribe("sync", (payload) => merge(payload));

コストと制約 小さな同期の読み取りなら、localStorage のほうが速いです——あれはページ自身のアドレス空間にあるマップであり、OPFS の上に作られたものが、そこで勝つことはありません。kevy が勝つのは、そもそも localStorage を選ぶべきでない理由のほうです。本物の TTL、5 MB の上限がないこと、値が文字列ではなくバイト列であること、そして書き込みがメインスレッドを止めないこと。

マイコンの上で

no_std、アロケータなし、OS なし。ストアは、大きさを自分で決める固定の arena に住みます。CI が push のたびに、これを起動させています。

そぎ落とす
# Cargo.toml
kevy-store = { version = "4.0", default-features = false }
メモリを渡して、使う
let mut arena = [0u8; 64 * 1024];
let mut store = Store::new_in(&mut arena);
store.set(b"temp", b"21.4")?;

コストと制約 arena は固定です。実行中に広げることはできません——「アロケータなし」とは、そういうことです。大きさを決めるのはエンジンではなく、あなたの設計です。機能の段階と、それぞれが何バイト要るのかは、IoT のガイドにあります。

次に

ガイド

WebAssembly の kevy

ブラウザ向けビルド、OPFS への永続化、そしてサイズの予算。

読む →

ガイド

組み込みリスナー

エンジンを組み込んだまま、ソケットで RESP を話す。

読む →

ガイド

IoT とベアメタル

no_std、arena、そして機能の段階。

読む →