バッチ最適化パイプライン設計 INP/品質/スループットを両立 2025
公開: 2025年9月22日 · 読了目安: 1 分 · 著者: Unified Image Tools 編集部
“大量の画像を安全に最適化する”には、単発ツールではなくパイプライン設計が必要です。本稿は INP/UX を損なわずに運用するための設計・運用レシピを、UI/キュー/検証/監視まで丁寧に解説します。
アーキテクチャ概要
- フロント: ドラッグ&ドロップ + 進捗 + キャンセル
- ワーカー: 並列度を制御しつつキュー処理
- ストレージ: 指紋付き命名で差し替えを安全に
INP を悪化させないUI
- アップロードは
fetch()
+AbortController
で中断可能に - 重い処理は Web Worker/Queue に移譲し、メインスレッドは軽く
- 進捗は「推定残り時間」より「処理数/合計数」の事実を表示
画質プリセット
- サムネ: WebP/AVIF 品質中〜高、リサイズ優先
- 写真: AVIF を第一候補、互換のため JPEG/WebP も生成
- UI/ロゴ: PNG/WebP ロスレス、色数削減
変換行列(例)
- 入力: PNG/JPEG/HEIC/TIFF → 出力: AVIF/WebP/JPEG(用途別ビット深度/ICC)
- サイズ: 320/640/960/1200/1600px のプリセット(
sizes
と合致) - 色: P3→sRGB へ明示変換。ICC は sRGB を埋め込み
自動テスト(擬似コード)
// 画質差分の自動チェック
expect(butteraugli(original, output)).toBeLessThan(1.2)
破損検出/構造化データ
// 生成ファイルのメタ検証
expect(hasICC(output)).toBe(true)
expect(readingTime(articleMdx)).toBeGreaterThanOrEqual(1)
公開前チェック
- [ ] LCP候補画像は
sizes
と一致 - [ ] 代替テキスト/著作表記の整合
- [ ] 指紋付きでキャッシュ破棄可能
監視とロールバック
-
生成ジョブはID付与し、失敗時は原本にフォールバック
-
CDNログでフォーマット/サイズ別ヒット率を可視化
-
バイタル(LCP/INP)悪化時はしきい値で自動ロールバック
キュー設計とバックプレッシャ
- 優先度キュー: サムネ/ヒーロー/低優先の3層
- バックプレッシャ: 着信>処理能力のときは受付をスロットルし再試行へ回す
- 可観測化: キュー長・滞留時間・失敗率をダッシュボード化
type Job = { id: string; kind: 'thumb'|'hero'|'bulk'; src: string }
並列度と再開性
- 並列度 N は CPUコア/メモリから算出、画像解像度で動的調整
- チャンク処理: 100件単位でコミット、失敗チャンクのみ再実行
- 再開性: 成果物のハッシュ指紋で冪等化し、途中から再開
const pool = new WorkerPool({ size: Math.max(2, os.cpus().length - 1) })
ストレージ設計(S3/GCS)
- オリジン:
original/
に保存し改変禁止、公開用はpublic/
- メタ:
x-amz-meta-icc: srgb
など最小メタのみ付与 - ライフサイクル: 生成物はアクセス頻度でストレージ階層を移動
リトライ/冪等/期限切れ
- リトライ: 指数バックオフ + ジッタ、最大試行回数を制限
- 冪等:
idempotency-key
にsrc + params
のハッシュを用いる - 期限切れ: 同一キーの古い生成物は GC タスクで削除
コスト最適化
- ホットサイズを先行生成しキャッシュヒット率を上げる
- 品質/幅のプリセット化で失敗作成を削減
- 高負荷時は effort を下げて処理時間短縮(画質は後追い再生成)
オブザーバビリティ
- ログ: jobId, src, fmt, w, q, duration, bytes, error
- 指標: p50/p95/p99 時間、成功率、再試行率、OOM 率
- アラート: p95>既定値 or 失敗率>閾値で通知
例: ワーカー擬似コード
import sharp from 'sharp'
export async function process(job: Job) {
const { src } = job
const buf = await fetchBuffer(src)
return sharp(buf)
.withMetadata({ icc: 'sRGB.icc' })
.resize({ width: 1200, withoutEnlargement: true })
.toFormat('avif', { quality: 60, effort: 4 })
.toBuffer()
}
トラブルシュート
- OOM 発生: 並列度を下げ、巨大画像はストリーミング/タイル化
- 画質ムラ: 目的別プリセットを導入し固定化
- 処理詰まり: 優先度キューでヒーローを先行、低優先は遅延
FAQ
Q. リサイズ幅は細かく用意すべき?
A. sizes
に揃えた有限集合で十分です。細分化はキャッシュ断片化を招きます。
Q. AVIF の effort は高い方が良い?
A. 実用では 3-6 の中庸がコスパ最適です。高すぎるとスループットを損ないます。
まとめ
“並列度の制御”と“画質プリセットの標準化”、そして“検証・監視・ロールバック”まで組み込んだら初めて本番耐性が整います。小さく始め、計測しながら段階的に最適化しましょう。
関連記事
メタデータ
EXIFとプライバシー情報の安全な除去フロー 2025
位置情報やカメラ固有情報などのメタデータを安全に取り扱うための実践ガイド。SNS/ブログ公開前のチェックリストと自動化フローを整理します。
Web
INP中心の画像配信最適化 2025 — decode/priority/スクリプト協調で体感を守る
LCPだけでは不十分。INPを悪化させない画像配信の設計原則とNext.js/ブラウザAPIでの実装手順を体系化。decode属性・fetchpriority・遅延読み込み・スクリプト協調まで。
圧縮
画像圧縮 完全戦略 2025 ─ 画質を守りつつ体感速度を最適化する実践ガイド
Core Web Vitals と実運用に効く最新の画像圧縮戦略を、用途別の具体的プリセット・コード・ワークフローで徹底解説。JPEG/PNG/WebP/AVIF の使い分け、ビルド/配信最適化、トラブル診断まで網羅。