Couchbaseに格納したドキュメントがどのvBucket／ノードにあるか調べる

# 概要
ドキュメント指向型 NoSQL である Couchbase を初めて触ってみました。目的としては、Couchbaseに格納したドキュメントがどのData Service ノードに格納されているのかを確認することです。CLI でやっていますが、特に理由は無いので作業はコンソールから行うほうが簡単です。couchbase-cli や cbq などをごちゃまぜで使ってしまっているので、もっとスマートな方法があるかと思います。

# vBuckets とは
Couchbase にドキュメントを格納するためには、バケットを作成する必要があります。このバケットですが、実際には 1024（macOSだと64）の [vBuckets](https://docs.couchbase.com/server/current/learn/buckets-memory-and-storage/vbuckets.html) に分割されており、それらが Data Service の稼働するノードに分散格納されています。
ドキュメントがどの vBucket／どのノードに格納されるかは「①ドキュメントのキー値をハッシュ化し vBucket 番号を算出」「②Cluster Manager が vBucket とノードをマッピング」というような流れで決まるようです。

# やったこと
### 概要
以下のような流れでやってみました。

* バケットを作成
* 適当なドキュメントを格納
* ドキュメントのキー値を取得
* キー値から格納先 vBucket／ノード情報を確認

### 環境
CentOS 上に Docker をインストールし、3ノードの Couchbase クラスタを構成して作業を行いました。コンテナ名はcb1~3とし、すべてで Data Service を有効化しています。

```bash
$ cat /etc/centos-release
CentOS Linux release 8.3.2011
$ docker -v
Docker version 20.10.6, build 370c289
$ docker exec cb1 couchbase-server -v
Couchbase Server 6.6.2-9588 (EE)
```

後続の作業は、コンテナcb1に接続して実施しています。コンテナとIPアドレスとの対応は以下の通りです。また、Couchbaseの操作は管理ユーザとして作成した Administrator（パスワード：password）で実行しています。

| コンテナ名 | IPアドレス |
|:-:|:-:|
| cb1 | 172.17.0.2 |
| cb2 | 172.17.0.3 |
| cb3 | 172.17.0.4 |

### バケットの作成
作業用バケットとして test-bucket を作成します。作成と確認は、[couchbase-cli](https://docs.couchbase.com/server/current/cli/cbcli/couchbase-cli.html) を使用して行いました。引数についてはマニュアルに記載があるので、詳細はそちらが参考になります。


```bash
# バケットの作成
$ couchbase-cli bucket-create \
  -c couchbase://172.17.0.2   \
  --bucket test-bucket        \
  --bucket-type couchbase     \
  --bucket-ramsize 256        \
  --bucket-replica 1          \
  --durability-min-level none \
  --wait

# 確認
$ couchbase-cli bucket-list -c couchbase://172.17.0.2
test-bucket
 bucketType: membase
 numReplicas: 1
 ramQuota: 805306368
 ramUsed: 61442848
```

### ドキュメントの格納
ここでは [cbq](https://docs.couchbase.com/server/current/cli/cbq-tool.html) を使用してデータを格納します。雑ですが、管理ユーザでやってしまっています。

```bash
# cbqで接続
$ cbq -e http://172.17.0.2:8091 -u Administrator -p password

# Insertを実行
cbq> insert into `test-bucket` (KEY, VALUE) values ("testKey", {"name": "Taro", "status": "working"});
```

### ドキュメントと vBucket の対応付け
格納したドキュメントがどの vBucket に格納されているかを確認するために、まずドキュメントのキー値を取得します。今回は Insert 実行時に "testKey" として明示指定していますが、例えば [cbimport](https://docs.couchbase.com/server/current/tools/cbimport.html) を使用すると #UUID# を指定してキー値を生成することができるため、別途確認を行う必要があります。その場合、以下のようにしてバケットの meta() から取得することができます。

```bash
# 存在しない場合、索引を作成（N1QLでは検索のために索引が必要）
# ここではPrimary Indexを作成
cbq> create primary index test_prim_idx on `test-bucket`;

# バケットのメタ情報からidを取得
cbq> select meta(t).id from `test-bucket` t;
  ：
    "results": [
    {
        "id": "testKey"
    }
  ：
```

キー値がわかったら [cbc hash コマンド](https://docs.couchbase.com/sdk-api/couchbase-c-client-2.8.5/md_doc_cbc.html) を使用することで、ドキュメントがどの vBucket ／ノードに格納されているのかを確認することができます。

```bash
$ cbc hash testKey -U couchbase://localhost/test-bucket -u Administrator -P password
testKey: [vBucket=705, Index=2] Server: 172.17.0.4:11210, CouchAPI: http://172.17.0.4:8092/test-bucket
Replica #0: Index=0, Host=172.17.0.2:11210
```

これより、以下ということがわかります。

| 項目 | 値 |
|:-:|:-:|
| 格納先の vBucket 番号 | 705 |
| アクティブ・データ | cb3（172.17.0.4） |
| レプリカ・データ | cb1（172.17.0.2） |

以上から、ドキュメントとその格納先である vBucket を対応付けることができました。
なお、cbc hash コマンドは実行時に指定したキー値からハッシュ値を算出し、それに基づいてデータの格納先を返却します。そのため、現時点で存在しない（例えば今後格納予定の）キー値を指定し、確認することもできます。

最後に、念のためcbc hash コマンドで取得した vBucket 番号 705 の格納ドキュメント数などの情報を参照してみます。確認には [cbstats](https://docs.couchbase.com/server/current/cli/cbstats-intro.html) を用いました。

```bash
# アクティブ・データのあるcb3に対して実行
$ cbstats -u Administrator -p password -b test-bucket 172.17.0.4:11210 vbucket-details 705 | \
  grep -E "num_items|topology|vb_705:\s"
vb_705:                                 active
vb_705:num_items:                       1
vb_705:topology:                        [["ns_1@172.17.0.4","ns_1@172.17.0.2"]]

# レプリカ・データのあるcb1に対して確認
$ cbstats -u Administrator -p password -b test-bucket 172.17.0.2:11210 vbucket-details 705 | \
  grep -E "num_items|topology|vb_705:\s"
vb_705:                                 replica
vb_705:num_items:                       1
vb_705:topology:                        null
```

実行結果は 3 つの値のみにフィルタしています。num_items が 1 となっており、active/replica の値も cbc hash の実行結果と一致しているので、上の確認方法で正しそうです。topology （active, replica のノード情報？）はアクティブ・データのあるノードのみから参照できるようです。

以上です。