1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RustのSledを使ってとにかくかんたんにキーバリューストアしてみた

Last updated at Posted at 2024-05-29

たまたま急にオクテット列とオクテット列を対応させたくなったので、
今回は、Rustで使えるシンプルで高性能なキーバリューストアのSledというのを試してみました。

Sledについて

Sledは高性能な埋め込みデータベースです。

  • スレッドセーフでアトミックな操作
  • 複数の独立したキー空間を持つツリー
  • ACIDトランザクションやキーの更新監視
  • 効率的にデータを統合

とにかく使ってみる

Cargo.tomlはこんな感じです。

Cargo.toml
[package]
name="handson-sled-sync"
version="0.0.0"
edition="2021"

[dependencies.sled]
version="^0.34.7"

今回はsledだけに依存します。たいへんシンプル。

開く・閉じる

まずはここからです。

src/main.rs
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

挿入・取得・削除

まずは基礎の基礎として、挿入、取得、削除をやってみます。

src/main.rs
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で取得してみると、キーには再び何もないことが確認できました。

トランザクション

途中でやめたりやめなかったりできます。

main.rs
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してみてはいるんですけどDbstd::ops::Dropを直接は実装していないようです。(db.flushが必要だと思う根拠)

Configのところに書いてある例ではflush_every_msという設定があったようです(db.flushが必要ないと思う根拠)、が、今はなさそうです(db.flushが必要だと思う根拠)。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?