代表的な面接トピック

システムデザイン面接:画像変換・配信パイプラインの設計

システム設計難しい
Offer.cc 編集チーム公開日 更新日

質問

オリジナル画像を受け入れ、重複作業や悪意のあるパラメータによるリソース枯渇を発生させることなく、リサイズ、トリミング、圧縮、変換された派生アセットを生成するサービスを設計してください。

問題とスコープ

このプラットフォームは1日あたり1,000万件のオリジナル画像を受信し、クライアントは多様なサイズ、トリミング、出力フォーマットを要求します。オリジナル画像のストレージ、変換ジョブ、派生画像のキャッシュ、およびCDN配信を設計してください。オリジナル画像は再処理可能である必要があります。共同編集レイヤー、コンテンツモデレーションモデル、専門的なカラーキャリブレーションは対象外です。提示されている数値は面接用の前提条件であり、業界のベンチマークではありません。

面接官が評価しているポイント

オリジナル画像、変換リクエスト、派生アセットのアイデンティティ(識別子)を分離できているか。低レイテンシの読み取りと非同期処理がどのように共存するかを説明できるか。優れた回答では、決定論的なキャッシュキー、重複ジョブの統合(coalescing)、キューのバックプレッシャー、ピクセル爆弾および解凍爆弾(decompression bombs)への対策、オブジェクトの認可、コンテンツネゴシエーション、削除後のクリーンアップについて言及されます。

明確化のための質問

  • 任意のサイズ、トリミング、フィルターは許可されますか?ピクセル数、ファイルサイズ、処理時間の制限はありますか?
  • どのフォーマット、品質レベル、カラースペース、アニメーションの動作が必要ですか?
  • 初回のリクエストは同期的である必要がありますか、それともジョブステータスとともに 202 を返してもよいですか?
  • 保持期間、テナント分離、クロスリージョンの要件は何ですか?
  • 結果は公開されますか、それともすべてのURLに認可が必要ですか?

30秒の回答フレームワーク

「私はオリジナル画像をコンテンツアドレス指定のイミュータブルなアセットとして保存し、正規化された変換パラメータを派生キーにエンコードします。読み取り処理ではまずCDNと派生オブジェクトを確認し、キャッシュミスの場合はテナントとオリジナル画像ごとにパーティション分割された冪等なジョブを投入します。ワーカーはピクセル数、メモリ、CPU、解凍サイズ、出力サイズに制限を設けたサンドボックス内で実行され、検証済みのテンポラリオブジェクトをアトミックに公開します。リトライにはプロセッサのバージョンと処理バジェットを持たせ、同一のリクエストは1つのジョブを共有します。アクセスレイヤーは署名付きURL、ネゴシエーション、キャッシュ制御を適用し、削除イベントによって派生インデックスとCDNエントリをクリーンアップします。」

ステップバイステップの詳細設計

アップロードAPIは認証を行い、ダイジェストを検証して、オリジナルオブジェクトを書き込みます。メタデータには asset_id、テナント、コンテンツハッシュ、メディアタイプ、寸法、フレーム数、カラー情報、スキャン状態、保持ポリシーが記録されます。コンテンツの重複排除がテナントの認可チェックの代わりになることは決してありません。ファイル名や Content-Type を信用するのではなく、マジックバイトとデコード可能性を検証します。

寸法の上限、トリミング座標、リサンプリングアルゴリズム、品質、回転、背景、出力フォーマットなどの変換パラメータを安定したシーケンスに正規化します。派生キーは hash(original_bytes, normalized_transform, processor_version) のようになります。プロセッサのアップグレードによってバージョンが変わるため、新旧のアルゴリズムが互いを暗黙的に上書きすることはありません。負の値、NaN、極端なアスペクト比、再帰的フィルターは拒否します。

読み取りパスは、CDN、派生オブジェクトインデックス、ジョブ重複排除テーブルを確認します。利用可能な派生画像がある場合、Cache-ControlETag、および正しいContent-Typeとともに返します。キャッシュミスの場合は、PENDING ジョブを作成し、レイテンシが許容されるならステータスURLを返します。一般的で小さな画像は、厳密に制限された同期パスを使用しても構いません。(tenant_id, derived_key) に対する一意性制約により、同一のリクエストで作業を共有できます。

キューをテナントとオリジナルハッシュによってパーティション分割し、リクエスト数ではなくピクセルコストによってスケジュールします。テナントクォータ、グローバル並行性、優先度付きキューによってバックプレッシャーを提供し、1つの巨大な画像がすべてのワーカーを占有できないようにします。ジョブにはリースとプロセッサバージョンが付与されるため、強制終了されたワーカーを再試行できます。指数バックオフは回復可能な障害に適用し、無効なパラメータやサポートされていないフォーマットは明示的に失敗させます。

処理サンドボックスは、CPU、メモリ、一時ディスク、解凍比率、フレーム数、出力寸法を制限し、内部ネットワークには到達できないようにします。解凍爆弾を防ぐため、デコード後に実際のピクセル数とフレーム数を再計算します。テンポラリオブジェクトを書き込み、ダイジェスト、寸法、フォーマットを検証してから、条件付き書き込みまたはバージョン付き書き込みによってアトミックに公開します。スクリプト実行、パストラバーサル、プライバシー漏洩を防ぐため、SVG、ICCプロファイル、メタデータに対して明示的なポリシーを定義します。

派生アセットはそのキーによってキャッシュします。オリジナル画像の削除や権限変更が発生するとアセットイベントが発行され、派生インデックスとCDNが無効化されます。プライベートアセットには、テナント、派生キー、有効期限、許可されたレスポンスヘッダーをカバーする署名を持つ有効期間の短い署名付きURLを使用します。Vary は、実際に出力を変化させるネゴシエーションディメンションのみに限定してください。任意のクエリパラメータによって無制限にキャッシュバリアントが作成されないようにします。

オリジナル書き込みの成功率、キューの滞留時間、ピクセル加重スループット、キャッシュヒット率、重複ジョブの統合率、変換のp95レイテンシ、障害分類、サンドボックスのピーク値、派生アセットの増加量、CDNの5xxエラーを監視します。調整(リコンシリエーション)プロセスにより、オリジナルメタデータ、ジョブの最終状態、派生インデックス、オブジェクト一覧を比較して孤立オブジェクトを削除します。ワーカーの強制終了、オブジェクトストレージのタイムアウト、重複メッセージ、プロセッサのアップグレード、CDN無効化の失敗、テナントクォータの枯渇などのカオステストを注入します。

質の高い模範解答

「私はオリジナル画像をイミュータブルでアドレス指定可能なアセットとして扱い、正規化されたパラメータとプロセッサバージョンを組み合わせて派生キーを生成します。リクエストはまずCDNと派生オブジェクトを確認し、キャッシュミスの場合は一意性制約のもとで共有ジョブを作成して、既存のジョブまたはステータスURLを返します。キューはテナントとピクセルコストに基づいてスケジュールされます。ワーカーは解凍比率、メモリ、CPU、フレーム数、出力サイズを制限するサンドボックス内で動作し、テンポラリ結果を検証してアトミックに公開します。

プライベートアクセスには、テナント、派生キー、有効期限をカバーする署名付きURLを使用し、パブリックレスポンスには適切な ETagCache-Control、Content-Typeを設定します。削除イベントはインデックスとCDNエントリをクリーンアップします。メトリクスとしてはヒット率、キュー滞留時間、p95レイテンシ、リソースピーク、障害分類をカバーし、調整処理で孤立オブジェクトを検出します。これにより、同一リクエストの再計算を防ぎ、悪意のある画像による容量枯渇を阻止し、プロセッサのアップグレードが古い派生アセットを汚染しないようにします。」

よくある間違い

  • ファイル名をアイデンティティとして使用する → リネームや競合によってアセットが上書きされる → コンテンツハッシュとアセットIDを使用する。
  • 生のパラメータを連結する → 同等なリクエストでキャッシュが断片化する → キー生成前に正規化する。
  • ミスごとに毎回ジョブを作成する → 人気のある画像で計算リソースのスパイクが発生する → テナントと派生キーで統合する。
  • バイトサイズのみを制限する → 解凍爆弾によって巨大なピクセルに展開される → デコード後のピクセル数、フレーム数、解凍比率を制限する。
  • すべてのリクエストを同期的に処理する → 時間のかかる処理が読み取りをブロックする → 非同期キューと制限付きの高速パスを組み合わせる。
  • 最終オブジェクトに直接書き込む → クライアントが不完全な出力を読み取る可能性がある → テンポラリオブジェクトを検証し、アトミックに公開する。
  • URLのみに署名する → パラメータやレスポンスポリシーが改ざんされる可能性がある → テナント、キー、有効期限、ポリシーに署名する。
  • オリジナル画像のみを削除する → プライベートな派生アセットにアクセス可能なまま残る → アセットイベントからクリーンアップを駆動する。

フォローアップの質問と回答

フォローアップ1:なぜキャッシュキーにプロセッサのバージョンを含めるのですか?

アルゴリズムやライブラリが異なると、生成されるバイト列が変わる可能性があるためです。バージョン管理により、新しい結果を共存させつつ、古い結果を計画的に廃止することができます。

フォローアップ2:アニメーション画像はどのように処理しますか?

フレーム数、再生時間、出力ポリシーをコストモデルに含め、合計フレーム数と総ピクセル数に上限を設定し、最初のフレームのサムネイルには別のキーを使用します。

フォローアップ3:202はどのような場合に返しますか?

コストが予測できない場合、出力が大きい場合、またはキューが滞留している場合に、202、ジョブID、およびリトライのガイダンスを返します。サイズが小さく一般的なフォーマットには、厳格な同期処理用バジェットを適用できます。

フォローアップ4:キャッシュポイズニングをどのように防ぎますか?

サーバー側でのみ正規化されたパラメータからキーを生成します。公開前にタイプ、長さ、ダイジェストを検証し、プライベートキャッシュをテナント認可に紐付けます。

フォローアップ5:オリジナル画像を置き換えた後、古いURLはどうなりますか?

イミュータブルなバージョンIDを使用します。置換処理によって新しいアセットまたはバージョンイベントが作成されます。古い派生アセットは、暗黙的にバイト列が変わるのではなく、保持ポリシーに従って残存するか期限切れになります。

フォローアップ6:コストの公平性はどのように維持しますか?

デコードされたピクセル数、出力ピクセル数、フィルターの複雑さに応じて課金します。テナントのトークンバケット、並行性の上限、日次バジェットによって高コストな処理を制御し、監査可能なクォータレスポンスを生成します。

公開情報ソース

関連する質問

関連面接ツール

システム設計の回答には「回答する」を使用

まず要件を明確にし、スケール、アーキテクチャ、コンポーネント選定、トレードオフの順に進めます。

ツールを見る