たまたま急にオクテット列とオクテット列を対応させたくなったので、
今回は、Rustで使えるシンプルで高性能なキーバリューストアのSledというのを試してみました。
Sledについて
Sledは高性能な埋め込みデータベースです。
- スレッドセーフでアトミックな操作
- 複数の独立したキー空間を持つツリー
- ACIDトランザクションやキーの更新監視
- 効率的にデータを統合
とにかく使ってみる
Cargo.toml
はこんな感じです。
[package]
name="handson-sled-sync"
version="0.0.0"
edition="2021"
[dependencies.sled]
version="^0.34.7"
今回はsled
だけに依存します。たいへんシンプル。
開く・閉じる
まずはここからです。
use std::error::Error;
fn main(
)->Result<(),Box<dyn Error>>{
let db=sled::open("db")?;
db.flush()?;
Ok(())
}
sled::open("db")
で開きます。
閉じる前にはdb.flush()
が(生成AI的には)おすすめのようですが、今回は無くても同じようなフォルダの構造はできていました。 でもやっぱり 生成AIに期待して、 心配なので、今回はちゃんとdb.flush()
することにします……。だって見た感じstd::ops::Drop
を実装している気配がなくて怖いんだもん。
ちなみにこんな構造のフォルダができました。
db/
|-- blobs/
|-- conf
`-- db
挿入・取得・削除
まずは基礎の基礎として、挿入、取得、削除をやってみます。
use std::error::Error;
fn main(
)->Result<(),Box<dyn Error>>{
let db=sled::open("db")?;
dbg!(db.get(b"")?);
db.insert(
b"",
b"",
)?;
dbg!(db.get(b"")?);
db.remove(b"")?;
dbg!(db.get(b"")?);
db.flush()?;
Ok(())
}
これを実行するとこうなりました。
[src/main.rs:7:2] db.get(b"")? = None
[src/main.rs:12:2] db.get(b"")? = Some(
[],
)
[src/main.rs:14:2] db.get(b"")? = None
キーとして空のオクテット列b""
を使います。
最初にdb.get
で取得してみると、キーになにもないことが確認できました。
次にdb.insert
でキーに空のオクテット列b""
を対応させます。
今度はdb.get
で取得してみると、キーに空のオクテット列b""
があることを確認できました。
次にdb.remove
でキーを削除してみます。
今度はdb.get
で取得してみると、キーには再び何もないことが確認できました。
トランザクション
途中でやめたりやめなかったりできます。
use std::error::Error;
use sled::transaction::ConflictableTransactionError;
use sled::transaction::TransactionError;
fn main(
)->Result<(),Box<dyn Error>>{
let db=sled::open("db")?;
dbg!(db.get(b"")?);
let _:Result<(),TransactionError<&str>>=db.transaction(
|tree|{
tree.insert(
b"",
b"",
)?;
Err(ConflictableTransactionError::Abort("something wrong"))
}
);
dbg!(db.get(b"")?);
let _:Result<(),TransactionError<&str>>=db.transaction(
|tree|{
tree.insert(
b"",
b"",
)?;
Ok(())
}
);
dbg!(db.get(b"")?);
db.flush()?;
Ok(())
}
これを実行するとこうなりました。
[src/main.rs:8:2] db.get(b"")? = None
[src/main.rs:18:2] db.get(b"")? = None
[src/main.rs:28:2] db.get(b"")? = Some(
[],
)
まずはトランザクションを途中でやめてみます。ただやめたいというだけでやめられます。
let _:Result<(),TransactionError<&str>>=db.transaction(
|tree|{
tree.insert(
b"",
b"",
)?;
Err(ConflictableTransactionError::Abort("something wrong"))
}
);
このとき、中のtree.insert
は起こってほしくないですが、ちゃんと起こってないことが確認できています。
次はほぼ同じクロージャーでOk(())
を返してみます。今度はちゃんと反映されていることが確認できました。
結論
ご覧の通り、オクテット列をオクテット列に対応させる程度の目的で、ちょっと使うだけなら記事一枚に収まる簡単さです。
これなら急にオクテット列とオクテット列を対応させたくなってもすぐになんとかできますね✨
付録
閉じる前のdb.flush
本当に必要か問題
結論としてはわからなかったです。
やっぱり生成AIにの主張は検証すべきというのが正論なのでちょっと調べてみました。
一応ちゃんとソースコードをgrep
してみてはいるんですけどDb
はstd::ops::Drop
を直接は実装していないようです。(db.flush
が必要だと思う根拠)
Config
のところに書いてある例ではflush_every_ms
という設定があったようです(db.flush
が必要ないと思う根拠)、が、今はなさそうです(db.flush
が必要だと思う根拠)。