Rustã§æ›¸ã‹ã‚ŒãŸKey-Valueストア CannyLS ã®åŸºæœ¬æ©Ÿèƒ½ã‚’ã€ã‚³ãƒžãƒ³ãƒ‰ãƒ©ã‚¤ãƒ³ã‹ã‚‰ç”¨ã„ã‚‹ãŸã‚ã®ãƒ„ールã§ã™ã€‚
- CannyLSã¯Key-Valueストアã§ã‚りã€Keyã¯ç¬¦å·ãªã—128bité•·æ•´æ•°ã§ã€Valueã¯ãƒã‚¤ãƒˆåˆ—
- Key-Valueã®çµ„ã¯lumpã¨å‘¼ã°ã‚Œã¦ã„る。詳細ã«ã¤ã„ã¦ã¯ã“ã¡ã‚‰ã¸ ↓
https://github.com/frugalos/cannyls/wiki/Terminology#lump
- Key-Valueã®çµ„ã¯lumpã¨å‘¼ã°ã‚Œã¦ã„る。詳細ã«ã¤ã„ã¦ã¯ã“ã¡ã‚‰ã¸ ↓
- CannyLSã¯ã‚¹ãƒ
8000
ˆãƒ¬ãƒ¼ã‚¸ã®æ•´åˆæ€§ã‚’ä¿ã¤ãŸã‚ã«ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«æ©Ÿèƒ½ã‚’æŒã¤
(ジャーナリングファイルシステムã§ã„ã†ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«ã®ã‚ˆã†ãªæ„Ÿã˜ï¼‰- CannyLSã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ãƒ•ァイル全体ã¯
- Key-Valueをストアã™ã‚‹ãŸã‚ã®ã€Œãƒ‡ãƒ¼ã‚¿é ˜åŸŸã€ã¨
- ジャーナル機能を実ç¾ã™ã‚‹ãŸã‚ã®ã€Œã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã€ã®ï¼’ã¤ã‹ã‚‰æˆã‚Šç«‹ã£ã¦ã„ã‚‹
- ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã®è©³ç´°ã«ã¤ã„ã¦ã¯ã“ã¡ã‚‰ã¸â†“
https://github.com/frugalos/cannyls/wiki/Journal-Region
- CannyLSã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ãƒ•ァイル全体ã¯
- CannyLSã¯ãƒ–ãƒãƒƒã‚¯ã‚µã‚¤ã‚ºæ›¸ãè¾¼ã¿ã‚’行ã†
- KaniLSã§ã¯1ブãƒãƒƒã‚¯512ãƒã‚¤ãƒˆã ã¨æ€ã£ã¦è‰¯ã„
- 例1. 10ãƒã‚¤ãƒˆã®ãƒ‡ãƒ¼ã‚¿ã‚’書ã込む際ã«ã¯ã€512ãƒã‚¤ãƒˆ(1ブãƒãƒƒã‚¯)ã¸ã®åˆ‡ã‚Šä¸Šã’ãŒèµ·ã“りã€512ãƒã‚¤ãƒˆæ›¸ãè¾¼ã¿ãŒç™ºç”Ÿã™ã‚‹
- 例2. 513ãƒã‚¤ãƒˆã®ãƒ‡ãƒ¼ã‚¿ã‚’書ã込む際ã«ã¯ã€1024ãƒã‚¤ãƒˆ(2ブãƒãƒƒã‚¯)ã¸ã®åˆ‡ã‚Šä¸Šã’ãŒèµ·ã“りã€1024ãƒã‚¤ãƒˆæ›¸ãè¾¼ã¿ãŒç™ºç”Ÿã™ã‚‹
- 技術的ã«ã¯ã€Linuxã®å®Ÿè£…ã§
O_DIRECT
を用ã„ãŸèªã¿æ›¸ãを行ã†ã“ã¨ã«èµ·å› ã—ã¦ã„ã‚‹
(詳ã—ã㯠https://github.com/frugalos/cannyls/wiki/Terminology#ブãƒãƒƒã‚¯ ã‚’å‚照)
KaNiLSã¯Rustã§æ›¸ã‹ã‚Œã¦ã„ã¾ã™ã€‚
Rustã®é–‹ç™ºç’°å¢ƒãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ãªã„å ´åˆã¯ rustup ãªã©ã‚’用ã„ã¦ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãã ã•ã„。
(å‚考: https://www.rust-lang.org/ja-JP/install.html)
$ git clone https://github.com/frugalos/kanils
$ cd kanils
kanils$ cargo build # ã“ã®å ´åˆã¯ target/debug ã« kanilsãƒã‚¤ãƒŠãƒªãŒã§ãã¾ã™
kanils$ cargo build --release # ã“ã®å ´åˆã¯ target/release ã« kanilsãƒã‚¤ãƒŠãƒªãŒã§ãã¾ã™
$ cargo install kanils
- Create -- ストレージファイル作æˆ
kanils Create --storage=storage_path --capacity=num
storage_path
ã«ã€num
ãƒã‚¤ãƒˆã‚’ãƒ‡ãƒ¼ã‚¿é ˜åŸŸã«ã‚‚ã¤cannylsストレージファイル(lusfファイルã¨å‘¼ã¶ï¼‰ãŒä½œæˆã•れる
- Put -- Key-Valueペアã®è¿½åŠ ï¼ˆä¸Šæ›¸ã)
kanils Put --storage=storage_path --key=num(128bit) --value=string
storage_path
ã®lusfファイルã«ã€key-valueペア<num, string>
を追åŠ- æ—¢ã«key
num
ãŒå˜åœ¨ã™ã‚‹å ´åˆã¯ä¸Šæ›¸ããŒè¡Œã‚れる
- Get -- Keyã«ã‚ˆã‚‹Key-Valueペアã®å–å¾—
kanils Get --storage=storage_path --key=num(128bit)
storage_path
ã®lusfファイルä¸ã®ãƒ‡ãƒ¼ã‚¿ã‚’keynum
を用ã„ã¦èªã¿è¾¼ã‚€
- Delete -- Keyã«ã‚ˆã‚‹Key-Valueペアã®å‰Šé™¤
kanils Delete --storage=storage_path --key=num(128bit)
storage_path
ã®lusfファイルä¸ã®ãƒ‡ãƒ¼ã‚¿ã‚’keynum
を用ã„ã¦å‰Šé™¤ã™ã‚‹
- Header -- lusfファイルã®ãƒ˜ãƒƒãƒ€æƒ…å ±ã‚’å–得(ストレージもã‚ã‚‚ã‚ã®æƒ…å ±ãŒåˆ†ã‹ã‚‹ï¼‰
kanils Header --storage=storage_path
- Dump -- lusfファイルã®ãƒ‡ãƒ¼ã‚¿é ˜åŸŸã‚’å–å¾—
kanils Dump --storage=storage_path
- Journal -- lusfファイルã®ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã‚’å–å¾—
kanils Journal --storage=storage_path
- JournalGC -- lusfファイルã®ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã«å¯¾ã™ã‚‹GCを実行
kanils JournalGC --storage=storage_path
- Open -- ファイルオープン
kanils Open --storage=storage_path
- å˜åœ¨ã™ã‚‹lusfファイル
storage_path
ã‚’é–‹ãã€å¯¾è©±ãƒ¢ãƒ¼ãƒ‰ã«å…¥ã‚‹ - 対話モードã§ä½¿ç”¨ã§ãるコマンドã¯
put key value
,get key
,delete key
,dump
,header
,journal
,journal_gc
# 2048ãƒã‚¤ãƒˆã‚’ãƒ‡ãƒ¼ã‚¿é ˜åŸŸã«å‰²ã‚Šå½“ã¦ã‚‹ã‚ˆã†ãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ãƒ•ァイルを作æˆ
$ ./kanils Create --storage demo.lusf --capacity 2048
passed data region size = 2048
---------------
actual data region size = 2048
actual journal size = 1536
actual journal size ratio = 0.42857142857142855
# ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã®æ§˜ã€…ãªæƒ…å ±ã‚’ç¢ºèª
$ ./kanils Header --storage demo.lusf
header =>
major version = 1
minor version = 1
block size = 512
uuid = 731d2970-b03f-4f1b-9da8-8b4617ace5fc
journal region size = 1536 // ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸå…¨ä½“ã®ã‚µã‚¤ã‚ºã¯ä»¥ä¸‹ï¼’ã¤ã‹ã‚‰ãªã‚‹
journal header size = 512 // ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã®ãƒ¡ã‚¿æƒ…å ±ã‚’æ ¼ç´ã™ã‚‹ãƒ˜ãƒƒãƒ€éƒ¨åˆ†
journal record size = 1024 // ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«ã‚¨ãƒ³ãƒˆãƒªã‚’å®Ÿéš›ã«æ›¸ã込む部分
data region size = 2048
storage header size => 512
storage total size = 4096
# (key=42, value="test_string")ã®çµ„をストレージã«put
$ ./kanils Put --storage demo.lusf --key 42 --value test_string
put key=42, value=test_string
# (key=7, value="🦀")ã®çµ„をストレージã«put
$ ./kanils Put --storage demo.lusf --key 7 --value 🦀
put key=7, value=🦀
# ç¾åœ¨ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ä¸ã®ãƒ‡ãƒ¼ã‚¿é ˜åŸŸã‚’ダンプ
$ ./kanils Dump --storage demo.lusf
<lump list>
(LumpId("00000000000000000000000000000007"), "🦀")
(LumpId("0000000000000000000000000000002a"), "test_string")
</lump list>
# ç¾åœ¨ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ä¸ã®ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã‚’ダンプ
$ ./kanils Journal --storage demo.lusf
journal [unreleased head] position = 0
journal [head] position = 0
journal [tail] position = 56
<journal entries>
JournalEntry { start: Address(0), record: Put(LumpId("0000000000000000000000000000002a"), DataPortion { start: Address(0), len: 1 }) }
JournalEntry { start: Address(28), record: Put(LumpId("00000000000000000000000000000007"), DataPortion { start: Address(1), len: 1 }) }
</journal entries>
# key=42ã‚’æŒã¤ãƒ‡ãƒ¼ã‚¿ã‚’削除
$ ./kanils Delete --storage demo.lusf --key 42
delete result => true
# 削除ã•れãŸã‹ã©ã†ã‹ã‚’確èª
$ ./kanils Dump --storage demo.lusf
<lump list>
(LumpId("00000000000000000000000000000007"), "🦀")
</lump list>
# ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã‚’ç¢ºèª
## ジャーナルファイルシステムã¨åŒã˜æ§˜ã«ã€ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã«ã¯æ“作を記録ã—ã¦ã„ãã®ã§ã€
## PUTã«å¯¾ã™ã‚‹DELETEã®å ´åˆã§ã‚‚ã€ãƒ‡ãƒ¼ã‚¿é ˜åŸŸã®ã‚ˆã†ã«æ‰“ã¡æ¶ˆã—åˆã£ã¦æ¶ˆãˆã‚‹ã“ã¨ã¯ãªã„
$ ./kanils Journal --storage demo.lusf
journal [unreleased head] position = 0
journal [head] position = 0
journal [tail] position = 77
<journal entries>
JournalEntry { start: Address(0), record: Put(LumpId("0000000000000000000000000000002a"), DataPortion { start: Address(0), len: 1 }) }
JournalEntry { start: Address(28), record: Put(LumpId("00000000000000000000000000000007"), DataPortion { start: Address(1), len: 1 }) }
JournalEntry { start: Address(56), record: Delete(LumpId("0000000000000000000000000000002a")) }
</journal entries>
# ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã¸ã®GC
## ãŸã ã—ã€ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«é ˜åŸŸã¸ã®GCを実行ã™ã‚‹ã“ã¨ã§ã€ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«å´ã®æƒ…å ±ã®ã†ã¡ã€å‰Šé™¤ã—ã¦ã‚‚å•題ãŒãªã„ã“ã¨ãŒåˆ†ã‹ã£ã¦ã„ã‚‹ã‚‚ã®ã‚’消ã™ã“ã¨ãŒã§ãる。
## 削除ã—ã¦ã‚‚å•題ãŒãªã„ジャーナルエントリ(もã—ãã¯GC対象ã«ãªã‚‹ã‚¸ãƒ£ãƒ¼ãƒŠãƒ«ã‚¨ãƒ³ãƒˆãƒªï¼‰ã«ã¤ã„ã¦ã®è©³ç´°ã¯
## https://github.com/frugalos/cannyls/wiki/Journal-Region-GC ã‚’å‚ç…§
$ ./kanils JournalGC --storage demo.lusf
run journal full GC ...
journal full GC succeeded!
$ ./kanils Journal --storage demo.lusf
journal [unreleased head] position = 77
journal [head] position = 77
journal [tail] position = 105
<journal entries>
JournalEntry { start: Address(77), record: Put(LumpId("00000000000000000000000000000007"), DataPortion { start: Address(1), len: 1 }) }
</journal entries>
$ ./kanils Put --storage demo.lusf --key 100 --value x
put key=100, value=x
$ ./kanils Put --storage demo.lusf --key 200 --value y
put key=200, value=y
$ ./kanils Put --storage demo.lusf --key 300 --value z
put key=300, value=z
# 5ä»¶ç›®ã®ãƒ‡ãƒ¼ã‚¿ã‚’書ã込もã†ã¨ã™ã‚‹ã¨ã‚¨ãƒ©ãƒ¼ã«ãªã‚‹ã€‚
# ã“れã¯2048ãƒã‚¤ãƒˆã‚’ãƒ‡ãƒ¼ã‚¿é ˜åŸŸã«ç¢ºä¿ã—ã¦ãŠã‚Šã€ã‹ã¤512ãƒã‚¤ãƒˆã‚’1書ãè¾¼ã¿ã«ä½¿ã£ã¦ã„ã‚‹ã‹ã‚‰ã§ã‚る。
$ ./kanils Put --storage demo.lusf --key 400 --value o
thread 'main' panicked at '
EXPRESSION: self.storage.put(&lump_id, &lump_data)
ERROR: StorageFull (cause; assertion failed: `self.allocator.allocate(block_size).is_some()`)
HISTORY:
[0] at /Users/ferris/.cargo/git/checkouts/cannyls-3a0f9a30cf1773f1/281ae5b/src/storage/data_region.rs:58
[1] at /Users/ferris/.cargo/git/checkouts/cannyls-3a0f9a30cf1773f1/281ae5b/src/storage/mod.rs:350
[2] at /Users/ferris/.cargo/git/checkouts/cannyls-3a0f9a30cf1773f1/281ae5b/src/storage/mod.rs:206
[3] at src/main.rs:165
', src/main.rs:165:22
note: Run with `RUST_BACKTRACE=1` for a backtrace.
--storage storage_path
ã‚’é€ä¸€æŒ‡å®šã™ã‚‹ã®ãŒé¢å€’ãªå ´åˆã¯ã€Create
ã—ãŸå¾Œã«Open
ã™ã‚‹ã¨è‰¯ã„。
上ã®ä¸€é€£ã®ä½œæ¥ã¯ã€å¯¾è©±ãƒ¢ãƒ¼ãƒ‰ã§ã¯æ¬¡ã®ã‚ˆã†ã«ãªã‚‹:
$ ./kanils Create --storage demo.lusf --capacity 2048
passed data region size = 2048
---------------
actual data region size = 2048
actual journal size = 1536
actual journal size ratio = 0.42857142857142855
$ ./kanils Open --storage demo.lusf
>> put 42 test_string
put key=42, value=test_string
>> put 7 🦀
put key=7, value=🦀
>> dump
<lump list>
(LumpId("00000000000000000000000000000007"), "🦀")
(LumpId("0000000000000000000000000000002a"), "test_string")
</lump list>
>> journal
journal [unreleased head] position = 0
journal [head] position = 0
journal [tail] position = 56
<journal entries>
JournalEntry { start: Address(0), record: Put(LumpId("0000000000000000000000000000002a"), DataPortion { start: Address(0), len: 1 }) }
JournalEntry { start: Address(28), record: Put(LumpId("00000000000000000000000000000007"), DataPortion { start: Address(1), len: 1 }) }
</journal entries>
>> delete 42
delete result => true
>> dump
<lump list>
(LumpId("00000000000000000000000000000007"), "🦀")
</lump list>
>> journal
journal [unreleased head] position = 0
journal [head] position = 0
journal [tail] position = 77
<journal entries>
JournalEntry { start: Address(0), record: Put(LumpId("0000000000000000000000000000002a"), DataPortion { start: Address(0), len: 1 }) }
JournalEntry { start: Address(28), record: Put(LumpId("00000000000000000000000000000007"), DataPortion { start: Address(1), len: 1 }) }
JournalEntry { start: Address(56), record: Delete(LumpId("0000000000000000000000000000002a")) }
</journal entries>
>> journal_gc
run journal full GC ...
journal full GC succeeded!
>> journal
journal [unreleased head] position = 77
journal [head] position = 77
journal [tail] position = 105
<journal entries>
JournalEntry { start: Address(77), record: Put(LumpId("00000000000000000000000000000007"), DataPortion { start: Address(1), len: 1 }) }
</journal entries>
>> put 100 x
put key=100, value=x
>> put 200 y
put key=200, value=y
>> put 300 z
put key=300, value=z
>> put 400 o
thread 'main' panicked at '
EXPRESSION: self.storage.put(&lump_id, &lump_data)
ERROR: StorageFull (cause; assertion failed: `self.allocator.allocate(block_size).is_some()`)
HISTORY:
[0] at /Users/ferris/.cargo/git/checkouts/cannyls-3a0f9a30cf1773f1/281ae5b/src/storage/data_region.rs:58
[1] at /Users/ferris/.cargo/git/checkouts/cannyls-3a0f9a30cf1773f1/281ae5b/src/storage/mod.rs:350
[2] at /Users/ferris/.cargo/git/checkouts/cannyls-3a0f9a30cf1773f1/281ae5b/src/storage/mod.rs:206
[3] at src/main.rs:165
', src/main.rs:165:22
note: Run with `RUST_BACKTRACE=1` for a backtrace.
# test.lusfã¨ã„ã†ãƒ•ァイルを作り
# 1ä»¶3MBã®ãƒ‡ãƒ¼ã‚¿ã‚’1000ä»¶ã‚·ãƒ¼ã‚±ãƒ³ã‚·ãƒ£ãƒ«ã«æ›¸ãè¾¼ã¿ã€
# ãã®å¾Œã«1000件をランダムã«GETã™ã‚‹ã€‚
kanils RandomGetBench --storage test.lusf --count 1000 --size 3MB
以下ã¯å‡ºåŠ›ã®ä¾‹
[src/bench.rs:91] path.clone() = "test.lusf"
[src/bench.rs:91] count = 1000
[src/bench.rs:91] size = 3145728
[Putting Data] start
[00:00:03] [########################################] 1000/1000 (0s, done)
[Putting Data] finish @ 3s 507ms
[Getting Data] start
[00:00:03] [########################################] 1000/1000 (0s, done)
[Getting Data] finish @ 3s 539ms