Slide 1

Slide 1 text

⼊⾨ リトライ 再試⾏を制するものは信頼性を制するかもしれない編 渡部⿓⼀ Road to SRE NEXT@札幌

Slide 2

Slide 2 text

● リトライの重要性 ● 実装編 ● どのレイヤーでやるの? ● TCP再送制御 ● まとめ アジェンダ

Slide 3

Slide 3 text

● 渡部⿓⼀ ● 株式会社IVRy SRE ● X: @ryuichi_1208 ● SRE NEXT Co-Chair ● 札幌は4回⽬ ● 障害対応‧EOL対応 ⾃⼰紹介

Slide 4

Slide 4 text

株式会社 IVRy ● 対話型⾳声AI SaaS ○ 最⾼の技術をすべての企業に届ける ● Architectureプロジェクト SREチーム ○ 全⼈類が電話をかけてきても耐えられるサービスを⽬指す

Slide 5

Slide 5 text

リトライの重要性

Slide 6

Slide 6 text

リトライしてますか?

Slide 7

Slide 7 text

● 1台の物理マシンかつ1プロセスで完結するサービスは少ない ● モノリス、マイクロサービス ● プロセス間通信、LAN内の通信、インターネット越しの通信 ● プロセス内からプロセス間へ 現代のアプリケーション

Slide 8

Slide 8 text

● 同⼀のプロセス内で⾏われるデータやメソッドの呼び出しを指す ● 以下の理由で⾼速 ○ メモリ空間を共有する ○ オーバーヘッドが⾮常に少ない ○ 迅速なデータの転送(メモリ内での参照渡しが可能) ● 10,000回呼び出せば10,000回成功する プロセス内通信

Slide 9

Slide 9 text

● パイプ ● メッセージキュー ● 共有メモリ ● セマフォ ● シグナル ● ソケット プロセス間通信 (IPC)

Slide 10

Slide 10 text

● ネットワークの信頼性 ○ ネットワークは障害が起きるし遅延も起きる ■ ケーブルや中間機器の故障、プロトコルのオーバーヘッド、 ■ 帯域、ルーティング、トラフィックの競合 ● それが原因でネットワーク越しのプロセス間通信の失敗を引き起こす可能性がある プロセス間通信における信頼性

Slide 11

Slide 11 text

● そういった障害は相⼿のサーバが⼀時的に繋がらなかっただけの可能性もある ○ ⾼負荷によって繋がらない ○ デプロイ起因などで障害が起きていた ○ OOMとかでプロセス再起動が発⽣して瞬断が発⽣していた ● 時間が経ったら正常に繋がるようになる可能性がある プロセス間通信における信頼性

Slide 12

Slide 12 text

リトライしたら成功しそう!

Slide 13

Slide 13 text

全部リトライだ!

Slide 14

Slide 14 text

● そんなことはない ● プロセス間通信で発⽣するエラーには、「⼀時的なエラーと永続的なエラー」がある ● エラーに合わせてリトライ可否を判断する必要がある エラーは全てリトライすれば良いのか?

Slide 15

Slide 15 text

● 失敗が⼀時的なものであり、リトライによって回復する可能性がある ○ ネットワークの瞬断 ○ サーバ側の⼀時的な⾼負荷 ○ タイムアウトや接続の遅延 ○ HTTPレスポンスコード ■ 502 Bad Gateway ■ 503 Service Unavailable ■ 504 Gateway Timeout ⼀時的なエラー

Slide 16

Slide 16 text

● エラーの原因が解決可能でない、または根本的な問題がある場合であり、リトラ イを試みても問題が解決しないことが多い ○ 不正なデータの送信、要求の送信 ○ HTTPレスポンスコード ■ 400 Bad Request ■ 401 Unauthorized ■ 404 Not Found(キャッシュで⾒れないとかはあり得る) 永続的なエラー

Slide 17

Slide 17 text

⼀時的なエラーは全部リトライだ!

Slide 18

Slide 18 text

● リトライは必ずしも安全ではない ● 冪等でないAPIは、同じリクエストを複数回実⾏すると、異なる結果が⽣じる可 能性がある ○ 例えば、データベースへの書き込み操作が冪等でない場合、リトライによっ て意図しないデータの重複や⽭盾が発⽣するかもしれない ● リトライを⾏う場合は、APIをべき等になるように設計することが推奨される 冪等性も重要

Slide 19

Slide 19 text

● サーバ側の操作がRDBとのやり取りだけならBEGIN ~ COMMITで括る ○ トランザクションが失敗した場合リトライ ● 外部のAPIとかDBを2つ使うとか普通にあるのでRDBのようには⾏かない ○ RDSに書いてDynamoDBにも書いて〜みたいなやつ ○ 分散トランザクション...?? ■ XA, 2PC, Paxos, Raft ● ロールバックを考慮すると操作単位で冪等性を担保しておく トランザクションでなんとかなる...のか?

Slide 20

Slide 20 text

リトライの実装

Slide 21

Slide 21 text

素朴にリトライ

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No Sleepでリトライ

Slide 24

Slide 24 text

● クライアントが⼀時的な障害を検知し、⼤量のリクエストをリトライ ● サーバーが⾼負荷になり、さらに レスポンスが遅延‧エラー増加 ● クライアントが 「まだ応答がない」 とさらにリトライ ● 結果、システム全体が スローダウンする リトライの連鎖

Slide 25

Slide 25 text

● サーバーやネットワークに問題が発⽣した場合、複数のクライアントでほぼ同時 に問題が起こることがある ● 通常時は1,000[req/sec]のところが2,000[req/sec]とかになり得る ● リトライ間隔にブレを持たせることで、リトライのタイミングをある程度分散さ せることが期待できる リトライにおけるsleep(待機)の重要性

Slide 26

Slide 26 text

● 待機時間が指数的に増加するため、初回のリトライ後に少しだけ待機し、次第に リトライ間隔を⻑くする ○ 1回⽬: 2秒、2回⽬: 4秒待機、3回⽬: 8秒待機 ● 最初のリトライではサーバが⼀時的に回復する時間を与え、徐々に負荷が減少す る可能性が⾼まる Exponential Backoff(指数バックオフ)

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

● デジタル信号における「揺らぎ」のこと ● リクエストのタイミングをランダムにずらす ○ 複数のクライアントが同じタイミングでリトライを⾏うのを避けるために使 ⽤する ● 指数バックオフと組み合わせることで、さらに効果的に動作する jitter

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

● HTTPS通信の場合はDNSで名前解決して、TCPのハンドシェイクしてTLSのハン ドシェイクしてデータを送信する ● どこのレイヤーでエラーが起きているのか次第では何をリトライすれば良いのか がわかる ○ DNSで名前解決したIPの宛先が落ちていた場合 ○ リクエスト処理中のサーバ側が原因の場合 ● TCP接続からやり直すのが良いのか接続後のHTTPリクエストだけをやり直せば良 いのかはそれぞれ別で考えておく 何をリトライする?

Slide 32

Slide 32 text

● Exponential Backoffの課題としてレイテンシの増加やスループットの低下 ● 再送間隔を増加させるためレイテンシがどんどん⻑くなる ○ ユーザー体験に悪影響を与える可能性がある ○ リトライ間隔は指数関数的バックオフを使うとともに、最⼩待機時間を 50msに設定し、最⼤待機時間を200msに制限 ■ DynamoDBとかだと推奨されている設定 SLO: p99.99で500msのサービスでリトライは?

Slide 33

Slide 33 text

● アーキテクチャの最適化 ○ インターネット越しのAPI通信はネットワーク遅延や不安定さが原因でリト ライが増えがち ○ インターネットを通さないようにする、キャッシュ、フォールバック ● SLI/SLOの⾒直し ○ ユーザー体験を考慮し、「リトライしても成功しない可能性が⾼い場合は、 最初のエラーで諦める」設計 ○ 機能⾃体を縮退してユーザーには最低限レスポンスを返す ■ 重要でない操作は再試⾏してスループットに影響を与えるよりも、フェ イルファストする SLO: p99.99で500msのサービスでリトライは?

Slide 34

Slide 34 text

どのレイヤーでやる?

Slide 35

Slide 35 text

● 1つの⾔語、1つのアプリケーションが1つのサーバとだけやり取りするならアプ リケーションコードで実装すれば良さそう ● 数種類の⾔語で書かれた100以上のアプリケーションがそれぞれ100以上のサーバ とプロセス間通信するようになったら?? ● 指数関数的にリトライロジックを実装する必要が出てくる ● アプリのビジネスロジック開発に集中したい! リトライの設定

Slide 36

Slide 36 text

● インフラ層でリトライやトラフィック管理を⼀元化する ○ Envoy や Nginx などのプロキシ ● アプリケーションはGatewayに対してHTTPリクエストを投げておくだけでイン フラ層でリトライ制御を実装してくれる ● リトライ制御だけでなくCircuit Breakerとしても働く ○ 障害発⽣時にリトライを⼀定時間停⽌し、過負荷状態を防ぐ 役割 ● リトライが発⽣したかの監視もやりやすかったりする ○ 特定の時間に⼤量にリトライが発⽣していたなどを後から追える API Gateway / Service Proxy

Slide 37

Slide 37 text

● BerriAI/litellm ● OpenAI形式の100以上のLLM(OpenAI、Bedrock)への呼び出し ● リトライ制御以外にも予算やレート制限の設定などを管理できる ● LLMを使ったプロダクトに置いてリトライ制御やフォールバックのはとても重要 (コラム)LiteLLM Proxy

Slide 38

Slide 38 text

TCPの再送制御

Slide 39

Slide 39 text

● HTTP/1.1, HTTP/2他の多くのネットワークプロトコルでも多く使われている ● TCPは、接続型のプロトコルであり、送信側と受信側の間でデータが順序通り に、かつ信頼性を持って送信されることを保証している ● データが確実に⽬的地に到達することを保証するために、さまざまな仕組みを提 供している ● RFC 9293でも「3.8.1. Retransmission Timeout」として書かれている ● synやackが届かない場合のリトライについても決まっている TCPの再送制御

Slide 40

Slide 40 text

● 再送タイムアウト(RTO) ○ 再送は、RTO(Retransmission Timeout)というタイマーを基に⾏われる ○ 送信側はデータを送信後、⼀定の時間内にACKを受け取れなかった場合、そ のデータを再送する ○ RTOの時間は、通常、ネットワークの遅延や輻輳状態によって動的に決まる ○ OSごとにデフォルトだったり実装が異なる ■ Linuxではカーネルパラメータで設定をすることができる ● tcp_syn_retries ● tcp_retries2 TCPの再送制御

Slide 41

Slide 41 text

https://ryuichi1208.hateblo.jp/entry/2021/02/10/000000_1

Slide 42

Slide 42 text

まとめ

Slide 43

Slide 43 text

● ネットワーク越しに通信を⾏うアプリケーションの信頼性を⾼めるためにリトラ イは重要な技術の⼀つ ● サービスレベルに合わせたタイムアウトや再送間隔の調整も重要 ● リクエストの殺到を防ぐためにsleep(待機)が重要であり、Exponential Backoffやジッターの追加が有効 まとめ

Slide 44

Slide 44 text

参考書籍

Slide 45

Slide 45 text

● SRE サイトリライアビリティエンジニアリング / オライリージャパン ● マイクロサービスアーキテクチャ 第2版 / オライリージャパン ● 実⽤ Go⾔語 / オライリージャパン ● UNIXネットワークプログラミング Vol.1 第2版 / ピアソン‧エデュケーション ● TCP技術⼊⾨ / 技術評論社 ● マイクロサービスパターン / インプレス 参考書籍

Slide 46

Slide 46 text

ご清聴ありがとうございました