TT Lab
はじめる
学ぶ 学習パス コース

EAI 中間層をつくる

遅い相手ひとつにハブを引きずらせない

TT Labで続きを見る

目標

サーキットブレーカーと対象別のバルクヘッドで、遅い、あるいは死んでいる対象1つがハブ全体を引きずり下ろさないようにします。

なぜ重要なのか

タイムアウトは、1件をどれだけ待つかを決めるだけです。死んでいる対象を呼び出し続けると、待機するスレッドが溜まってハブが先に息詰まり、関係のない取引まで一緒に止まります。早く拒否する仕組みが対象ごとに別々にあって初めて、障害が広がりません。

ステップ

  1. /root/eaimw/breaker/breaker.pyにCircuitBreaker(failure_threshold, reset_timeout, clock=time.monotonic)を作成してください。state(CLOSED・OPEN・HALF_OPEN)、allow()、record_success()、record_failure()を持ちます。連続失敗が基準に達したらOPEN、OPENならallow()はFalseです。時刻は必ずclock()で読みます(採点ツールがフェイククロックを渡します)。
  2. OPENでreset_timeoutが経過したら、allow()が1回だけTrueを返してHALF_OPENになるようにしてください。試験呼び出しが成功したらCLOSED、失敗したら再びOPEN(時間は最初から)です。複数のスレッドが同時に尋ねても、1件だけを許可します(ロック)。
  3. cp /opt/lab/fixtures/eaimw/breaker/relay_base.py /root/eaimw/breaker/relay.pyで始めて、--fail-threshold(既定は5)・--reset-timeout(既定は10)引数を追加し、対象の呼び出しをブレーカーで包んでください。開いていれば呼び出さず、すぐE904を返します。このステップでは、0000以外の結果を失敗として数えます。
  4. --max-inflight(既定は10)の同時処理数の上限を設けてください。上限に達していたら、待たずにすぐE905を返します。
  5. ブレーカーと同時処理数の上限を、対象(CORE・CLAIM)ごとに別々に置いてください。勘定系の故障や飽和が、請求の取引に広がってはいけません。
  6. --breaker-log(既定は/root/eaimw/breaker/breaker.log)に、状態が変わるたびに<시각>|<대상>|<FROM>-><TO>(プレースホルダーは時刻と対象です)を1行残してください。
  7. 失敗として数えるものを、システムエラー(E500・E901・E902)に絞ってください。業務上の拒否(B2xx)は、対象が健全に答えたものなので、成功として数えます。

参考

連続失敗が溜まったら回路を開く

/root/eaimw/breaker/breaker.pyのCircuitBreakerが、連続失敗の基準でOPENになり、OPENならallow()がFalseを返すようにしてください。

成功したら失敗数を0に戻して「連続」を数えます。開いた時刻はtime.time()ではなくself.clock()で記録してください。採点ツールが時計を握ります。

1件で探りを入れて閉じる

reset_timeoutのあとHALF_OPENで試験呼び出しを1件だけ許可し、成功ならCLOSED、失敗なら再びOPENにしてください。

allow()の中で、OPENかつ時間が経過していればHALF_OPENに変え、試験呼び出しがすでに出たかの印を持ちます。2つのスレッドが同時に尋ねても、片方だけがTrueを受け取るよう、ロックの中で判断してください。

開いていれば呼び出さない

relay_base.pyをコピーして対象の呼び出しをブレーカーで包み、開いていればE904ですぐ答えてください(--fail-threshold・--reset-timeout)。

handle_txが包む場所です。allow()がFalseならcall_targetを呼ばずに戻り、呼び出したあとは結果に応じてrecord_success/record_failureを呼びます。

上限を超えたら待たない

--max-inflightの同時処理数の上限を設け、上限に達していたらすぐE905で答えてください。

BoundedSemaphoreのacquire(blocking=False)がFalseなら、上限に達しています。呼び出したあとは、例外が出てもreleaseされるように、try/finallyを使います。

対象ごとにバルクヘッドを分ける

ブレーカーと同時処理数の上限をCORE・CLAIMの対象ごとに別々に置き、勘定系の故障・飽和が請求に広がらないようにしてください。

1つずつ置いていたものを、対象名をキーにした辞書に変えます。隔壁が1つなら、1区画の水が船全体に広がります。

状態遷移を記録する

--breaker-logに、状態が変わるたびに「時刻|対象|FROM->TO」を1行残してください。

allow()の前後、recordの前後のstateを比較し、違っていれば1行書きます。複数のスレッドが書くので、ロックを取って書きます。

業務上の拒否は失敗ではない

失敗として数えるものをE500・E901・E902に絞り、B2xxは成功として数えてください。

残高不足は、勘定系が健全に答えたものです。これを失敗として数えると、月末の業務上の拒否だけで、正常な勘定系へ向かう道を断ち切ってしまいます。