0.71 が利用可能になったことを受けて、2022年11月4日に React Native の最初の 0.71 リリース候補(0.71.0-rc0)を公開した際に発生し、すべての React Native バージョンの Android ビルドを破壊したインシデントについて、重要な情報を共有します。本件に対応したコントリビュータはポストモーテム会議を開き、何が起きたか、得られた知見、将来同様の障害を防ぐために取るべき対策を議論しました。以下にその要点をまとめます。
何が起きたか
2022年11月4日、React Native の 0.71.0-rc0 をいくつかの公開リポジトリに公開しました。このリリース候補には、アーティファクトをソースからビルドする代わりに Maven Central に公開することでビルド時間を改善する大きな変更が含まれていました(詳細は RFC#508 および関連ディスカッションを参照)。
しかし、新しいプロジェクトをテンプレートからスキャフォールドする方法のために、古いバージョンを利用している任意の Android ユーザーのビルドが失敗する事態が発生しました。原因は、古いプロジェクトが自分のプロジェクトで使用しているバージョン(例: 0.68.0)ではなく、0.71.0-rc0 のアーティファクトをダウンロードし始めたためです。
なぜ起きたか
React Native のテンプレートは Android アプリをビルドするための build.gradle ファイルを提供しています。このファイルには次のような依存関係が含まれていました:
implementation("com.facebook.react:react-native:+")
重要なのは、この + の部分(Gradle の Dynamic version)が Gradle に利用可能な最も高いバージョンの React Native を選ぶよう指示する点です。Gradle の Dynamic versions を使うことは、再現性の低いビルドにつながるアンチパターンとされています。0.71 では新規アプリテンプレートのクリーンアップを行い、+ 依存を削除しましたが、古いバージョンの React Native を使っているユーザーは依然として + を使っていました。
その結果、0.71.0-rc.0 より前の React Native バージョンでのビルドは、利用可能な最高バージョンの React Native を全リポジトリに対して問い合わせることになりました。Maven Central にプッシュされた新しい 0.71.0-rc.0 が最高バージョンになったため、古いバージョンのビルドが 0.71.0-rc.0 のアーティファクトを使用し始め、ローカルの React Native バージョン(例: 0.68.0)と Maven Central のアーティファクト(0.71.0-rc.0)の不一致によりビルドが失敗しました。詳細は該当の GitHub issue を参照してください。
緩和と解決
問題を特定した 11月4日 の時点で、コミュニティは手動ワークアラウンドを見つけ共有しました。これは React Native を特定のバージョンにピン(固定)するというもので、問題を解決しました。
その後、11月5日〜6日にかけてリリースクルーが 0.63 までの旧バージョンすべてに対してパッチリリースを出し、自動的にパッチが適用されるようにしました。これによりユーザーは修正された React Native バージョンへ更新できるようになりました。同時に Sonatype に連絡して問題のあるアーティファクトの削除を依頼しました。11月8日には Maven Central から該当アーティファクトが完全に削除され、問題は完全に解決されました。
イベントのタイムライン
すべての時刻は GMT/UTC +0 です。
- Nov 4th - 5:06 PM:
0.71-RC0 をリリース。
- Nov 4th - 6:20 PM: ビルド問題の最初の報告がオープン。
- Nov 4th - 7:45 PM: コミュニティが問題を特定。
- Nov 4th - 9:39 PM: ワークアラウンドが共有され、Expo がすべてのユーザーに修正をデプロイ。
- Nov 5th - 03:04 AM: ステータスとワークアラウンドを伝える新しい issue をオープン。
- Nov 6th - 04:11 PM: Sonatype にアーティファクト削除を依頼するチケットをオープン。
- Nov 6th - 04:40 PM: @reactnative からの最初のツイート(認識と issue へのリンク)。
- Nov 6th - 07:05 PM: React Native のバージョンを 0.63 までパッチする決定。
- Nov 7th - 12:47 AM: 最後のパッチリリース
0.63.5 を公開。
- Nov 8th - 08:04 PM: Maven Central 上のアーティファクトが完全に削除される。
- Nov 10th - 11:51 AM: インシデントに関する issue をクローズ。
学んだこと
このインシデントは、React Native の開発とリリースの基盤をより強固にする必要性を示しました。以下は学んだことと、今後のプロセス・インフラ改善に向けたアクションです。
インシデント対応戦略
- 本インシデントはオープンソースの問題に関する我々のインシデント対応戦略のギャップを浮き彫りにしました。コミュニティは2時間以内にワークアラウンドを見つけましたが、被害の範囲に関する可視性不足と古いバージョンへの対応の複雑さのため、我々は GitHub issue に頼る形になってしまいました。
- 影響の大きさを認識するまでに 48 時間かかり、全員が GitHub issue を見つけられるとは限らないため、より積極的に自動的にプロジェクトを修正するような緩和策を優先する必要があることが分かりました。
- 今後は、開発者が手動で適用するワークアラウンドに頼るべきタイミングと、自動でデプロイ可能な修正を優先すべきタイミングの判断を見直します。また、エコシステムの健全性をリアルタイムで把握する手段の検討も行います。
リリースサポート方針
rn-versions ツールの可視化が示すように、当時の React Native 開発者ベースの90%以上をカバーするためには 0.63 までパッチを出す必要がありました。これは過去から続く React Native のアップグレード体験にフリクションが多かったことが原因だと考えています。
- 今後はアップグレード体験を改善し、エコシステムの断片化を緩和する方法を検討します。新しい React Native のリリースが古いバージョンのユーザーに影響を与えてはならず、この点について深くお詫びします。
- また、依存関係や React Native 本体を可能な限り最新に保つ重要性も強調します。今回のインシデント発生時には公式のリリースサポート方針が定義途中で、まだ十分に周知・強制されていませんでした。今後はサポート方針をコミュニケーションチャネルで周知し、npm 上で古いバージョンの非推奨化も検討します。
サードパーティライブラリ向けのテスト強化とベストプラクティス
- 本件は、リリース時のテストやサードパーティライブラリ向けガイダンスの重要性を浮き彫りにしました。
0.63.x までのバージョンに対するリリースは、現在整備されている自動化・テスト基盤が不足しているため困難でした。
- リリースとテストインフラの重要性を認識しており、今後さらに投資します。具体的にはサードパーティライブラリのテストを React Native のリリースプロセスの一部として奨励・支援します。
- Core Contributors の Discord サーバーに新しいチャンネルや役割を追加し、コミュニケーションと協力体制を強化します。
- また、
create-react-native-library のメンテナである Callstack とより密に連携し、ライブラリテンプレートを改良して React Native プロジェクトに統合しやすくする取り組みを始めました。create-react-native-library の新バージョンは 0.71 プロジェクトと完全互換であり、後方互換性も保持しています。
結論
世界中の開発者のワークフローに混乱をもたらしたことをお詫びします。上に挙げた通り、既に基盤強化に向けた対応を始めており、さらに取り組みを進めていきます。このインシデントに関する知見を共有することで、皆さんが自身のツールやプロジェクトでより良い対策を取る助けになれば幸いです。
最後に、アーティファクト削除に協力してくれた Sonatype、対応に尽力してくれたコミュニティ、そしてリリースクルーに改めて感謝します。