MetabaseとmongoDBでセンサーデータをリアルタイムに可視化した話

やってる仕事のこの部分はTIS AdventCalendarに投稿したらいいかーと思って書きました。

# 本記事の目的
センサーから収集＆蓄積したデータをできるだけ早く可視化するために、
mongodbに収集したデータをMetabase上でできるだけ早く表示するという取り組みです。

その中で発生した課題と対応や諦めたところを書いておくと誰か教えてくれるかなというのです。

mongoDBとMetabaseの組み合わせに起因すると考えられる挙動がいくらかあったので、記事タイトルはmongoDBを明示しています。

# 全体構造
大まかな構成はこんなカンジ。
![overview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19483/8f725644-3e23-5920-2b13-6742faafca37.png)

`複数のセンサーのデータを溜まったそばから可視化に用いたい` というところが主要なモチベーションです。

本記事での利用バージョンは様々な事情で少し古いですが以下になります。

metabase: 0.39.4
mongodb: 3.6.23

# MongoDB
## データの例
なんの変哲もないデータですが例を示します。

```
{
    _id: ObjectId('6139f7249da5cfbe02d0866f'),
    id: 'urn:ngsi-ld:BodyTemp:003',
    type: 'BodyTemp',
    TimeInstant: ISODate('2021-09-09T11:59:32.301Z'),
    temp: 2662
}
```
データにはタイムスタンプが存在し（この例では `TimeInstant` ）、
可視化のためのグラフ化に用いられる値が一つ以上含まれているもの（この例では `temp` ）を対象にしています。

# Metabase
ここからMetabaseをこう使いましたという内容になります。
主に踏み抜いた釈然としない挙動とやったことを記載します。

## データモデル
Metabaseは自動的にmongoDBのdbやCollectionを読み込みデータモデルを設定してくれます。

先程示したデータから、Metabaseがデータモデルを設定してくれました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19483/d734debe-622e-bc9b-f15f-7c98c9c483ee.png)

しかしながらいくつか期待と異なる独特の挙動をします。
1. `_id` も　`id` も同じカラム名になってしまう。
1. `id` は`Entity Key`（おそらくRDBであればPrimaryKey相当）と認識されてしまう（この例では`Category` と認識されたい）
1. `TimeInstant` も `Timestamp` などと認識はされない。

ここのデータモデルを最初に適切にしておかないと、グラフの定義画面で納得行かない挙動になります。
具体的には Metabaseのデータモデル上 EntityKey と認識されているものをフィルタ条件にすると、1件のデータしか可視化に使えません。

私見ですが、RDBが相手であれば、テーブル定義の情報から適切に作れるのでしょうが、mongoDBのようなスキーマレスなDBからデータモデルを作るので誤りや独特な挙動につながっているようです。

## 時間フィルタ
グラフ上で時間範囲を指定しようとするとエラーになる。
リアルタイムに画面更新するという機能要件上は不要ですが、
値が集中しているところをUIから選択して（下図のうす青い部分）詳細を見ようとすると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19483/7ba05263-e5c7-6712-80b4-e20c8c4bc48c.png)

以下のような画面に遷移し期待を裏切られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19483/42ccc213-667d-55ec-7459-75d1ee83a722.png)

デフォルトのh2などのDBに対してはこの操作で時間の範囲を指定した可視化が可能でした。

私見ですが、タイムゾーンをmongoDBが持ってないところに起因していそうですが、解決はしてません。（metabaseのバージョンアップすら試していない）

## ダッシュボードとフィルタ条件
実験用にダッシュボードを作りました。
これにより複数のセンサ値を同じ画面で見られるようになります。

![da20da77878473b82a7775352b2c0969.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19483/25d33465-a313-02ff-b4b6-56647420d124.png)

時刻フィルタのところで直近数分だけのデータだけを表示するようにします。
ここはあまり罠がなかったですが、画面の操作が億劫になる程度には重たい画面になりました。

## 定期的なダッシュボード更新のしかけ
定期的なダッシュボード更新はmetabaseの機能として持っているのですが、最短の頻度でも60秒ごとの更新でした。
もっと高頻度で画面を更新してもらわないと、センサー値の動きが見えないため面白く有りません。

そこで以下の工夫を行いました。
60秒ごとの更新に設定すると、以下のようなURLに書き換わっています。
`https://example.com/dashboard/1?time=past3minutes~#refresh=60`

明らかに `refresh=60`　が怪しいので `refresh=3` として別のタブに開きます。
すると、3秒に1回画面が更新されるダッシュボードになります。
リアルタイムに画面を出すダッシュボードとしてこのURLをブックマークしました。

※URLの書き換えを行うと何故かrefreshの値がもとに戻るので別のタブとして開きましょう。

# 発生した課題
というようなことを実施したところ以下のような課題が発生しました。

## CPUリソースの枯渇
Metabaseが利用するCPUリソースの枯渇により画面が正しく表示できない。
おそらく画面の更新を3秒と意図しないレベルで高頻度にしたのが原因。

稼働環境のインスタンスをスケールアップすることで対応。
AWSのEC2のt3.xlargeのノード群でk8sを動かしてたのですがc5.2xlargeくらいまで上げたらリソース上だいぶ余裕が出ました。
~~が、金で解決したような状態ではあります。~~

感覚としては、ボトルネックがDBへのクエリ内容、インデックスの有無とかにあるならよくあるケースなのですが、MetabaseのCPUがなんでそんな忙しいの？って感じではありました。
が、そう入っても観測された事実に対して対応を行います。

# まとめ
というわけで、少し不思議な挙動を踏みながらも、工夫や力技によって、センサーデータが数秒以内に画面に表示される仕組みを実現できました。

が、Metabaseがこの用途に適していたか、というと答えはNoである気がしているので、この記事を読んだ方でおすすめのOSSなどがあればぜひ教えて下さい！