HN 日本語サマリー

← 一覧へ戻る
プログラミング

PyPI Blog: ファイルホスティングエラーのインシデント

PyPI Blog: Incident File Hosting Errors (blog.pypi.org)

4 pointsby miketheman0 コメント

要約

2026年8月に発生したPyPIのファイルホスティングエラーについて、インシデントレポートが公開されました。約2週間にわたり、一部のユーザーがファイルダウンロード時に502および503エラーに遭遇し、インストールの失敗を引き起こしました。原因は、Fastlyネットワーク内の設定ミスと、PyPI側のFastly設定におけるオリジンフォールバックおよびレンジリクエストのバグの組み合わせでした。これらの問題は修正され、8月28日以降、ダウンロードは完全に復旧しています。

全文翻訳

インシデントレポート:ファイルホスティングエラー エグゼクティブサマリー 2026年8月の約2週間にわたり、一部のPyPIユーザーはfiles.pythonhosted.orgからのファイルダウンロード時に断続的な502および503エラーに遭遇し、PyPIからのインストール失敗を引き起こしました。ユーザーがサポートトラッカーに報告を提出してくれたおかげで、特に1つの報告が問題を単一のキャッシュノードに絞り込みました。2つの別々の問題が明らかになりました。Fastlyのネットワーク内でのカナリアデプロイメントが、1つのキャッシュノードで設定ミスを引き起こし、Fastlyのルーティングレイヤーが影響を受けたキャッシュノードに到達するトラフィックに対して502レスポンスを返す原因となりました。別途、オリジンフォールバックとレンジリクエストの動作に関する、私たち自身のFastly設定におけるいくつかのバグを見つけて修正しました。これらはしばらく前から存在していましたが、これらの報告を調査している最中にのみ表面化しました。これら2つの断続的な問題は現在修正されており、8月28日以降、ダウンロードは完全に機能しています。 背景:PyPIのファイルホスティングキャッシュの仕組み ほとんどの人はpip install(または好みのインストーラー)を実行し、それが機能することを期待しています。裏側では、files.pythonhosted.orgは3つのオリジン(または「バックエンド」)の前面にあるFastly CDNサービスです。ファイルがアップロードされると、PyPIはまず耐久性のあるメインコピーとしてAmazon S3に書き込みます。バックグラウンドジョブがそれをBackblaze B2に同期します。これは、FastlyとBackblazeがゼロコストのEgress(データ転送)契約を結んでおり、B2からFastly経由でファイルを配信しても、ストレージ料金以外に追加料金がかからないためです。読み取り(インストーラー)パスでは、FastlyはまずB2を試します。B2が応答しない場合、または予期しない応答を返す場合、FastlyはS3にフォールバックします。PyPIは、ストレージと取得コストのバランスを取るために、Amazon S3 Glacier Instant Retrievalストレージクラスを使用しています。FastlyがフォールバックとしてS3を呼び出す場合、これはB2からよりもコストがかかりますが、一時的な解決策として消費者のために機能し続けます。キャッシュされると、FastlyはB2またはS3を確認する必要がなくなります。ファイルは決して変更されないはずであり、Cache-Controlヘッダーはmax-age=365000000、immutable、public(約11.5年)を設定しています。Conveyorという名前の3番目のバックエンドは、予測可能なURLや、少数のレガシーリダイレクトなど、パッケージファイルリクエスト以外のすべてを処理します。 ```mermaid flowchart TD Client([Client request]) --> Edge{Fastly edge} Edge -->|package file| B2[(B2: egress-free cache)] Edge -->|everything else| Conveyor[Conveyor] B2 -->|200 or 206| Response([Response to client]) B2 -.->|404, timeout, or 5xx| Archive[(S3: origin, fallback)] Archive --> Response Conveyor --> Response ``` このフォールバックパスは、エッジがB2の障害を認識した場合にのみ機能します。私が修正したバグの1つは、それが常に正しく認識しなかったことであり、これが「通常の」エラーノイズに追加されました。 タイムライン 8月15日 Fastlyのレポートによると、問題はこの土曜日にシアトル地域の一つのキャッシュノードで開始されました。これは#11876によって裏付けられています。 8月17日 files.pythonhosted.orgからの持続的な502に関する最初の2つの報告が開かれました (#11895/#11897)。 8月18日 #11908は詳細な再現手順を追加し、6時間で88件の502エラーが記録されたことを示しています。これらは32の無関係なパッケージにまたがり、インストーラーがPEP 658メタデータを読み取るために使用する小さな.whl.metadataレンジリクエストも含まれていました。3つのオープンなネットワークレポートがあり、Datadog Logsで対応するPyPI Filesのエラーを調査しましたが、重要なものは見つかりませんでした。infra#237がマージされ、B2がエラーで応答するのではなく、全く応答しない場合のB2からアーカイブへのフェイルオーバーが修正されました。 8月19日 #11925は、問題を1つのFastlyキャッシュノード、cache-pae2080020に特定しました。このノードにルーティングされたすべてのリクエストに対して19時間以上にわたり502エラーが発生し、失敗したレスポンスのx-served-byヘッダーによって確認されました。Fastlyのネットワークオペレーションは、影響を受けたポイントオブプレゼンスにトラフィックの一部を送信するルーティングオーバーライドを削除しました。infra#238がマージされ、ファイルホスティングサービスのロギング設定が改善されました。infra#239がマージされ、ファイルホストにアクセスすべきでないHTTPメソッドが拒否されるようになりました。 8月20日 Fastlyは影響を受けたポイントオブプレゼンスでの回復を観測し、その後、エラー率の上昇が停止したことを確認しました。 8月21日 infra#241がマージされ、午前中に単一クライアントからの論理的に無効なレンジ送信に起因するスパイクの後、サフィックスおよびマルチレンジリクエストがセグメント化されたキャッシュから除外されました。 8月24日 infra#243がマージされました。2つの壊れた並列ダウンローダーが、1日で同じクラスのエラーを41,315件生成しました。 8月28日 Fastlyは、彼らの側の根本的なカナリア設定バグをパッチし、PyPIを含むすべてのPSFトラフィックをカナリアコホートから除外しました。infra#245がマージされ、URL正規化の順序バグが修正されました。これにより、悪いセグメント化されたキャッシュレスポンスがキャッシュされ、同じファイルに対する後続のすべてのリクエストに配信される可能性がありました。 寄与要因 POP goes the canary 2 Fastlyは、完全なロールアウトの前にキャッシュおよびルーティングソフトウェアを実行するフリートのサブセットであるカナリアコホートを実行しています。PyPIのトラフィックは何年もの間そのコホートの一部であり、Fastlyエンジニアリングが変更を検証するのを助けてきました。カナリアデプロイメント中の部分的なロールバックにより、シアトルの一つのポイントオブプレゼンス(POP)のキャッシュ設定がロールバックされたまま、その前面のルーティング設定はロールバックされませんでした。この不一致により、そのルーティングレイヤーが502を返す原因となりました。Fastlyはその後、PyPIを含むすべてのPSFトラフィックをカナリアプログラムから削除しました。より明確な制御と通知がこのトラフィックに関して存在するようになれば、将来的には再び参加したいと考えています。これは、変更がすべての人に影響を与える前にFastlyがインフラストラクチャの変更を検証するのを助けるための合理的な方法であり、これまであまりコストはかかっていませんでした。 自宅でのバグ それが起こっている間、私は私たち自身の設定で同じ症状(502エラーの増加、およびいくつかのケースでは外部から502のように見える501エラー)を引き起こす無関係なバグを見つけました。上記の図のアーカイブフォールバックは、B2がエラーコードで応答した場合にのみ実行されました。B2が全く応答しなかった場合(タイムアウト、接続拒否、TLS障害)、Fastlyは独自の503を合成し、アーカイブを試みるコードをスキップしました。パッケージファイルは不変であるため、そのケースではアーカイブを試みるパスは存在しませんでしたが、infra#237がそれを追加しました。別途、1ギガバイトサイズのホイール全体をキャッシュにロードしてから部分コンテンツのRangeリクエストを配信することを避けるためにセグメント化されたキャッシュを使用しています。この機能は、一般的なHTTPよりもレンジ構文のサポートが狭いです。サフィックスレンジ(bytes=-1024、最後のNバイトを読み取る)や、レンジの開始が終了を超えているリクエストには対応できず、どちらの場合も合成された501を返します。一部のインストーラーは、ファイル全体をダウンロードせずにホイールメタデータを読み取るために、まさにこの種のサフィックスレンジを使用しているため、それらの読み取りは私がそれらを除外するまで完全に失敗していました。 ```mermaid subgraph Before["Before infra#241 and infra#243"] direction TB C1([Client: suffix or inverted range]) --> E1{Fastly edge} E1 --> S1[Segmented caching] S1 --> R1([501 Not Implemented]) end subgraph After["After"] direction TB C2([Client: suffix or inverted range]) --> E2{Fastly edge} E2 -->|range shape not supported| N2[Segmented caching skipped] N2 --> R2([Normal range handling: 206 or 416]) end Before ~~~ After ``` 不正なクライアントリクエストに対する501は、間違ったステータスコードです。セグメント化されたキャッシュのレンジ処理についてFastlyにサポートチケットを開きました。修正されれば、私の処理コードはおそらく元に戻せるでしょう。別のバグ:既存のセグメント化されたキャッシュ除外チェックは、リクエストURLが正規化される前に実行されていたため、クエリ文字列が付いた.metadataリクエストは一致せず、セグメント化されたキャッシュが有効なままになり、通常のフェッチであるべきものに対してアーカイブバックエンドから501が返されました。 ```mermaid subgraph Before["Before infra#245"] direction TB C3([Client: <code>GET name.whl.metadata<b>?token=x</b></code>]) --> E3{Fastly edge} E3 -->|exemption checked before URL is normalized| S3[Segmented caching stays on] S3 --> B23[(B2)] B23 -->|501 for the 1MiB segment, cached at the edge| R3([Every later request: 501]) end subgraph After2["After"] direction TB C4([Client: <code>GET name.whl.metadata<b>?token=x</b></code>]) --> E4{Fastly edge} E4 -->|URL normalization happens first| N4[Exemption check passes] N4 --> R4([Normal range handling: 206 or 416]) end Before ~~~ After2 ```