AWS
IoT

[IoT×AWS] AWSIoTにMQTTでPublishしてるけどデータの欠損を無くしたいって言われた時は…

はじめに

IoT関連の案件に携わっていて、お客様が強い関心をお持ちになる事の一つに データの欠損 があります。
センサーが取得したデータが欠損してしまうということは、お客様から見ると データをDBに格納する処理のバグ という認識をされてしまう可能性があります。
もちろん100%データの欠損しないというのは難しい話になりますが、限りなくデータの欠損を0%に近づけていきたいですよね。
今回はそんな要望に対して一人の開発者が奮闘した記録となります。

あんた誰よ?

はい、私はこういうサービスを展開しているチームの一員です。
IoT関連の案件に携わっており、基本的にAWSの環境上にシステムを構築しています。
他にも通信回線にはSORACOM様のサービスを利用しています。(というかほとんどSORACOM様のサービスを使っている)

では本題です

デバイスからAWSIoTにPublishしてLambdaでデータを加工、そのあとDynamoDBにデータをPUTするという処理フローがありました。
こんな感じです。

スクリーンショット 2018-03-20 16.24.08.png

上記のような処理フローの中で、どのような場合にデータ欠損する可能性があるでしょうか?
※決してこれだ!という答えがあるわけではありません。

体験記

先ほどの画像と同じ構成でDeviceからデータを受け取っていたところ、データが欠損していました。
Deviceの数が増えるなどして、送信されてくるデータ量が著しく増加した際に、DynamoDBのスループットに十分なキャパシティが適用されていないがために、LambdaからのPUT時にDynamoDBが「もうスループットに余裕がないから受け入れられないンゴ」といって、LambdaがPUTできずにエラーを吐いていたのです。スケールしたときの対処がなされていなかったということですね。

AWSIoTは基本的にデータを横流しにするだけなので、Lambdaをinvokeすればデータは削除されてしまいます。
Lambdaも性質上、一度実行が完了すればそのときのデータを保持することはありません。
なので、「DynamoDBのスループット不足でLambdaがPUTできない」 = 「データの欠損!」となるわけです。

「それならDynamoDBのスループットを十分に確保すれば問題解決するやん」と思うのですが、そうすると当然のごとく金額が上がってしまいます。要するに運用費が上がります。できるだけ避けたいですよね。
DynamoDBのスループット課金は時間単位での課金となるので、データがきていないときもその時のスループット分は課金されてしまいます。

DynamoDBにはスループットをオートスケールさせる機能があるので、
「オートスケールが適用されるまでなんとか待てへんものか?」
と考えていたときでした…
「そうか!KinesisDataStreamsを間に用意してやればええやん!」
と閃いたわけです。

どうしたか?

結論。こうしました。

スクリーンショット 2018-03-20 16.44.16.png

AWSIoTとLambdaの間にKinesisDataStreamsを用意しました。
KinesisDataStreamsはストリームデータを処理するために弊社でもよく使っているサービスで、一定時間(24~168h)データを保持してくれます。
そのあとのLambdaでエラーが発生した時にちゃんとエラーをcallback()で返せば、Lambdaの再invokeをしてくれます。
要するに、 Lambdaがエラーで死んでも(一定時間は)データが欠損することがない ということです。
(Kinesis StreamsのようなストリームベースのイベントソースがトリガのLambdaは、エラー時にデータの有効期限が切れるまで処理を再実行してくれますが、その代わりに、エラーが発生した以降のデータは処理されずに、いわゆる「詰まった状態」になってしまいます。詰まった状態のまま有効期限が過ぎると、そのデータは愚か、それ以降のデータまで実行すること無くロストしていってしまうので、DynamoDBのAutoScaleが確実に有効である事を確認する必要があります)

さいごに

もちろんKinesisDataStreamsもシャードという単位で金額がかかってきます。(約$20/month)
実際にDynamoDBのスループットを十分に確保するための金額と比較すると半額程度ですみましたので、この構成を採用しました。
DynamoDBのスループットを確保する方が安く済むのであっても、KinesisDataStreamsを用意しておけば、AWSIoTのみの構成よりもデータの欠損を防ぐことができるのでおすすめです。

今回は データ欠損を防ぐ(減らす) という観点で考えた時に、私一個人としては「KinesisDataSteamsにデータを保持させる」という答えにたどり着きました。もちろん他にも答えがあると思います。

この記事は 個人的に考えついたベストプラクティス を書いています。他にもこんな方法あるよ!などご教授いただければ嬉しいです。

ではまた!