sledというRust製のNoSQLなライブラリ(?)触ってみた

## はじめに

状態を保っていてcrudな操作をするようなcliをRustで作ろうとした時に、
sqliteなど外部ツールに依存するのもアレだなと思って、
パス1個渡すだけで、ファイルなりにcrudな操作ができる、DBみたいなライブラリがあれば良いなと思って調べていました。

すると、[sled](https://github.com/spacejam/sled)という組み込み向けのそれっぽいライブラリがあり、
スターが1800くらい付いていて、
サンプルを見た感じ、path1個通すだけで済んでcrudな操作も出来そうなので、少し触ってみました。

*載せてる公式のサンプルコードやライブラリのAPIは2019/03/10現在のものです

```rust:公式のgithubに書いてあるサンプルコード
// https://github.com/spacejam/sled#sled
use sled::Db;

let tree = Db::start_default(path)?;

// set and get
tree.set(k, v1);
assert_eq!(tree.get(&k), Ok(Some(v1)));

// compare and swap
tree.cas(k, Some(v1), Some(v2));

// scan forward
let mut iter = tree.scan(k);
assert_eq!(iter.next(), Some(Ok((k, v2))));
assert_eq!(iter.next(), None);

// deletion
tree.del(&k);

// block until all operations are on-disk
tree.flush();
```

# 触ってみる
↓dependenciesにsledを追加する

```toml:cargo.toml
# ...省略
[dependencies]
sled = "0.19.0"
```

↓ストレージ用のディレクトリを作成する

```diff
// projectのルートに作る
$ mkdir test_storage
$ tree -L 1
  .
  ├── Cargo.lock
  ├── Cargo.toml
  ├── src
  ├── target
+ └── test_storage
```

↓こんな感じにmain.rsを書いて

```rust:main.rs
extern crate sled;

use sled::Db;

fn main() {
    let path = "test_storage";
    let tree = Db::start_default(path).unwrap();
}
```

cargo runする
（test_storage以下にそれっぽいファイルとかが増えていればひとまず動いていそう）

```diff
$ cargo run
$ tree -L 2
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
├── target
│   └── debug
└── test_storage
+    ├── blobs
+    ├── conf
+    ├── db
+    └── snap.0000000000000000
```


setしてgetするだけの単純なコードを書いてみる

```rust:main.rs
extern crate sled;

use sled::Db;

fn main() {
    let dir_path = "test_storage";
    let tree = Db::start_default(dir_path).unwrap();

    let key = "hoge";
    let value = "abcdefg";

    // `API similar to a threadsafe BTreeMap<Vec<u8>, Vec<u8>>` とのこと。
    // tree.setのvalueのtypeも `type Value = Vec<u8>;`
    // を要求しているので、`as_bytes().to_vec()`を噛ませる
    tree.set(&key, value.as_bytes().to_vec())
        .expect("set fail");

    match tree.get(&key) {
        Ok(Some(res)) => {
            let converted: String = String::from_utf8(res.to_vec()).unwrap();
            assert_eq!(value,converted);
            println!("{}", converted); // abcdefg
        }
        _ => {}
    }
}
```


公式のサンプルにも書いてあるが、set,get以外にもupdate(swap)したり、scanしたり、deleteしたり、簡単な操作は一通りできるよう。

```rust

// compare and swap
    tree.cas(k, Some(v1), Some(v2));

// scan forward
    let mut iter = tree.scan(k);
    assert_eq!(iter.next(), Some(Ok((k, v2))));
    assert_eq!(iter.next(), None);

// deletion
    tree.del(&k);

// block until all operations are on-disk
    tree.flush();
}
```


## 良いところ、イマイチなところ
### sledの良いところ
- ディレクトリのパス1個渡すだけで、使える
- sqliteなど使わずにrust単体でちょっとしたツールが作れそう


### sledのイマイチなところ
- 2019/03/10時点でまだアルファ版
- 32bit環境はnightly版で動かさないといけなさそう
    - [32 bit architectures require Rust nightly with the nightly feature enabled.](https://github.com/spacejam/sled/issues/145)
- test_storageの中にdbというファイルが生成され、雰囲気的にこのファイルにガリガリ書き込んでいく様子
    - append onlyなのか`tree.flush()`を走らせても中身は削除されず、tree.set()のたびにファイルがどんどん大きくなるようだった。
    - （もしかしたら、そういうものなのかもしれない。）
    

### まとめ
まだアルファ版なのと、setするとひたすらでかくなるファイルが気になりますが、
ディレクトリのパス1個だけ渡すだけですぐに使えるのと、
rust単体で完結しそうなのでポータビリティ高めなツールが作れそうな気がしました。