クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

クラウド呼び出しのエラーと再試行

このレッスンのあとで: 再試行するエラーと、直すエラーを、見分けることができる。

55分のレッスンコマンドなし

待てば直るエラーがある

クラウドのサービスは、どの呼び出しにも答えますが、その答えがエラーのこともあります。エラーコードを見れば、どの種類かがわかります。

存在しない S3 バケットは、もう一度聞いても、存在しません。混み合っている DynamoDB の表は、1秒後に答えるかもしれません。このレッスンでは、2つを見分け、再試行してよいほうだけをやり直すことを学びます。

このレッスンのゴール

メモ

再試行するエラーと、直すエラーを、見分けることができる。

キーワード

ことばいみれい
エラーコード何が悪かったかを表す、短い名前NoSuchKey
ステータスコード答えを種類ごとに分ける数404
スロットリング呼び出しが多すぎるので、サービスが断ること
再試行同じ呼び出しを、もう一度送ること
バックオフ再試行のたびに、待つ時間を長くすること
ジッター待つ時間のうち、ランダムな割合。呼び出し側が同時に動かないようにする

学生が入力するセルは、このプレビューでは空白です。

エラーには、コードと状態がある

呼び出しが失敗すると、boto3 はエラーを渡します。エラーには、404 のようなステータスコードと、NoSuchKey のようなエラーコードがあります。

調べるのは、エラーコードと状態です。メッセージは調べません。文言はサービスによって変わることがあり、エミュレーターの文言は、本物と違う場合があるからです。

例: 取り込んだ答えのエラー

呼び出しCode状態再試行するか
get_object、キーがないNoSuchKey404しない。キーを直す
head_object、キーがない404404しない。読む本文がない
list_objects_v2、バケットがないNoSuchBucket404しない。バケットを作る
put_item、条件が偽ConditionalCheckFailedException400しない。その答えが答え
query、キーの名前がちがうValidationException400しない。呼び出しを直す
send_message、キューがないAWS.SimpleQueueService.NonExistentQueue400しない。キューを作る

学生が入力するセルは、このプレビューでは空白です。

実行: コードと状態を読む

コード: python
answers = [
    {"error": {"Code": "NoSuchKey", "Message": "Missing.", "HTTPStatusCode": 404}},
    {"error": {"Code": "ConditionalCheckFailedException", "Message": "No.", "HTTPStatusCode": 400}},
    {"error": {"Code": "InternalServerError", "Message": "Oops.", "HTTPStatusCode": 500}},
    {"Items": []},
]
for answer in answers:
    if "error" not in answer:
        print("成功")
    else:
        error = answer["error"]
        print(error["Code"], error["HTTPStatusCode"])

次はうまくいくものだけを再試行する

エラーには、「今はだめ」と言うものがあります。スロットリングは、呼び出しが多すぎる、という意味です。状態が 500 以上なら、サービスの側の故障です。どちらも、待てば通るかもしれません。

ほかのエラーは、「この呼び出しはだめ」と言います。呼び出しが間違っているので、同じ呼び出しは、何度でも失敗します。再試行せず、直します。

よくある間違い

注意

念のため、どのエラーも再試行する人が多いです。すると、間違ったキーが5回失敗し、待つ時間が無駄になります。再試行するのは、スロットリングとサーバーの故障だけです。ほかは、同じ呼び出しが同じ答えになるので、直します。

実行: 再試行と修正に分ける

コード: python
# スロットリングの Code は文書にあるもので、ここで取り込んだものではありません。
THROTTLING = ["ThrottlingException", "ProvisionedThroughputExceededException", "SlowDown"]

def worth_retrying(error):
    if error["HTTPStatusCode"] >= 500:
        return True
    return error["Code"] in THROTTLING

errors = [
    {"Code": "NoSuchKey", "HTTPStatusCode": 404},
    {"Code": "SlowDown", "HTTPStatusCode": 503},
    {"Code": "ThrottlingException", "HTTPStatusCode": 400},
    {"Code": "ValidationException", "HTTPStatusCode": 400},
]
for error in errors:
    print(error["Code"], worth_retrying(error))
# THROTTLING に "RequestLimitExceeded" を足して、試しましょう。

バックオフは、待ちを長くする

サービスが混んでいるときに、1ミリ秒後に再試行すると、もっと混みます。だから待ちます。失敗のたびに、待ちを長くします。待ちを2倍にするのが、よくある方法です。上限も決めて、待ちが限りなく伸びないようにします。

さらにジッターを足します。その時間の、ランダムな割合だけ待つ方法です。ないと、100の呼び出し側が同時に起き、同時にまた失敗します。

例: 各回の待ちの上限

回2倍上限 8 のときpick が 0.5 のときの待ち
10.5 秒0.50.25
21 秒10.5
32 秒21
44 秒42
58 秒84
616 秒84

学生が入力するセルは、このプレビューでは空白です。

実行: テストできる待ち

コード: python
# pick は 0 から 1 までの数です。本物の呼び出し側は、random.random() を渡します。
# テストは決まった数を渡すので、答えがいつも同じになります。
def backoff(attempt, base, cap, pick):
    ceiling = min(cap, base * 2 ** (attempt - 1))
    return ceiling * pick

for attempt in range(1, 7):
    print(attempt, backoff(attempt, 0.5, 8, 1.0), backoff(attempt, 0.5, 8, 0.5))
# 上限を 8 から 2 に変えましょう。どの回が変わりますか。

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。