在庫ゼロと取得失敗は同じ?|例外処理・ログ・監視のサムネイル
ガイドAP

在庫ゼロと取得失敗は同じ?|例外処理・ログ・監視

公開: 2026-10-03
在庫APIのタイムアウトを題材に、失敗を正常値へ変えない例外処理、原因の保持、再試行、ログとメトリクス・トレースによる調査をコードと図で学びます。

在庫の取得が失敗したとき、「在庫0」と返してもよいのでしょうか。利用者には売切れに見え、運用担当は障害に気付きにくくなります。失敗をどこで判断し、何を返し、何を記録するかを分けて考えます。

事例:在庫APIの先で、タイムアウトが起きる

画面が在庫APIへ問い合わせ、APIは保存先から個数を読みます。個数0は正常な売切れです。一方、保存先が時間内に返さない場合は個数が分からず、一時的に利用できない状態とします。

正常な0と、取得できなかった失敗を区別します。この事例では取得失敗に503と「在庫を確認できません」を返し、運用向けには原因と問い合わせを結び付けて記録します。

利用者への説明と、運用向けの記録個数が不明な失敗を、正常な売切れへ変換しません。画面在庫API保存先ログ1. 在庫を問い合わせ2. 個数を取得3. タイムアウト4. 原因・request_idを記録5. 503:確認できません
利用者への説明と、運用向けの記録

個数が不明な失敗を、正常な売切れへ変換しません。

例外を捕まえる場所は、どこがよい?

取得処理は、保存先のタイムアウトを「在庫を取得できない」という業務側の失敗へ変えます。APIの境界でその失敗を捕捉し、応答とログを決めます。各層が同じ例外を何度も記録すると、件数や原因を読み違えやすくなります。

  1. 取得層

    保存先固有の失敗を、呼出し側が理解できる例外へ変換します。変換前の原因も保持します。

  2. API境界

    利用者向けの応答を選び、調査できる情報を記録します。秘密の値や詳細な内部構成を応答へ出しません。

  3. 最上位の境界

    想定外の失敗を処理し、内部エラーとして記録・応答します。正常値へ置き換えて隠さないことが重要です。

結果

この事例の扱い

理由

個数0

200・在庫0

正常な値

保存先のタイムアウト

503・確認できない

一時的な利用不能

個数が負数など

不正なデータとして別の境界へ伝播

タイムアウトではない障害

プログラム上の想定外の例外

最上位で内部エラーとして扱う

正常な個数を作ってはいけない

ここでのHTTP応答は、このAPIが保存先の一時的な利用不能として扱う設計です。APIがゲートウェイとして振る舞う場合などは別のステータスが適切なこともあり、責務と契約に合わせて決めます。

短いコードで、失敗と正常値を分ける

python
class StorageUnavailable(RuntimeError):
    pass

def read_stock(load):
    try:
        quantity = load()
    except TimeoutError as exc:
        raise StorageUnavailable("inventory backend timeout") from exc
    if type(quantity) is not int or quantity < 0:
        raise ValueError("invalid stock")
    return quantity

def stock_response(load, logger, request_id):
    try:
        quantity = read_stock(load)
    except StorageUnavailable:
        logger.exception("stock_read_timeout",
                         extra={"request_id": request_id})
        return {"status": 503, "message": "在庫を確認できません"}
    return {"status": 200, "stock": quantity}

loadは取得処理を渡す引数です。logger.exceptionは例外を捕捉した場所で呼び、トレースバックを記録します。request_idは同じ問い合わせを追うための値で、データベースの内部主キーとは役割が異なります。

raise ... from excで、元の原因を残します。このコードはTimeoutErrorだけを変換します。不正な個数のValueErrorや別の障害は上位へ伝わり、アプリ全体の境界で記録・応答する設計です。

実装に組み込むときは、使うライブラリがどの型の例外を返すかを確認します。HTTPクライアント固有のタイムアウトなどを、この例のTimeoutErrorと無条件に同一視しないことが必要です。

後片付けで、元の失敗を消していない?

ファイルや接続を開いたままにしないため、withなどの資源管理やfinallyを使います。ただし、finallyでreturnすると、先に起きた例外や戻り値が隠れる場合があります。後片付けと結果の決定を混ぜないようにします。

例外を捕まえた後に何もせず続けると、半分だけ更新された状態で後続処理が動くことがあります。トランザクションの取消し、再開できる地点、既に完了した外部処理を確認します。

再試行すれば、失敗は解決する?

読み取りを1回200ms、最大3回行うなら、各回の最大待ちは合計600msです。待ち間隔や追加処理は別に足します。呼出し元の期限が500msなら、その条件のまま最大3回待つ計画は合いません。

更新処理のタイムアウトは、未実行を意味しません。保存先では確定し、応答だけ届かなかった可能性があります。注文の重複を防ぐキーや結果の照会など、同じ依頼を重ねても二重に更新しない条件が必要です。

大量の再試行は障害中の負荷を増やします。回数、全体の期限、待ち間隔、再試行する失敗の種類を決め、必要なら一時的に呼出しを抑制します。

ログ・メトリクス・トレースは、どう使い分ける?

ログは個々の出来事と原因、メトリクスは失敗率や遅延の傾向、トレースは一つの処理がどのサービスを通りどこで待ったかを調べる手掛かりです。目的が異なるため、必要な情報をつなげます。

異常の検知から、問い合わせの調査へ時刻・問い合わせ・版を結び付けます。相関があるだけで原因を断定はしません。処理観測調査123456在庫API失敗率・応答時間原因・イベントのログ経路と区間の時間運用担当
異常の検知から、問い合わせの調査へ
  1. 1. 集計
  2. 2. 出来事と原因
  3. 3. 処理の区間
  4. 4. 異常を検知
  5. 5. 詳細を確認
  6. 6. 待つ区間を確認

時刻・問い合わせ・版を結び付けます。相関があるだけで原因を断定はしません。

ログにはUTCなど基準をそろえた時刻、イベント名、処理の結果、所要時間、問い合わせの識別子、プログラムの版を必要に応じて含めます。パスワード、認証トークン、不要な個人情報や本文全体は記録しません。

外部から来る識別子を使うなら長さや文字を検証し、ログの改行混入も防ぎます。記録に失敗した場合の扱い、閲覧権限、保存期間も決め、必要な情報を必要な範囲で残します。

演習1:正常な売切れ

条件:保存先が正常に0を返します。

問い:掲載コードの応答は何ですか。

解答例:statusが200、stockが0です。

根拠:0は非負の整数なので正常な取得結果です。

誤答の理由:0を取得失敗として503へ変えると、正常な売切れを障害と扱ってしまいます。

演習2:タイムアウトの伝播

条件:loadがTimeoutErrorを発生させます。

問い:取得層とAPI境界でどう扱いますか。

解答例:原因を残したStorageUnavailableへ変え、境界で一度記録して503を返します。

根拠:限定した例外型を、それぞれの責務で扱っています。

誤答の理由:例外を捕まえて0を返すと、個数が不明な状態が隠れます。

演習3:想定外のデータ

条件:loadが-1を返します。

問い:503を返す経路へ入りますか。

解答例:入りません。ValueErrorが発生し、上位の境界へ伝播します。

根拠:掲載コードはStorageUnavailableだけを捕捉します。

誤答の理由:すべての例外を同じ取得失敗と扱うと、不正なデータと一時的な通信障害を区別できません。

演習4:更新の再試行

条件:注文登録後の応答だけがタイムアウトした可能性があります。

問い:同じ注文をそのまま再登録してよいですか。

解答例:重複を防ぐ条件や結果照会を設けてから再試行します。

根拠:相手側では既に登録済みかもしれません。

誤答の理由:応答がないから未実行と判断すると、二重の注文を作る可能性があります。

演習5:記録する項目

条件:問い合わせの特定が必要で、認証トークンも受け取ります。

問い:何をログへ残し、何を除外しますか。

解答例:検証した問い合わせ識別子や結果・時刻・版を残し、認証トークンなどの秘密は除外します。

根拠:調査に必要な情報と秘密を分けます。

誤答の理由:全リクエストを無条件に保存すると、調査用ログへ認証情報が流出します。

演習6:遅い区間の調査

条件:失敗率が上がり、複数サービスを経由します。

問い:観測情報をどう使いますか。

解答例:メトリクスで異常を検知し、同じ問い合わせのログとトレースで原因や待ち区間を調べます。

根拠:集計と個別の出来事、経路を結び付けます。

誤答の理由:失敗率だけから原因を断定すると、通信、保存先、アプリのどの区間かを取り違えます。

出典と仕様を確認する

IPA:APシラバス Ver.7.2

RFC 9110:HTTPの応答と失敗

Python:例外処理と原因の連鎖

Python:logging

OWASP:Logging Cheat Sheet

OpenTelemetry:トレース

関連テーマを続けて学ぶ

責務と通知の分け方

例外を含むテスト設計

次におすすめの学習

この記事を共有する

編集・検証について

編集・検証:IT資格ラボ編集部

IPAが公開する試験要綱・シラバス・過去問題と、各技術の公式資料を優先して内容を確認しています。制度変更や誤りを確認した場合は、記事を見直して更新します。

編集方針・情報源・訂正方針を見る