はじめに
こんにちは。
GadgetStreemでは現在、ガジェット価格の上昇圧力を一つの数字で確認できる独自指数、
GPI(Gadget Pressure Index/ガジェット価格圧力指数)

を開発しています。
これまでの開発日誌では、次の段階まで進んできました。
- Vol.1:Docker、PostgreSQL、FastAPIなどの開発基盤
- Vol.2:USD/JPYの公式データソース選定とCollector設計
- Vol.3:USD/JPY Collectorの実装と実データ保存
- Vol.4:継続運用、鮮度監視、再試行、障害対応の設計
Vol.3で、Federal Reserve H.10からドル円データを取得し、PostgreSQLへ保存する最初のデータパイプラインが形になりました。
Vol.4では、それを毎週安定して使うために、
- 実行履歴
- データ鮮度
- 欠損検知
- 再試行
- 自動実行
- 障害通知
をどのように管理するか決定しました。
そして今回のVol.5では、その運用設計を実際のシステムへ落とし込んでいきます。
USD/JPY Collectorを「手動で一度動くプログラム」から、「毎週の更新を任せられる仕組み」へ近付けます。
今回の目的
USD/JPY Collectorの基本機能は、すでに次の流れを持っています。
Federal Reserve H.10
↓
USD/JPYデータを取得
↓
値・単位・日付を検証
↓
重複・訂正を確認
↓
PostgreSQLへ保存
しかし、この処理を長期間運用するには、さらに次の問いへ答えられなければなりません。
- 最後に実行されたのはいつか
- 正常終了したのか
- 何件保存されたのか
- 最新の観測日はいつか
- 同じ処理が二重に動いていないか
- データが古くなっていないか
- 失敗した場合に再実行できるか
- 今週のGPIへ使ってよい状態か
今回追加するのは、こうした情報を管理するための運用レイヤーです。
Collection Runで「一回の実行」を記録する
最初に必要なのが、
Collection Run
という考え方です。
これまで保存していたのは、主にUSD/JPYの観測値でした。
しかし、それだけではCollectorの動作状況を確認できません。
そこで、Collectorを一度実行するたびに、一つのCollection Runとして履歴を残します。
記録する情報の例は次のとおりです。
実行ID
データソース
開始日時
終了日時
取得対象期間
実行結果
新規保存件数
変更なし件数
訂正件数
スキップ件数
失敗件数
エラー概要
例えば、最新10営業日分を取得した結果が次のようになったとします。
Status:Success
Inserted:5
Unchanged:5
Revised:0
Skipped:0
Failed:0
この結果なら、
- 新しい5日分を保存
- 残り5日分はすでに保存済み
- 訂正やエラーはなし
ということが分かります。
単に「Collectorが終了した」だけではなく、
何を処理して、DBがどのように変わったのか
まで確認できるようになります。
成功だけでなく「警告付き成功」も記録する
Collectorの結果は、成功と失敗の二択ではありません。
処理自体は最後まで完了していても、注意が必要なケースがあります。
例えば、
- 最新データがまだ公開されていない
- 一部の日付が欠損している
- 想定より観測件数が少ない
- 取得値が前回から大きく動いた
- 新しいデータが一件もなかった
といったケースです。
そのため、Collection Runでは次のような状態を区別します。
Success
想定したデータを正常に取得・検証・保存できた状態。
Success with warnings
処理は完了したものの、欠損や公開遅延などの注意事項がある状態。
Failed
通信、解析、検証、DB保存などの問題で処理を完了できなかった状態。
Partial failure
一部の観測値だけ処理できなかった状態。
これにより、
終了コード0
だけで正常と判断せず、処理内容まで確認できます。
最新データが「新しいか」を判定する
Collectorが正常に終了しても、最新の観測値が古ければ、そのままGPIへ使用できるとは限りません。
そこで、データの鮮度を判定します。
初期段階では、次の状態を想定しています。
| 状態 | 意味 |
|---|---|
| Fresh | 想定どおり最新のデータがある |
| Delayed | 提供元の更新が通常より遅れている |
| Stale | 一定期間、新しい観測値がない |
| Unavailable | 利用できる値が存在しない |
| Invalid | 形式や単位などに問題がある |
USD/JPYで利用しているH.10は、通常、前営業週分の二国間為替レートを米国東部時間の月曜日16時15分に公開し、月曜日が米国の祝日なら次の営業日に公開します。
そのため、月曜日の公開予定時刻を少し過ぎただけで、すぐStaleとは判定しません。
- 米国の休日
- 公開の遅れ
- 日本と米国の時差
- データ取得処理の実行時刻
を考慮して、一定の猶予を持たせます。
また、具体的なFresh/Staleの境界はコードへ直接埋め込まず、将来変更できる設定として管理する予定です。
「実行日時」と「観測日時」を混同しない
自動実行を導入すると、
Collectorを実行した日時
が目立つようになります。
しかし、GPIで重要なのは、
為替レートが示す観測日時
です。
例えば、前週金曜日の為替レートを翌週火曜日に取得した場合、
観測日:金曜日
取得日:火曜日
となります。
これを「火曜日のドル円」として扱ってしまうと、他の構成指標と時点が合わなくなります。
そのため、引き続き次の日時を分離して保存します。
- 観測日時
- 公表日時
- 取得日時
- Collection Runの開始・終了日時
- DBへの保存日時
この区別は、将来GPIを過去へさかのぼって再計算するときにも重要になります。
同じCollectorを同時に動かさない
手動実行と自動実行を両方用意すると、処理が重なる可能性があります。
例えば、
自動実行中
+
同じCollectorを手動実行
という状態です。
観測値側には重複防止がありますが、同じ外部データへ同時アクセスしたり、同じ期間の保存処理が競合したりすることは避けたいところです。
そこで、同じUSD/JPY Collectorは、原則として一度に一つだけ動かすよう制御します。
GitHub Actionsにはconcurrencyという仕組みがあり、同じグループに属するワークフローの同時実行を制限できます。
概念的には、
USD/JPY Collector実行中
↓
新しい実行要求
↓
待機または安全に停止
という動作です。
これにより、ボタンを複数回押した場合や、自動実行と手動実行が重なった場合の競合を防ぎます。
GitHub Actionsから手動実行できるようにする
障害対応や動作確認のために、CLIだけでなくGitHubの画面からもCollectorを起動できるようにします。
GitHub Actionsでは、ワークフローにworkflow_dispatchを設定すると、Actions画面の「Run workflow」から手動実行できます。
手動実行では、将来的に次のような入力項目を指定できるようにします。
取得開始日
取得終了日
再取得の有無
Dry Run
対象環境
Dry Runとは
Dry Runでは、外部データの取得と解析までは実施しますが、DBへ保存しません。
これにより、
- H.10の形式が変わっていないか
- 日本円系列を正しく抽出できるか
- 何件が保存対象になるか
を確認してから本実行できます。
自動実行は「決まった時刻ぴったり」を保証しない
GitHub Actionsでは、cron形式のscheduleイベントを使って定期実行できます。
ただし、GitHub公式ドキュメントでは、利用が集中する時間帯にはスケジュール実行が遅れ、状況によっては一部のジョブが実行されない可能性も説明されています。
特に毎時0分付近は集中しやすいため、別の分を指定することが推奨されています。また、スケジュール実行はデフォルトブランチ上のワークフローだけが対象です。
そのため、GPIでは、
毎週火曜日 6時00分
のような時刻ではなく、
毎週火曜日 6時17分ごろ
のように、集中しにくい時刻を選ぶ方針です。
そして、GitHub Actionsが起動したかどうかとは別に、
Collection Runが正常に完了したか
を確認します。
「スケジュールされた」ことと「最新データを保存できた」ことは、別の出来事だからです。
自動実行と手動実行を併用する
運用開始後も、手動実行は残します。
通常時
自動実行
障害時・検証時
手動実行
という使い分けです。
手動実行があれば、
- 自動実行に失敗した期間の再取得
- 過去データの追加取得
- 提供元復旧後の再実行
- Collector更新後の確認
- Dry Runでの事前検証
が可能になります。
完全自動化とは、人間が操作できなくなることではありません。
通常作業は自動化し、異常時には安全に手動介入できる
ことを目指します。
再試行するエラーを限定する
Collectorが失敗した場合、何でも繰り返せばよいわけではありません。
再試行が有効なのは、主に一時的な問題です。
再試行する候補
- 一時的な通信エラー
- タイムアウト
- HTTP 429
- HTTP 502/503など
- 一時的な名前解決エラー
自動再試行しないもの
- XML構造の変更
- 日本円系列が見つからない
- 単位が想定と異なる
- 不正な日付や数値
- DBスキーマの不一致
- 必須設定の不足
後者は何度繰り返しても改善しない可能性が高いため、人間による確認が必要です。
再試行する場合も、
失敗
↓
少し待つ
↓
再試行
↓
さらに待つ
↓
上限に達したら停止
という形で回数を制限します。
エラー内容を安全に記録する
Collection Runには、失敗理由も記録します。
ただし、外部サービスから返されたレスポンスや内部エラーを、そのまま公開するわけではありません。
ログには、
- APIキー
- パスワード
- DB接続情報
- 制限付きの原データ
- 不要な内部パス
が含まれる可能性があるためです。
DBへ保存するエラー概要は、
HTTP timeout
XML parse failed
Target series not found
Database transaction failed
のように整理し、詳細な調査ログとは分離します。
将来的に管理画面へ表示する場合も、安全な概要だけを表示します。
GPIで使用できる状態かを判断する
最終的な目的は、Collectorを動かすことではありません。
取得したUSD/JPYを、今週のGPIへ使ってよいか判断すること
です。
例えば、次の状態なら通常利用できます。
Collection Run:Success
Data freshness:Fresh
Quality status:Valid
Latest observation:想定期間内
一方で、
Collection Run:Failed
Data freshness:Stale
なら、通常どおりのGPI公開はできません。
Rule Bookに従って、
- 前回値をStale表示付きで利用
- Provisionalとして暫定公開
- GPIの更新自体を停止
のいずれかを選択します。
Collectorが独自判断で古い値を「最新」として扱うことはありません。
今回できるようになること
今回の運用基盤によって、USD/JPY Collectorは次の情報を持てるようになります。
最後にいつ実行されたか
最後に成功したのはいつか
何件追加されたか
新しいデータが存在したか
最新の観測日はいつか
現在FreshかStaleか
連続して何回失敗したか
どの種類のエラーだったか
これにより、
データがDBに入っている
だけでなく、
今も正常に更新され続けている
ことを確認できるようになります。
まだ「本番完全自動化」ではない
ここは、正確にお伝えしておきたい部分です。
今回実装するのは、USD/JPY Collectorを継続運用するための基礎です。
ただし、現時点で次のすべてが本番稼働しているわけではありません。
- 24時間監視
- 独立した本番用スケジューラー
- メールやSlackへの通知
- 公開用Collector監視画面
- 自動復旧
- SLAの設定
- GPIの自動公開判断
- DRAMなど他Collectorとの統合監視
最初は開発・検証環境で動作を確認し、取得履歴と失敗パターンを蓄積します。
その結果を見ながら、本番環境へ必要な機能だけを追加します。
「完成」よりも「確認できること」を増やす
今回の実装で重視したのは、
失敗しないシステム
ではありません。
外部データ、ネットワーク、サーバーを扱う以上、失敗を完全になくすことは困難です。
そのため目指しているのは、
いつ失敗したか分かる
なぜ失敗したか分かる
どこまで処理できたか分かる
安全に再実行できる
古いデータを最新と誤認しない
というシステムです。
異常が起きないことより、異常を見逃さないこと
を優先します。
USD/JPY Collectorで作った仕組みを横展開する
今回追加した運用基盤は、ドル円専用で終わらせません。
今後追加する、
- DRAM
- NAND
- GPU需給
- 海上運賃
でも共通利用できる形を目指します。
共通のCollection Run
共通のFresh/Stale判定
共通のエラー分類
共通の再試行
共通の手動実行
共通の監視情報
を用意することで、新しいCollectorを追加するたびに、運用機能をゼロから作り直す必要がなくなります。
USD/JPY Collectorは、GSIにとって最初のCollectorであると同時に、
今後のデータ収集システムの標準モデル
でもあります。
次のVol.6で紹介すること
USD/JPYの取得・保存・運用設計が一通り形になった後は、GPIの二つ目の構成要素へ進みます。
次回のGPI開発日誌 Vol.6では、
DRAM価格のデータソース選定
を取り上げる予定です。
DRAMはドル円と違い、
- 契約価格
- スポット価格
- チップ価格
- モジュール価格
- DDR4
- DDR5
- 容量・規格別価格
など、複数の価格体系があります。
そのため、
GPIで使う「DRAM価格」とは、具体的に何を指すのか
から決めなければなりません。
また、データの取得方法だけでなく、
- 商用利用できるか
- 過去データを取得できるか
- 値を公開・引用できるか
- 日本のガジェット価格をどの程度代表できるか
も確認します。
まとめ
GPI開発日誌 Vol.4では、USD/JPY Collectorを継続運用するためのルールを設計しました。
今回のVol.5では、その方針をシステムへ反映し、
- Collection Run
- 実行結果の記録
- Fresh/Stale判定
- 重複実行の防止
- 手動再実行
- 自動実行への対応
- 制限付き再試行
- エラー分類
- GPI利用可否の判断
という運用基盤を整えていきます。
これによりUSD/JPY Collectorは、
ドル円を一度取得できるプログラム
から、
更新状態と異常を確認しながら、継続利用できるCollector
へ変わります。
まだGPIそのものは表示されていません。
しかし、信頼できる指数を作るには、計算式だけでなく、その前段にあるデータが継続的に正しく更新されている必要があります。
GadgetStreem Intelligenceでは、見える数字だけでなく、
その数字を支える取得・検証・運用の仕組み
も一つずつ作っています。
次回からは、GPIの二つ目の構成要素となるDRAM価格の調査へ進みます。
引き続き、GPIが実際の指数として動き出すまでの過程を公開していきます。
ぜひ一緒に完成まで見守っていただけるとうれしいです。
※GPIは現在開発中の実験的な市場分析指標です。個別製品の将来価格、値上げ、値下げ、在庫状況、購入利益を保証するものではありません。また、投資判断を目的とした金融指標ではありません。


コメント