たまたま急にオクテット列とオクテット列を対応させたくなったので、
今回は、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が必要だと思う根拠)。