Google Cloud APIを使用している際に発生する例外を処理するスキル
共通規約は .github/copilot-instructions.md を参照してください。
この文書は、Google Cloud API 呼び出し時の例外処理を「運用しやすい粒度」で整理したスキルです。 個別例外を網羅的に列挙するより、次の 3 点で方針を決めることを優先してください。
代表的な例外は次のグループに整理できます。
InvalidArgumentFailedPreconditionOutOfRangePermissionDeniedUnauthenticatedNotFound(要件次第で許容するケースあり)AlreadyExists(作成 API では成功相当として扱える場合あり)UnimplementedTooManyRequests / ResourceExhaustedServiceUnavailableUnavailableInternalServerError / InternalDeadlineExceededAbortedUnknown(一時障害の可能性がある場合のみ)RetryError: ライブラリ側のリトライが最終的に失敗した状態。cause を確認して最終原因を記録する。GoogleAPICallError: API 呼び出し失敗の基底例外。個別分類できない場合のフォールバックに使う。Cancelled: 呼び出しキャンセル。アプリ終了やユーザー操作由来かを区別して扱う。InvalidArgument などの恒久的エラーは即時失敗として返し、無駄な再試行を避けます。
固定間隔ではなく指数バックオフを使い、少量のランダム値(jitter)を加えて同時再試行の集中を避けます。
無限リトライを避け、max_retries と 1 回あたりの timeout(deadline)を明確に設定します。
作成系・更新系 API は、再送で重複作成や上書きが起きないかを事前に確認します。
例: operation 名、resource 識別子、attempt 回数、例外型、メッセージ。 資格情報やトークンはログに出力しません。
from __future__ import annotations
import random
import time
from collections.abc import Callable
from typing import TypeVar
from google.api_core import exceptions
T = TypeVar("T")
def call_with_retry(func: Callable[[], T], *, max_retries: int = 5, base_delay: float = 0.5) -> T:
"""Call Google Cloud API with categorized exception handling."""
for attempt in range(max_retries + 1):
try:
return func()
except (
exceptions.InvalidArgument,
exceptions.PermissionDenied,
exceptions.Unauthenticated,
exceptions.FailedPrecondition,
exceptions.OutOfRange,
exceptions.Unimplemented,
) as err:
msg = f"Non-retriable Google API error: {type(err).__name__}: {err}"
raise RuntimeError(msg) from err
except (
exceptions.TooManyRequests,
exceptions.ResourceExhausted,
exceptions.ServiceUnavailable,
exceptions.InternalServerError,
exceptions.DeadlineExceeded,
exceptions.Aborted,
) as err:
if attempt >= max_retries:
msg = f"Google API retry exhausted: {type(err).__name__}: {err}"
raise RuntimeError(msg) from err
delay = base_delay * (2**attempt)
jitter = random.uniform(0.0, delay * 0.1)
time.sleep(delay + jitter)
except exceptions.GoogleAPICallError as err:
if attempt >= max_retries:
msg = f"Google API call failed: {type(err).__name__}: {err}"
raise RuntimeError(msg) from err
delay = base_delay * (2**attempt)
jitter = random.uniform(0.0, delay * 0.1)
time.sleep(delay + jitter)
msg = "Unexpected retry loop termination"
raise RuntimeError(msg)
非同期処理では time.sleep ではなく asyncio.sleep を使用してください。
from __future__ import annotations
import asyncio
import random
from collections.abc import Awaitable, Callable
from typing import TypeVar
from google.api_core import exceptions
T = TypeVar("T")
async def call_with_retry_async(
func: Callable[[], Awaitable[T]],
*,
max_retries: int = 5,
base_delay: float = 0.5,
) -> T:
for attempt in range(max_retries + 1):
try:
return await func()
except (
exceptions.InvalidArgument,
exceptions.PermissionDenied,
exceptions.Unauthenticated,
exceptions.FailedPrecondition,
exceptions.OutOfRange,
exceptions.Unimplemented,
) as err:
msg = f"Non-retriable Google API error: {type(err).__name__}: {err}"
raise RuntimeError(msg) from err
except (exceptions.TooManyRequests, exceptions.ServiceUnavailable, exceptions.DeadlineExceeded) as err:
if attempt >= max_retries:
msg = f"Google API retry exhausted: {type(err).__name__}: {err}"
raise RuntimeError(msg) from err
delay = base_delay * (2**attempt)
jitter = random.uniform(0.0, delay * 0.1)
await asyncio.sleep(delay + jitter)
msg = "Unexpected retry loop termination"
raise RuntimeError(msg)
NotFound は「本当に異常」か「存在しないことが正常」かを仕様で明確化する。AlreadyExists は冪等作成の成功相当として扱える場合がある。except しているmax_retries と timeout が固定値で管理されている