プロモートフレーム訪問時のみ previousFrameElement を保持する最適化

hotwired/turbo

Turbo のフレームコントローラは、すべてのフレーム描画でフレーム全体のクローンを作成し続け、不要なメモリ保持とオブザーバー生成を引き起こしていました。この PR は、data-turbo-action が設定されたプロモートフレーム訪問 のみでクローンを取得するよう条件分岐を追加し、メモリ使用量の肥大化を防ぎます。

背景

FrameController#willRenderFrame は従来、currentElement.cloneNode(true) によってフレームの現在サブツリー全体を深くコピーし、this.previousFrameElement に格納していました。コピーは visitCachedSnapshot がキャッシュ復元時にだけ消費し、他の描画パス(src の変更や refresh="morph"、ブロードキャスト駆動のリロード)では決して使用されずに残っていました。その結果、フレームが頻繁にリロードされると、ピン留めされたクローン毎回の cloneNode(true) による子フレームの FrameControllerAppearanceObserverIntersectionObserver の生成 が累積し、ヒープスナップショットで多数のオブザーバーが残留していることが確認されました。

技術的な変更

willRenderFramethis.action が存在する場合のみ クローンを作成する guard を追加しました。this.actionproposeVisitIfNavigatedWithAction が設定するため、プロモートフレーム訪問時にのみ真となります。コード変更は次の通りです。

@@ -299,7 +299,15 @@ export class FrameController {
   // Frame renderer delegate

   willRenderFrame(currentElement, _newElement) {
-    this.previousFrameElement = currentElement.cloneNode(true)
+    // Only clone the frame for promoted frame visits, where visitCachedSnapshot
+    // consumes the clone to restore the frame's contents into the cached page
+    // snapshot (and deletes it). For plain frame renders — src changes without
+    // a data-turbo-action, refresh="morph" reloads, broadcast-driven reloads —
+    // nothing ever consumes or clears the clone, so it would pin a complete
+    // copy of the frame's previous subtree on the controller indefinitely.
+    if (this.action) {
+      this.previousFrameElement = currentElement.cloneNode(true)
+    }
   }

   visitCachedSnapshot = ({ element }) => {

この guard により、通常のフレーム描画 では previousFrameElement が未設定のまま残り、不要なメモリ保持が回避されます。テストスイートへの影響はなく、autofocus_tests.js に軽微な待機処理が追加されただけで、既存の機能テストはすべて成功しています。

設計判断

今回の修正は 最小限の侵入性 を保ちつつメモリリークを解消することを狙い、this.action という既存フラグを再利用しています。代替案としては visitCachedSnapshot の呼び出し側でクローン生成を制御するか、previousFrameElement の保持自体を廃止する設計が考えられましたが、後者は多数の既存コードへの広範な変更が必要となります。this.action 判定のみを追加することで、プロモート訪問の動作は変わらず、それ以外のケースでの不要なオブジェクト生成が排除され、リグレッションリスクを低減しています。

まとめ

この PR は、Turbo フレームの 不要なクローン保持 をプロモートフレーム訪問時に限定することで、メモリ使用量とオブザーバー生成コストを大幅に削減しました。機能的な挙動は変わらず、既存テストをすべてパスする安全な最小変更です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
bc070ef1

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文→背景→技術的な変更→設計判断→まとめの構成が明確で、必要な要素がすべて揃っています。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

GitHubリンクは「[PR #1542](URL)」の形となっており、#記号はあるものの推奨の "[#1542](URL)" 形式から外れています。シンタックスハイライトは問題ありません。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

専門エンジニア向けの技術的記述で、過度な初心者向け解説はなく適切です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論・各論・結論の流れがあり、段落はトピックセンテンスで始まり、1段落1トピックで適切な長さです。空行も確保されています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中の diff ブロックは提供された Diff と一致しており、コード変更内容も正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている用語は PR 内容と合致しており、誤用はありません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的な説明は PR の記述と合致し、根拠が明示されています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

全ての主張は PR の説明・Diff に裏付けられており、捏造や憶測は見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

数値や識別子(PR番号 #1542 等)は正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR タイトルの意味を正しく日本語化しており、一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

外部知識(バージョンサポート等)は一切言及されておらず、PR 内容の範囲内に留まっています。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現のずれはなく、PR の記述と整合しています。