分散SQLデータベースCockroachDBのキーバリューストレージのデバッグコマンドを試してみた

はじめに

LSM-TreeとRocksDB、TiDB、CockroachDBが気になる で紹介した CockroachDBWhat is CockroachDB? によるとスケールアウトできる分散SQLデータベースです。 PostgreSQLのワイヤープロトコルをサポート していて、 Quickstart の例のようにPostgreSQLで扱えるSQLのサブセットが使えます。

Overview によるとストレージには RocksDB を使用し、複数台のサーバ間の合意アルゴリズムにはRaftを使用しています。

分散SQLデータベースという本来の機能も魅力的なのですが、書き込みが多いケースに最適化したLSM Treeというデータ構造の実装であるRocksDBをRaftを使って分散トランザクションを実現しているという部分も個人的には興味があります。

ということで、そのへんのソースを見ていこうと思います。といっても、まだ全体を把握しているわけではないので、だらだら書いていきます。 CockroachDBにデバッグ用のコマンドが用意されていたので、それで実験しつつ読み進めたいと思います。

RocksDBラッパーレイヤとengineパッケージ

RocksDBはC++で書かれているので、Goから呼び出すためcgoでラッピングしているレイヤがあります。 cockroach/storage/engine/rocksdb にC++で書かれたファイルがいくつかあります。 cockroach/storage/engine パッケージのドキュメント engine - GoDoc にこのパッケージで低レベルのストレージを提供しているという説明があります。

Engine はRocksDBなどのストレージバックエンドとやり取りするためのインターフェースです。 Engineは ReadWriter インタフェースをエンベッドしていて、それがさらに ReaderWriter インタフェースをエンベッドしています。

Reader や Writer インタフェースのメソッドを見るとキーバリューストアのキーは MVCCKey という型になっています。

type MVCCKey struct {
    Key       roachpb.Key
    Timestamp hlc.Timestamp
}

roachpb.Key[]byte と定義されており、 hlc.Timestamp は以下のように定義されています。

type Timestamp struct {
    // Holds a wall time, typically a unix epoch time
    // expressed in nanoseconds.
    WallTime int64 `protobuf:"varint,1,opt,name=wall_time,json=wallTime" json:"wall_time"`
    // The logical component captures causality for events whose wall
    // times are equal. It is effectively bounded by (maximum clock
    // skew)/(minimal ns between events) and nearly impossible to
    // overflow.
    Logical int32 `protobuf:"varint,2,opt,name=logical" json:"logical"`
}

engine - GoDoc にEngineインタフェースの上にMVCC (Multi-Version Concurrency Control) システムが提供されていて、それがCockroachDBが分散トランザクションをサポートするための基礎になっていると書かれています。

その下の Notes on MVCC architecture にMVCCアーキテクチャについて詳細な説明があります。じっくり読んだほうが良いと思いますが、一旦飛ばして先に進みます。

RocksDB という構造体定義があり、これが Engine インタフェースを実装しています。 NewRocksDB 関数で RocksDB を作成できます。

NewRocksDB関数の呼び出し箇所

NewRocksDB関数は、テストコード以外では、以下の2箇所で呼ばれていました。

後者を呼び出している箇所を見ていくとデバッグ用のサブコマンドがあることがわかりました。

デバッグ用サブコマンドを試してみた

前提条件としてLXDの3つのコンテナroach1, roach2, roach3で以下のようにCockroachDBを起動している状態とします。

roach1

/usr/local/sbin/cockroach start --host 192.168.0.13 --insecure

roach2

/usr/local/sbin/cockroach start --join 192.168.0.13:26257 --insecure --host 192.168.0.14

roach3

/usr/local/sbin/cockroach start --join 192.168.0.13:26257 --insecure --host 192.168.0.15

特にコンテナでなくても1台のサーバで Quickstartのlocal clusterでも構いません。その場合は下記のコマンドの --host の部分を適宜読み替えてください。

debug kv コマンドを試してみた

debug kvコマンドで、キー・バリュー・ストアに値を設定したり取得したり出来ます。

コンテナroach1で値をセットして取得してみました。

root@roach1:~# cockroach debug kv scan --host 192.168.0.13
0 result(s)
root@roach1:~# cockroach debug kv put --host 192.168.0.13 foo bar
root@roach1:~# cockroach debug kv get --host 192.168.0.13 foo
"bar"
root@roach1:~# cockroach debug kv scan --host 192.168.0.13
"foo"   "bar"
1 result(s)

上記で設定した値がコンテナroach2でも取得できました。

root@roach2:~# cockroach debug kv scan --host 192.168.0.14
"foo"   "bar"
1 result(s)

コンテナroach2からroach1上の値を変更も出来ます。

root@roach2:~# cockroach debug kv put --host 192.168.0.13 foo 'Hello, key value store in CockroachDB'

コンテナroach1上の値一覧を取得して更新されていることを確認しました。

root@roach1:~# cockroach debug kv scan --host 192.168.0.13
"foo"   "Hello, key value store in CockroachDB"
1 result(s)

debug keys コマンドを試してみた

debug keysコマンドで、キー・バリュー・ストアの内部構造をダンプして見ることが出来ます。このコマンドはサーバを停止した状態でデータのディレクトリを指定して実行するようになっています。

サーバが起動したまま実行すると以下のようにロックが取得できないというエラーになります。

root@roach2:~# cockroach debug keys ./cockroach-data
Error: storage/engine/rocksdb.go:158: could not open rocksdb instance: IO error: lock ./cockroach-data/LOCK: Resource temporarily unavailable
Usage:
  cockroach debug keys [directory] [flags]

Flags:
      --from string
        Start key in pretty-printed format. See also --raw.

      --raw
        Interpret keys as raw bytes.

      --to string
        Exclusive end key in pretty-printed format. See also --raw.

      --values
        Print values along with their associated key.

Global Flags:
      --alsologtostderr value[=INFO]   logs at or above this threshold go to stderr (default NONE)
      --log-backtrace-at value         when logging hits line file:N, emit a stack trace (default :0)
      --log-dir value                  if non-empty, write log files in this directory
      --logtostderr                    log to standard error instead of files
      --no-color value                 disable standard error log colorization
      --verbosity value                log level for V logs
      --vmodule value                  comma-separated list of pattern=N settings for file-filtered logging

Failed running "debug"

そこでコンテナroach2のサーバを停止してみます。

root@roach2:~# cockroach quit --host 192.168.0.14

サーバを停止したらキーの一覧を表示してみます。以下の例では foo の前後5行を表示しています。 fooという文字列の後にタイムスタンプがついているのがわかります。

root@roach2:~# cockroach debug keys ./cockroach-data | grep -A 5 -B 5 foo
"/System/\"update-cluster\"/1466351519.447511853,0"
"/System/\"update-cluster\"/1466265107.436191749,0"
"/System/\"update-cluster\"/1466265097.406397710,0"
"/System/\"update-cluster\"/1466178687.396782782,0"
"/System/\"update-cluster\"/1466178677.619687555,85"
"\"foo\"/1467234744.564568969,0"
"\"foo\"/1467221373.376922221,0"
"/Table/2/1/0/\"bank\"/3/1/1466178749.722011447,0"
"/Table/2/1/0/\"system\"/3/1/1466178677.367397368,0"
"/Table/2/1/1/\"descriptor\"/3/1/1466178677.367397368,0"
"/Table/2/1/1/\"eventlog\"/3/1/1466178677.367397368,0"
"/Table/2/1/1/\"lease\"/3/1/1466178677.367397368,0"

--values オプションも追加すると、キーだけではなく値も表示されます。

cockroach debug keys --values cockroach-data/ | less

を実行して、 foo のキーに対応する部分を見てみると以下のようになっていました。横に長過ぎるので折り返して表示しています。

/Local/RangeID/21/u/RaftLog/logIndex:104861: Type:EntryNormal Term:51415 Index:104861 by {2 2 2} Put ["foo",/Min) range_id:21 origin_replica: cmd: replica: range_id:21 user_priority:NORMAL read_consistency:CONSISTENT trace: max_scan_results:0 distinct_spans:false > requests: value: > inline:false blind:false > > > max_lease_index:990

おわりに

CockroachDBのキーバリューストレージのデバッグコマンドを試してみました。対応するソースコードも読んでみたいところですが、 ArangoDB 3.0 – A Solid Ground to Scale – ArangoDB というニュースを知ったので、今後はArangoDBのほうを先に調べたいと思います。