Amazon QLDBのチュートリアルをやってみる

Amazon QLDBがGAになったので、公式チュートリアルに沿って触ってみます。
チュートリアルのシナリオは、車両登録を台帳で管理するというものになっています。
[Getting Started with the Amazon QLDB Console - Amazon Quantum Ledger Database (Amazon QLDB)](https://docs.aws.amazon.com/ja_jp/qldb/latest/developerguide/getting-started.html)

## 台帳の作成
<img width="1168" alt="スクリーンショット_2019-09-11_11_05_30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/1df0cbae-9a64-ead8-d54d-21ca7941ffd2.png">


台帳名はサンプル提供の名前が入力されているので、そのまま「台帳の作成」をクリックします。
ステータスは1分も経たずに作成中からアクティブになりました。

<img width="1181" alt="スクリーンショット 2019-09-11 11.02.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/970ad97d-29d5-568b-88ba-957a2ccfeee9.png">


## サンプルデータの入力
プルダウンから先程作成した台帳を選択し、「サンプルデータをロード」をクリックします。サンプルデータがロードされます。

<img width="1176" alt="スクリーンショット 2019-09-11 11.04.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/a9706487-a6b0-f345-a5cc-ba3de1bd6f08.png">

## データの取得
ドキュメントに沿って、クエリを作成してデータを取得します。
[Step 3: Query the Tables in a Ledger - Amazon Quantum Ledger Database (Amazon QLDB)](https://docs.aws.amazon.com/ja_jp/qldb/latest/developerguide/getting-started-step-3.html)

Vehicleテーブルからデータを取得します。

```
SELECT * FROM Vehicle AS v WHERE v.VIN = '1N4AL11D75C109151'
```

SQLを実行すると、結果のタブに取得結果が表示されました。

<img width="845" alt="スクリーンショット 2019-09-11 11.16.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/15b67047-8fde-2e64-98be-4e6334093419.png">


テーブルを結合してみます。

```
SELECT v.VIN, r.LicensePlateNumber, r.State, r.City, r.Owners
FROM Vehicle AS v, VehicleRegistration AS r
WHERE v.VIN = '1N4AL11D75C109151'
AND v.VIN = r.VIN
```

<img width="846" alt="スクリーンショット 2019-09-11 11.18.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/719f4482-ef1c-66b4-c307-a1c8064e345d.png">


## データの変更
以下のシナリオを想定してデータを変更していきます。

1. シアトル在住のRaul LewisがAudi A5を所有している
2. Raulがエベレット在住のBrent Loganに車を売る
3. BrentとAlexis Penaが結婚する
4. Alexisを車の副所有者として登録する

まずはPersonテーブルからRaulのドキュメントIDを取得します。
`commited view`というシステムが定義したテーブルから取得できます。
テーブルの頭に`_ql_committed_`というプレフィックスが付きます。

```
SELECT metadata.id FROM _ql_committed_Person AS p
WHERE p.data.FirstName = 'Raul' and p.data.LastName = 'Lewis'
```

<img width="841" alt="スクリーンショット 2019-09-11 12.06.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/61ce9967-84c2-ca98-290b-895b2cf6c8c4.png">


ベストプラクティスとして、外部キーはこのメタデータのIDを利用します。
**IDはシステムが自動採番するため、自身の環境で取得できたものに適宜置き換えて下さい。**

取得できたIDをPersonIdとして、車の所有者を書き換えます。

```
UPDATE VehicleRegistration AS r
SET r.Owners.PrimaryOwner.PersonId = '77o9bWOlQL1HG0Vv7p726n' --replace with your id
WHERE r.VIN = '1N4AL11D75C109151'
```

修正できたことを確認します。

```
SELECT r.Owners FROM VehicleRegistration AS r
WHERE r.VIN = '1N4AL11D75C109151'
```

<img width="841" alt="スクリーンショット 2019-09-11 12.07.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/51d40291-53f7-f58f-bd69-8e1079b3acba.png">

次に、車の売り先のBrentのIDを取得します。

```
SELECT metadata.id FROM _ql_committed_Person AS p
WHERE p.data.FirstName = 'Brent' and p.data.LastName = 'Logan'
```

<img width="842" alt="スクリーンショット 2019-09-11 12.08.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/3fbaa7c4-7e79-1762-b8da-2f27a91a9d3f.png">

取得したIDで所有者を更新します。

```
UPDATE VehicleRegistration AS r
SET r.Owners.PrimaryOwner.PersonId = 'CX9hTfs8G83LM9WNVRxpqd', --replace with your id
r.City = 'Everett'
WHERE r.VIN = '1N4AL11D75C109151'
```

変更されたことを確認します。

```
SELECT r.Owners.PrimaryOwner, r.City
FROM VehicleRegistration AS r
WHERE r.VIN = '1N4AL11D75C109151'
```

<img width="842" alt="スクリーンショット 2019-09-11 12.17.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/e6af350f-0c10-e44e-929c-825567a4d13c.png">

次に、副所有者として登録するAlexisのIDを取得します。

```
SELECT metadata.id FROM _ql_committed_Person AS p
WHERE p.data.FirstName = 'Alexis' and p.data.LastName = 'Pena'
```
<img width="836" alt="スクリーンショット 2019-09-11 12.18.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/19f6f209-7b2d-8fd7-df19-8cb36edf6bf8.png">

副所有者として、登録します。

```
FROM VehicleRegistration AS r 
WHERE r.VIN = '1N4AL11D75C109151'
INSERT INTO r.Owners.SecondaryOwners
    VALUE { 'PersonId' : 'BPxNh6SOCsbDTHFB4tbj2y' } --replace with your id
```

登録されたことを確認します。

```
SELECT r.Owners.SecondaryOwners FROM VehicleRegistration AS r
WHERE r.VIN = '1N4AL11D75C109151'
```

<img width="838" alt="スクリーンショット 2019-09-11 12.20.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/cc5e7ff8-e86d-efdf-8741-035c1a02d243.png">


## 変更履歴の確認
`VehicleRegistration`テーブルについて、変更履歴を確認します。
先程行った`committed view`からIDを取得する方法とは別に`BY`句を用いてIDを取得することも可能です。

```
SELECT r_id FROM VehicleRegistration AS r BY r_id
WHERE r.VIN = '1N4AL11D75C109151'
```

<img width="828" alt="スクリーンショット 2019-09-11 12.47.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/b3520f8f-3171-6314-4029-0d1265a9c2ad.png">

`History Function`を使って、このIDの変更履歴を照会できます。

```
SELECT h.data.VIN, h.data.City, h.data.Owners
FROM history(VehicleRegistration) AS h
WHERE h.metadata.id = '9MCnAfEFUCK8JZsP0RpZmK' --replace with your id
```

<img width="838" alt="スクリーンショット_2019-09-11_12_56_07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/4fc9fae2-b61d-54a4-35dc-0166e3fe285e.png">

赤枠で囲ってあるデータが今回の変更履歴になります。
所有者が変更され、副所有者が追加されていることがわかります。

（誤ってUPDATEクエリを入力したため不要なデータがあります）

また、各リビジョンについていつ変更があったのか、トランザクションID等のメタデータを取得できます。

```
SELECT VALUE h.metadata
FROM history(VehicleRegistration) AS h
WHERE h.metadata.id = '9MCnAfEFUCK8JZsP0RpZmK' --replace with your id
```

<img width="833" alt="スクリーンショット 2019-09-11 13.00.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/08786cab-05db-19ba-0cb9-cb3dc8432ca8.png">

## 検証
SHA-256の暗号化ハッシュを利用して、ドキュメントの整合性を検証することができます。
想定シナリオとして、AlexisとBrentが、所有している車を車販売店で取引し、新しいモデルに変えようと考えています。それに対して、ディーラーが車の所有権を確認します。

まず、ダイジェストを取得します。
ダイジェストとは、データの変更履歴全体のフィンガープリントとして機能する、台帳の出力ファイルです。
台帳一覧画面で、対象の台帳を選択し、「ダイジェストを取得」をクリックします。

<img width="1171" alt="スクリーンショット_2019-09-11_14_06_17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/d6e9a523-067d-cec9-778a-14e8a1a4c880.png">

ダイジェストと、ダイジェストのヒントアドレスが取得できます。
保存をクリックすると、[Amazon Iron](https://docs.aws.amazon.com/ja_jp/qldb/latest/developerguide/ion.html)という形式でダウンロードされます。

- ダイジェスト: ダイジェストのSHA256ハッシュ
- ダイジェストのヒントアドレス: ダイジェストがリクエストされた時点での最新ブロックを特定するための情報。

<img width="594" alt="スクリーンショット 2019-09-11 14.12.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/4a9c4b66-35af-996d-663d-f4e65abf3c6e.png">

検証したいドキュメントのIDとBlockAdressを取得します。

```
SELECT r.metadata.id, r.blockAddress
FROM _ql_committed_VehicleRegistration AS r 
WHERE r.data.VIN = '1N4AL11D75C109151'
```

<img width="826" alt="スクリーンショット 2019-09-11 14.23.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/ae17fc7e-77a9-8bbe-750c-ff7859989fa4.png">

左メニューの「検証」からドキュメントの検証を行います。

先程取得したドキュメントIDとBlockAddressを入力します。

<img width="843" alt="スクリーンショット 2019-09-11 14.27.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/0320a5aa-6089-4a62-774b-48494a838c6e.png">

ダウンロードしたダイジェストのファイルをアップロードし、「検証」をクリックします。

<img width="1095" alt="スクリーンショット 2019-09-11 14.28.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/738d914c-09cb-df24-6882-690fd3f31327.png">

検証が終了すると、結果が表示されます。

<img width="1131" alt="スクリーンショット 2019-09-11 14.27.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/7d7e01e7-bf2e-82e5-aa9c-3ceb894b3041.png">

## 削除
一覧画面の「削除」ボタンから削除することができます。

<img width="1168" alt="スクリーンショット 2019-09-11 14.36.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/cb18602b-2af2-4c6f-bdfe-4a49d134d335.png">

デフォルトで削除保護がかかっているため、「削除保護をオーバーライドして、この台帳を削除できるようにします。」にチェックを入れ削除します。

<img width="601" alt="スクリーンショット 2019-09-11 14.36.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234559/9b7bc11a-49d8-469d-84ea-6d446d42c764.png">


## 感想
- 管理画面上にエンドポイントが見当たらないので、実運用する際にはどうやって使うことになるんでしょうか？
- ~~ユーザーが誤って削除する可能性を考慮して、スナップショットは欲しいかも。~~ エクスポート機能でバックアップできそうですね。
- QLDB似合わせてPartiQLを初めて利用しましたが、レコードの中のオブジェクトまで指定して取得・更新出来るのは便利。