はじめに
今年も、幸いなことに地元では夏祭りと共に花火大会が開催されました。 それは、高崎健康福祉大学付属高校野球部の甲子園準優勝を祝うようでもありました。
と、このブログは、小学生の夏休みの日記ではありませんね。
今回は、この花火大会当日、IoT的にある意味良いデータが取れたので、その共有です。 実は、当日夕方頃、雨量観測点からのテレメトリがまだらに欠け始めました。 すぐに全部止まったわけではありません。1 通来て、次の 1 通が来ない。また来る。また消える。そして 21 時前、1 分できっかりと平常に戻りました。

これは、2018 年後半に組んで以来、8 年近く静かに動いてきた小さな雨量観測システムが、今日まで一度も見せなかった顔の話です。降雨さえなければ欠測は誰も困らせないので、そもそも “気づく機会” が無かったのです。
なぜ気がついたか?システムのアーキテクチャを変更中で、移行の準備をしていた中(移行システム側を並走させていた)で気がつくことができました。
前提: 8 年前に組んだ、素朴な観測システム
技術的な全体像は当時 別記事にまとめてあります が、今回の話に関わる要点だけ書くと ――
- 群馬県高崎市内に設置した雨量観測点
- LTE Cat.1 の回線で、1 分に 1 回 HTTP POST で「今の雨量」を投げる
- 常時給電(電池ではなく AC ソース)
- 再送もバッファリングも持たない。投げっぱなし。届かなかった分は、その 1 分ぶんが永久に失われる
「送れない分は諦める」という設計は 8 年前の当時から意図的なもので、この判断が今回のトラブルシューティングでも効いてきます(後述)。とにかく端末は薄く、状態も時刻も持たず、投げるだけ。単純さと引き換えに欠測を受け入れる、明示的な取引です。
大会当日: 5 相の到着プロファイル
まず全体像を出します。花火大会の主催発表による開催時間は 19:30 - 20:20(50 分)。この前後 5 時間の到着率を、5 つの相に分けたのが下の表です。
| 相 | 時間帯 (JST) | 到着率 | 実測メモ |
|---|---|---|---|
| 平常 | 〜17:58 | 100% | 359 分連続で 1 分も欠けない |
| 参集 | 17:59 - 19:29 | 34% (31/90) | 17:59 に最初の欠落。以降まだらに劣化 |
| 開催中 | 19:30 - 20:19 | 4% (2/50) | ほぼ全損。通ったのは 20:09 / 20:10 の 2 分だけ |
| 解散 | 20:20 - 20:45 | 0% (0/26) | 完全にゼロ。公演終了後もしばらく戻らない |
| 回復 | 20:46 - | 98% (82/84) | 20:46 に 1 分で平常復帰 |
視覚化するとこうなります。1 分ぶんを 1 セルとして横に並べ、緑=到達 / 赤=欠測です。
上部の淡黄バンドが花火大会の時間帯(19:30-20:20)、破線は「17:59 に最初の欠落」と「20:46 に 1 分で平常復帰」の瞬間です。参集帯からまだらに欠け始め、開催帯でほぼ全損、そして解散帯を挟んで 20:46 に一気に復帰する ―― 「まだら」「全損」「瞬間復帰」の 3 相がひと目で見て取れます。
3 つの発見があります。
1. 劣化は開演の 90 分前 (17:59) から始まっていた。 「集まっている過程」で既に回線は苦しい。イベント開始時刻を見て構えても遅い。
2. 解散は独立した山を作らなかった。 事前に予想したのは「参集と帰宅で 2 つの谷が出る」だったが、実際は開演前から解散後まで一続きの谷だった。20:20 の公演終了で人が消えるわけではないので、当然といえば当然。
3. 復帰は段階的でなく、瞬間的だった。 20:45 まで 0、20:46 から 1 通/分。輻輳が抜けた形であって、機器が復旧した形ではない(機器が起きた瞬間なら、起動シーケンスぶんのばらつきが出るはず)。
最長連続欠測は 58 分(19:11 - 20:08)。全体では 17:00 - 22:10 の 310 分中、136 分(43.9%)が欠測していました。
誰が消したのか: 4 層に分けて並べる
「メッセージが来ない」とき、疑うべき層は 4 つあります。
- 端末が送っていない(電源断・機器故障・時計狂い)
- 回線が運べていない(電波・輻輳・キャリア側障害)
- 受け口(API Gateway / Lambda)が受けられていない(Throttle・エラー・タイムアウト)
- 自分たちの処理が落としている(例外・DLQ・ロジックバグ)
このどれかを言い当てるコツは、上流から順にログを追わないこと。各層の通過数を、同じ時間バケットで横に並べるのが速いです。今回はサーバ側 3 層(本番取込 Lambda invocation / SNS publish / 並走側 shadow invocation)の 15 分粒度を並べました。
| JST 窓開始 | 本番取込 invocation | SNS publish | Errors | Throttles |
|---|---|---|---|---|
| 17:00 | 16 | 16 | 0 | 0 |
| 17:45 | 15 | 16 | 0 | 0 |
| 18:00 | 11 | 11 | 0 | 0 |
| 18:30 | 4 | 4 | 0 | 0 |
| 18:45 | 1 | 1 | 0 | 0 |
| 19:00 | 8 | 8 | 0 | 0 |
| 19:15 - 19:45 | — | — | — | — |
| 20:00 | 2 | 2 | 0 | 0 |
| 20:15 - 20:30 | — | — | — | — |
| 20:45 | 16 | 15 | 0 | 0 |
| 21:00 | 16 | 17 | 0 | 0 |
— はデータポイントが存在しない、つまり呼び出し 0 です(CloudWatch は 0 を返すのではなく行そのものを返さない)。1 と 2 の差は 15 分境界をまたぐ処理のズレです。
サーバ側 3 層の数字は 1:1 で一致しました。 入ってきたものは 1 通残らず処理されており、Lambda の Errors / Throttles も全時間帯 0、独自メトリクスの DroppedMessages も 0。
各層の通過数を同じ時間バケットで横に並べると、"どこまでは届いていて、どこから消えているか" が一目で分かります。「まずログを grep する」より 100 倍速い。
この "並べ方" を持っているかどうかが、監視の本体だと今回の一件で痛感しました。アラームは「止まった」ことしか言えません。「誰のせいで止まったか」は、層ごとの通過数を並べて初めて分かります。
「まだら欠測 + 瞬間復帰」は、輻輳の署名
残る候補は 2 つになりました:
- (1) 端末が送っていない
- (2) 回線が運べていない
この 2 つは、欠測の “形” で見分けが付きます。
| 原因 | 期待される形 | 今回 |
|---|---|---|
| 端末の電源断・故障 | ある時刻からきれいに切れる/復旧はばらつく | ✗ まだら・復帰は 1 分 |
| バックエンド障害 | エラーメトリクスが立つ | ✗ すべて 0 |
| 回線輻輳 | 確率的に一部だけ通る=まだら/混雑が抜けると即復帰 | ✓ 完全一致 |
加えて、時間帯が人の集まる過程と一致します。前日・前々日の同時刻は 16〜17 通/15 分で完全に安定していたので、周期的な現象でもない。
今回の形は、回線輻輳の署名そのものでした。
もう 1 つの署名: “取り返しの山” が無かった
19:00 - 19:10 の小康時、そして 20:46 の復帰直後、流量はちょうど 1 通/分でした。どちらも平常値ぴったり。
もし端末が失敗を貯めて後から流していたなら、ここに “取り返しの山” が出るはずです。出ていない。
つまり この端末は投げっぱなしで、送れなかった分は永久に失われた ―― 設計どおりの挙動です。
そしてこの「山が無い」こと自体が、輻輳説の裏付けにもなっています。 端末が再起動していたなら、時刻同期や初回接続のぶんだけ挙動が乱れる。乱れずにいきなり定常の 1 通/分に戻ったのは、端末はずっと 1 分ごとに投げ続けていて、“通る確率” だけが 0% から 100% に戻った、という読み方に合います。
「投げっぱなし・再送なし」は正解だった
ここで 8 年前の設計判断を振り返りたいと思います。
「送れないぶんは諦める」は、当時から意図的なものでした。理由は 2 つ:
- 端末に状態・ストレージ・時刻管理を持たせずに済み、圧倒的に薄く安く作れる
- 輻輳しているまさにその瞬間に一斉再送して事態を悪化させるという古典的な失敗を抱え込まずに済む
軽さと引き換えに欠測を受け入れる、明示的な取引です。
もし端末が「失敗を貯めて回線が戻ったら一斉に流す」設計だったら、20:46 の復帰直後に 136 分ぶんの再送が一気に押し寄せていた可能性があります。復帰直後の回線は "戻ったばかり" で余力があるとは限らず、その一斉再送で二次的な輻輳を起こしていたかもしれない。
もちろん再送が「常に悪」というわけではありません。用途と規模しだいです。今回のように、「今の値」に意味があり、古い値を後から届けても価値が薄い用途(雨量警報、リアルタイム監視)なら、投げっぱなしのほうが素直だし壊れにくい。逆に積算値や履歴が主役の用途では、再送 + ジッタ + 段階的リトライを組み込んだほうがよい。
教訓
10 年近く運用していて、今日まで一度も見なかった顔でした。降雨さえなければ欠測は誰も困らせないので、そもそも “気づく機会” が無かったのです。逆に言えば、降ってから初めて “見えない” と気づくタイプの欠陥です。今回、たまたま鳴った(移行予定システムによる)並走運用側の新しいアラームが拾ってくれたのは、幸運でした。
1. 花火大会・スタジアム・大規模イベントの近くに端末があるなら、当日は欠測を織り込む。 「まさか自分の観測点が」と思っていても、会場になりうるエリア周辺ではあり得る話です。(実際、起きました)。
2. 劣化はイベント時間の "3 倍" 広がる。 今回は開演 91 分前から始まり、終演 26 分後まで戻らなかった。50 分の花火に対して実効的な影響時間は 167 分。「本番の時間だけ避ければよい」ではないです。
3. 「まだら欠測」は輻輳の署名、「きれいに切れる」は電源か機器、「エラーが立つ」はバックエンド。 復帰の形も見る。瞬間復帰なら輻輳側、段階的ならこちら側。
4. アラームの閾値は "平常値の半分" が具合が良い。 まだらな取りこぼしは見逃し、継続的な停止は捕まえる。今回は 16〜17 通/15 分に対して 8。ただし検知は 33 分遅れ、解除は 8 分遅れました。窓を広く取ったぶんの代償で、これは「早く知る」ではなく「無駄に鳴らさない」ための設定です。
5. 積算値を出す機能を持つなら、欠測はそのまま過小評価になる。 今回の欠測帯は雨量 0 だったので実害はなかったが、大雨のさなかに輻輳が重なる日が来る可能性は、頭の片隅に置いておく必要があります。
まとめ
- 8 年動いていた LTE Cat.1 の雨量観測点が、花火大会の日に 43.9% を欠測した
- 「まだら欠測 + 瞬間復帰」は回線輻輳の署名。端末なら “きれいに切れる”、バックエンドなら “エラーが立つ” のに対し、輻輳は “確率的にまだら” と “抜けたら即復帰” になる
- 各層の通過数を同じ時間バケットで横に並べることで、“どこまで届いて、どこで消えたか” は 30 秒で分かる。アラームは「止まった」ことしか言えない
- 8 年前に選んだ「投げっぱなし・再送なし」設計は、今回の輻輳では正解だった。もし再送を持たせていたら、復帰直後に 136 分ぶんの再送で二次輻輳を起こしていたかもしれない
- 遠隔センサー(雨量・水位・電源状態など)と LTE / LoRaWAN 回線の設計・実装のご相談
- 「まだら欠測」を捉える監視設計(層ごとの通過数を並べる仕組み、閾値の決め方、誤発報を抑える窓幅の考え方)
- 端末側の "再送するか諦めるか" の設計判断のご相談(用途・規模・回線特性に合わせて)
参考
- 元となるシステム: 雨量観測サーバの構成 (2019 年公開)
- こういう “地味に効く” 監視をご相談されたい方は、お問い合わせフォーム からお気軽にどうぞ
