代表的な面接トピック

PerformanceObserver を使用して信頼性の高いリアルユーザーモニタリングを構築するにはどうすればよいですか?

フロントエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

シングルページアプリケーション向けのリアルユーザーパフォーマンス収集を設計してください。LCP、リソース、ロングタスクのデータを収集し、レポート送信コストを抑制し、PerformanceObserver のバッファ損失、クロスオリジンのタイミングの欠落、ルート変更をまたぐ重複帰属を検出する必要があります。

1. 設問

世界中からアクセスされるシングルページアプリケーションにおいて、デバイス、ネットワーク、リリース間での体験を比較するためにリアルユーザーパフォーマンスデータが必要です。ページセッションごとのレポート送信を制限しながら、LCP、リソース読み込み、ロングタスクのデータを収集してください。PerformanceObserver コレクターを設計し、オブザーバー登録前に作成されたエントリ、バッファ損失、クロスオリジンリソース、SPA のルート変更をどのように処理するかを説明してください。

2. 制約と確認事項

  • 各メトリクスの対象期間(初回ドキュメント、ソフトナビゲーション、セッション全体など)を定義します。
  • 収集処理がページに影響を与えないよう、サンプリングレート、タイプごとのエントリ上限、バッチサイズ、失敗時の再試行ポリシーを設定します。
  • ブラウザのサポート状況の違いとデータの欠落を区別します。欠落した値には理由が必要であり、ゼロにしてはなりません。
  • リソース URL、ユーザー識別子、クエリディメンションに対するプライバシー境界を定義します。完全な URL や機密パラメータを直接報告しないでください。

3. コアコンセプト

PerformanceObserver は、選択されたエントリタイプのエントリを受信します。buffered: true は、オブザーバー作成前に記録されたエントリを取得できます。リソース、ロングタスク、ペイント、LCP の各タイプにはバッファ制限があり、コールバックはバッファがいっぱいになって破棄されたエントリを把握するために droppedEntriesCount を受信できます。LCP は、読み込み中にレンダリングされた最大の画像またはテキストブロックのレンダリング時間です。ユーザー入力の後、それ以降のコンテンツを初期 LCP として扱ってはなりません。適切な Timing-Allow-Origin レスポンスヘッダーがない場合、クロスオリジンリソースのタイミングデータは制限されるか欠落する可能性があります。

4. 参照実装

text
startCollector():
  session = createSessionId()
  observe("largest-contentful-paint", {buffered: true})
  observe("resource", {buffered: true})
  observe("longtask", {buffered: true})

observe(type, options):
  if type not in PerformanceObserver.supportedEntryTypes:
    markUnsupported(type)
    return
  observer = new PerformanceObserver((list, _, dropped) =>
    appendSanitized(list.getEntries())
    if dropped > 0: markDropped(type, dropped)
    flushWhenBatchIsReady()
  )
  observer.observe({type, ...options})

onSoftNavigation(route):
  closePreviousView(route)
  resetViewScopedMetrics()

エントリタイプごとに個別のオブザーバーを登録し、buffered を使用して初期のレコードを取得します。コールバックは必要なフィールドのみを保持し、URL パラメータを削除してバッチ送信します。各ビューには、オブザーバーの登録時刻、ルート、リリースが保存されます。ソフトナビゲーションが発生すると、以前のビューを閉じてビュースコープのメトリクスをリセットします。セッションスコープのリソースやエラー数は、明示的な重複排除キーを使用して継続します。

5. データ品質とコストのトレードオフ

サンプリングレートを高くすると分位数は安定しますが、ネットワークとストレージのコストが増加します。LCP とロングタスクには高めのサンプリングを維持し、リソースエントリには先頭サンプリングを適用するか、低速なリソースのみを保持します。1 つの resource バッファはデフォルトで最大 250 件のエントリを保持するため、コレクターはレポート頻度をむやみに増やすのではなく、ページのライフサイクル中にバッファを消費し、損失を記録する必要があります。バッチ送信失敗後のクライアントの再試行を制限し、ローカルキューが起動を遅延させないよう、次回の訪問時に古いバッチを破棄します。

6. 検証と可観測性

  • オブザーバー登録前にエントリを作成して buffered を検証し、リソースの多いページを使用して droppedEntriesCount アラートを検証します。
  • Timing-Allow-Origin がある場合とない場合でクロスオリジンリソースをテストし、フィールド欠落の理由が区別可能なままであることを確認します。
  • 初回読み込み、ソフトナビゲーション、バックグラウンドからの復帰、ページの非表示化を個別に再現し、LCP やリソースエントリが複数のビューに帰属していないことを検証します。
  • コレクターの CPU、メモリ、レポートバイト数、バッチ失敗率、エントリ損失率、サポート率を監視します。

7. よくある間違い

  • load イベントの後にのみオブザーバーを作成し、初期の LCP やリソースエントリを完全に逃してしまう。
  • droppedEntriesCount を暗黙的に無視しながら、不完全なサンプルを完全な分布として扱う。
  • 制限されたクロスオリジンのタイミングを実際のレイテンシゼロとして扱い、パフォーマンス分位数を破損させる。
  • SPA のルートごとに古いオブザーバーを閉じずに新しいオブザーバーを作成し、重複レポートやメモリリークを引き起こす。

8. 面接の評価ポイント

オブザーバーのライフサイクルを正しく使用しているか

初期損失や重複帰属を避けるために、bufferedsupportedEntryTypes、オブザーバーのシャットダウン、ソフトナビゲーションの境界について言及できているか。

バッファおよびクロスオリジンの制限を処理しているか

候補者が droppedEntriesCount を監視し、リソースバッファの上限を把握し、Timing-Allow-Origin を使用して欠落しているクロスオリジンフィールドを説明できるか。

制御されたサンプリングとレポートを設計しているか

メトリクス値ごとにサンプリング、エントリ上限、バッチ処理を割り当て、再試行がページを遅延させない理由を説明できるか。

データの信頼性を検証しているか

コレクターのオーバーヘッドと損失を測定しながら、初期エントリ、リソースの多いページ、クロスオリジンのタイミング、ソフトナビゲーション、バックグラウンド復帰をカバーできているか。

公開情報ソース

関連する質問