MomentoキャッシュをSQLでクエリする

# はじめに

私は仕事でTiDBを利用するので、Momento CacheもDBに入れるデータのキャッシュとして利用したいですが、そうなるとMomento CacheのデータとDBのデータのJOINなどがやりたくなってきます。
もちろんクライアント側でコード書けばできますが、ここでは[Apache Drill](https://drill.apache.org/)を利用してMomentoのデータをSQLで検索し、JOINする方法を紹介していきます。

## Apache DrillでMomento Cacheのデータをクエリする

Apache Drillはデータベース以外の様々なデータソース、例えばCSVやJSON、オブジェクトストレージやdropboxといった様々なデータソースをSQLで扱えるようにするツールです。元のデータソースのスキーマ情報を定義する必要がなく、接続できればすぐに利用できる手軽さが便利な点です。

実態としてはSQLクエリエンジンで、SQLをパースして裏のデータソースのAPIを呼び出し、結果をパースして表形式にマップします。

Apache DrillはMomentoに直接対応しているわけではありませんが、JSONを返すHTTPエンドポイントをテーブルとして扱える[HTTP Storage Plugin](https://drill.apache.org/docs/http-storage-plugin/)という機能があります。また、Momentoには[HTTP API](https://docs.momentohq.com/ja/cache/develop/api-reference/http-api)があり、HTTPでキャッシュの中身の取得が可能です。キャッシュにJSONを保存すれば、Apache Drillからも取得可能でしょう。

ここではmacosにインストールしてMomento Cacheの中身を検索してみます。

### Apache Drillのインストール

Apache Drillをインストールします。Macの場合はbrewでインストール可能です。ラクですね。
```bash
brew install apache-drill
```

インストールできたら、`drill-embedded` コマンドで起動します。
```bash
> drill-embedded
Apache Drill 1.21.1
"Keep your data close, but your Drillbits closer."
apache drill> 
```

### [Momento] キャッシュの作成とAPIトークンの発行

Momento側の準備をします。まず、この用途に使うキャッシュを作成しておきます。`drill`というキャッシュにしました。GUIでもCLIでも、お好きな方で作成してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/6dc0fb2f-9253-6d32-b9b5-bd2217b818be.png)

次に、APIトークンを発行します。`Fine Grained Access Key`で、今回作成したキャッシュのRead-Onlyで十分です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/cbca311e-d4aa-83eb-c32a-dd3b7fe3e076.png)

### HTTP Storage Pluginの設定

Apache Drill側の設定を行います。今回は試しに利用するだけですので、動作しているdrill-embeddedの設定を変更してしまいましょう。drill-embeddedは http://localhost:8047 でサービスを起動しているので、ここにアクセスします。

上部のメニューから、`Storage` を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/cdc21da7-e924-67b8-519d-20ec32327eb5.png)

デフォルトではHTTPプラグインは有効化されていないので、右側にある`Disabled Plugin`からHTTPプラグインを探し、Enableにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/3fb2c834-e721-4527-257e-63f08f93985d.png)

有効化されたら、`Update`を押して設定ファイルを更新します。下記の内容を自分の環境に合わせて修正します。（恐らくAuthorizationのトークンの修正だけで良いはずです）

```json
{
  "type": "http",
  "connections": {
    "mo": {
      "url": "https://api.cache.cell-ap-northeast-1-1.prod.a.momentohq.com/cache/drill?key=",
      "requireTail": true,
      "method": "GET",
      "headers": {
        "Authorization": "<YOUR API TOKEN>"
      },
      "authType": "none",
      "inputType": "json",
      "xmlDataLevel": 1,
      "postParameterLocation": "QUERY_STRING",
      "verifySSLCert": true
    }
  },
  "retryDelay": 1000,
  "proxyType": "direct",
  "authMode": "SHARED_USER",
  "enabled": true
}
```

設定の解説ですが、`connections`の下に接続先の設定を記載します。ここでは`mo`という名前で定義していますが、これがSQLで検索するときのFROMで記載するテーブル名の一部になります。接続先設定で必須の内容は下記です。

* url - MomentoのHTTP APIエンドポイントを記載しています。キャッシュをパスに含め、キーは可変になるようにしています。
* requireTail - 追加情報をurlの末尾に追加するかどうかを指定します。ここではキー情報を与えるのでtrueにしています。このキー情報はFROMで指定するテーブル名として与えます。例えば、`test`というキーから取得したい場合は、```FROM http.mo.`test` ``` のように指定すると、`test`がurlの末尾に追加されるというわけです。
* headers - HTTPリクエストの際のヘッダ情報を記載します。MomentoのHTTP APIはヘッダとパラメーターのいずれかでAPIトークンを渡すことができますが、Drillから利用する際はヘッダに含めた方が使い勝手が良いと思います。

それ以外のパラメーターは指定しなくても勝手に追加されるものですので、そのままの値で大丈夫です。

### [Momento] キャッシュに値を入れる

クエリする前に値がないといけないので、キャッシュに値を入れます。JOINとかやってみたいので、関連テーブルみたいな形にしました。

**key=companies** 
```json
[
  {
    "companyId": 1,
    "name": "SampleTech Inc.",
    "location": "Tokyo"
  },
  {
    "companyId": 2,
    "name": "SampleDesign Co., Ltd.",
    "location": "Osaka"
  },
  {
    "companyId": 3,
    "name": "SampleSolutions LLC",
    "location": "Fukuoka"
  }
]
```

**key=employees**
```json
[
  {
    "employeeId": 101,
    "name": "Taro Yamada",
    "position": "Engineer",
    "companyId": 1
  },
  {
    "employeeId": 102,
    "name": "Hanako Suzuki",
    "position": "Designer",
    "companyId": 2
  },
  {
    "employeeId": 103,
    "name": "Ichiro Sato",
    "position": "Marketing Manager",
    "companyId": 1
  },
  {
    "employeeId": 104,
    "name": "Rie Tanaka",
    "position": "Sales",
    "companyId": 3
  }
]

```

### 検索する

では、drill-embeddedのコンソールに戻って検索していってみましょう。JSONのキーをそのまま列名として利用することが可能です。

#### companiesの検索
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/4c286bf1-8a93-1fd2-5eb0-fd8ea544cb1b.png)

#### employeesの検索
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/f607357f-c399-c535-1b32-0a93fffdbcc9.png)

#### JOIN
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/7b2337de-01f6-8b5f-cd74-89a4c3f25b05.png)

ちゃんと動作していますね。

実際に試した感じでは、JSONに日本語が含まれているとDrill側でパースに失敗します。どうもBOMつきUTF-8にしたりエンコード変えなかったりしないといけなそうなんですが、調べてはいないです。

## おわりに
Apache Drillを使ってMomento CacheをSQLで検索する方法について紹介しました。この方法を使うと、Apache Drill経由で他のデータソースとMomentoに保存されたJSONを組み合わせて結果を返すことが可能になります。Momento Cacheには1キーあたり1MBの制限があるので本格的なデータベースとしての利用はちょっと厳しいですが、例えばリアルタイムランキングに詳細情報をJOINして返したいようなシチュエーションで役立つかもしれません。