enebularでCacheを使ってフロー上で状態保持を実現する

メリークリスマス！！！（まだです）
この記事は[enebular Advent Calendar 2019](https://qiita.com/advent-calendar/2019/enebular)の5日目の記事です。

## はじめに

enebularをバックエンドとして使うとき、みなさんどんな実装されてますか？
例えば状態保持だとかデータ保存とかしたくなったとき、enebularでは`file`のようなファイルシステムが扱えないかわりに、`firebase`や/tmp上の`sqlite`が使えたりしますよね。
でも`firebase`は別サービスなのでgoogle側での設定が別途必要だったり、`sqlite`だったらSQL文の知識が必要だったりして、さくっと状態保持の実装ができる方法ってあまりないのでは？　と思ってます。

そこで「Cacheノード」を使うことで、簡単な状態保持であればすぐに実現する方法がありますが、実際に使ってそうな人をあまり見かけなかったので書いてみました。

## インストール

<img width="315" alt="Screen Shot 2019-12-02 at 22.14.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/88bf915d-d083-8232-7bbc-5e5cbc376143.png">

adminタブにて`node-red-contrib-cache`をインストールします。

<img width="495" alt="Screen Shot 2019-12-02 at 21.00.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/2bd285e9-87d5-8a00-9871-daefc1b8d224.png">

`Cache in`ノード、および`Cache out`ノードがインストールされます。

## 基本的な使い方

Cacheノードは、簡易的なKey-Value形式のデータストアとして利用することができます。
以下のようなフローを組んでみましょう。

<img width="880" alt="Screen Shot 2019-12-02 at 23.50.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/42eccae3-e89b-0af2-91d2-70db774412a2.png">

こちらのフローは2つのノードチェーンを持っており、
上が`Cache out`を利用したキャッシュ内の特定のKeyへのValue書き込み、
下が`Cache in`を利用したキャッシュ内の特定KeyからのValue読み出しを実現するものです。

### Topicの指定

<img width="1320" alt="Screen Shot 2019-12-02 at 22.34.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/dc1e006e-804e-a7b0-4154-63291a1d18ed.png">

`Cache in`ノードと`Cache out`ノードの両方に関して、ノードに入力するメッセージにはキーを示す文字列を設定する必要があります。ここでは`function`ノードを利用して、`msg.topic = "count"`を指定しています。
これが指定されたメッセージを`Cache out`に入れると`count`トピックに対する書き込み、
`Cache in`に入れると`count`トピックからの読み出し、という操作になります。
キーを示す文字列を格納するメッセージ要素`.topic`は、以下のキャッシュノードの各設定から変更できます。

＊Cache **in**は「キャッシュ**への入力（書き込み）**」、Cache **out**は「キャッシュ**からの出力（読み出し）**」です。

### キャッシュへ書き込み（Cache in）

<img width="1324" alt="Screen Shot 2019-12-02 at 23.26.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/4a89beef-ad67-b1e1-df7a-9a4a7fdf8ff8.png">

`Cache in`ノードは入力しかありません。
設定項目は以下のようになっています。

- 名前
- キャッシュの指定（後述）
- キープロパティ：キー名はmsgのどの要素に格納されているか
- バリュープロパティ：キーに対する値はmsgのどの要素に格納されているか
- TTL：ここで指定したmsgの要素内に数値が含まれていれば、デプロイ後その秒数を超えるとキャッシュがクリアされる

設定2つ目の「キャッシュの指定」は「データベースの指定」のようなもので、鉛筆マークから新たにキャッシュ追加することができます。といっても設定項目は名前ぐらいで、いくつでも作成することができます。

<img width="1327" alt="Screen Shot 2019-12-02 at 23.26.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/7a21981d-767f-b5d0-26db-dae75b9fcd25.png">

Default TTLは、指定しておくとデプロイ後この秒数がたつとキャッシュが自動でクリアされます。
Check Intervalはよくわかんないです。誰か教えてください。

### キャッシュから読み出し（Cache out）

<img width="1324" alt="Screen Shot 2019-12-02 at 23.26.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/4770cf44-982c-1298-69bb-9283b3b6ac9c.png">

`Cache out`ノードは入出力の両方を備えています。
`msg.topic`（設定で変更可）に読み出したいキー名を指定して入力すると、`msg.payload`（設定で変更可）にキー名に対応した値が格納された状態で出力されます。
設定項目は以下のようになっています。

- 名前
- キャッシュの設定：既存のキャッシュを選択or新規で作成もできる
- キープロパティ：読み出したいキー名がmsgのどの要素に格納されているか
- バリュープロパティ：読み出した値をどこに格納したいか
- Use string payload：文字列のペイロードを利用？（詳細不明）
- Route to the second...：出力を2系統にし、通常時は1つめの出力ポートのみに値を出力し、キーに対応する値がもうない（null）ときは2つめの出力ポートのみにnull値を出力する

### 使ってみる

<img width="1637" alt="Screen Shot 2019-12-02 at 23.29.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/705c4530-fed7-6eee-8c68-48ab8a25ce35.png">

デプロイしたら、まず上側の`inject`をクリックしてみましょう。すると`Cache`の各ノードの下に「1」と表示が出て、データが1つだけストアされたことが確認できます。
そのあとで下側の`inject`をクリックすると、先ほどストアされたtimestampがデバッグノードに出力され、デバッグタブに表示されることが確認できます。

### バックエンドとしての利用例

<img width="871" alt="Screen Shot 2019-12-02 at 23.50.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/d9bc2d1f-9585-7bf1-dd11-c5c611b57022.png">

`inject`をそれぞれ`http`に置き換え、各ノードチェーンの末尾に`http response`を付加するだけで、特定のURLをGETしたときのパラメータ、あるいは特定のURLに対してPOSTしたJSONを、別のURLから参照することができます。

## 応用例

いくつか応用例を示します。
フローは公開しておきますので、興味ある方はインポートしてご利用ください。

[https://enebular.com/discover/flow/3d0dd2a4-8c92-413d-a6a4-319fb57e5660](https://enebular.com/discover/flow/3d0dd2a4-8c92-413d-a6a4-319fb57e5660)

おそらく、インポート後にエディタを開くと、`Cache`ノードがインストールされておらずエラーが出るかと思います。

<img width="1327" alt="Screen Shot 2019-12-02 at 22.23.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/1ee4697a-6834-04db-0ff0-f2223d5ab4e5.png">

このような場合はそのままadminタブを開いて`node-red-contrib-cache`をインストールしてください。
「Installing...」という表示が出て、インストールが完了すればそのままブラウザをリロードしてください。
いつまでもインストール中の表示が出ている場合は2〜3分ほど待ってからリロードするとインストールに成功している場合があります。うまくできないときは根気よく何回か試してみてください。

<img width="1084" alt="Screen Shot 2019-12-03 at 0.17.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/37d556f5-4735-3831-d407-2b65fbf7e0a7.png">

更新がうまくいくとこんな感じで復活します。

### アクセスカウンタ

<img width="864" alt="Screen Shot 2019-12-03 at 0.04.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/0cf2656c-58c6-057a-0343-0d18ccdbf12e.png">

`/counter` にアクセスするたびに数値が増えていきます。カウンタ値を一度読み出して+1し、同じキーに上書きしています。

<img width="248" alt="Screen Shot 2019-12-03 at 0.05.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/77ce9f00-5e20-9123-ba38-f54992ad64b2.png">

なんだかちょっと懐かしい感じですね！

### JSONをストアしてくれる簡易アップローダ的なもの

<img width="1035" alt="Screen Shot 2019-12-03 at 0.57.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/2e04df65-57a1-e59c-5e35-0137e511737e.png">

`/store`にapplication/jsonをPOSTすると、キャッシュ内にどんどん保存してくれます。
`/get`にindexパラメータつきでアクセスすると、そのindexに対応する保存済みのJSONを出力してくれます。
上記フローのままだと、POST時にストア先のindexを教えてくれないことと、index=0がnullになるという欠点があるため、若干修正したほうがよいかもしれません。

### LINE風の簡易チャット

<img width="1074" alt="Screen Shot 2019-12-03 at 2.25.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/94e291b7-bdf5-ba8b-ef85-b5a3e9a823e0.png">

1対1のチャットも簡単に作れます。
こちらのフローでは、`/me`を自分のエンドポイント、`/you`を相手のエンドポイントとして、GETパラメータの`message`に投稿したいメッセージを指定してアクセスすると某メッセージングアプリ風のチャット画面が開くようにしてみました。

<img width="376" alt="Screen Shot 2019-12-03 at 3.09.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116011/d0a1ec19-802a-e7c5-b32c-889052c059eb.png">

フロントは`template`ノードを使ってCSSとHTMLでがっつり作ってありますが、記事の主旨と離れますので公開されているフローをインポートして直接確認してみてください。
バックエンドとしては、アクセスがあるたびに投稿されたメッセージを読み取り、キャッシュからログを読み出し、htmlタグで囲んだメッセージをログの末尾に追記し、そのログをまたキャッシュの同じ場所に保存する、という流れとなっています。
アクセスカウンタの文字列版みたいな感じですね。

## まとめ

外部サービスに頼ったり、SQLのような追加知識を必要とすることなく、データの簡易保存ができました！
しかしもちろん、使っている人がほぼいないということは、良いところばかりではないということで……

### 利点

- enebularだけでデータ保存が実現できる
- Key-Value形式でシンプルにJSONオブジェクトが保存できる
- SQLiteのようにSQL文を直接書いたりしなくてもよい

### 欠点

- 「キーを示す文字列が入ったmsgの要素」を指定する必要があり、ややこしい
- デプロイしなおすと中身がすべてクリアされてしまう
- フローを再読み込みしたときにアンインストールされた状態になってしまうことがある


以上の点をふまえると、データストアが必要だけどさくっとバックエンド組みたい！　なんてときに最適なのではないでしょうか。
ハッカソンのように短時間でプロトタイプを作るときなどに向いているかもしれません。
こんな感じで少しクセのあるCacheノードですが、ぜひとも活用してみてくださいね〜。