はじめに:リアルタイム価格が重要な理由

大規模Eコマースにおいて「リアルタイム価格」は単なる機能以上の意味を持ちます。ブラックフライデーのような大規模トラフィックイベントで価格が1時間も同期されないと、顧客はカートで異なる価格を目にして「カートショック(Cart Shock)」を経験します。これは単なるバグではなく、アーキテクチャの根本的なレイテンシ問題です。

Samsung.comはスマートフォン、TV、家電など多数のバリエーションと地域別価格を管理する複雑な環境で、この問題を解決する必要がありました。本記事では、レガシーなデータ集約(DA: Data Aggregation)アーキテクチャを解体し、AWS Lambda Response StreamingとAmazon CloudFrontを活用したステートレスストリーミングアーキテクチャへの移行プロセスを詳しく解説します。

現場適用のポイント: 単なる「キャッシュクリア」ではなく、中間ストレージ層そのものを排除する根本的な解決策を提示しています。日本企業のシステムでもよく見られる「バッチ同期遅延」問題を解決する良い参考事例です。

AWS Lambda serverless function diagram showing response streaming for real-time pricing Development Concept Image

問題:データ集約(DA)の罠

レガシーシステムはBackend for Frontend(BFF)サービスとしてDAレイヤーを配置し、1時間に1回のCronジョブで全商品カタログの価格を事前計算(precompute)してキャッシュに保存していました。

2つの深刻な失敗

  1. 組み合わせの爆発(Permutation Explosion)

    • 30商品 × (バリエーション × オファー × アドオン) = ページあたり数千レコード
    • 新商品バリエーション追加ごとにキャッシュが指数関数的に増加
    • 事前計算された組み合わせの大半は実際にリクエストされない(無駄)
  2. 同期遅延(Synchronization Lag)

    • Cronジョブが1時間に1回しか実行されないため、フラッシュセールのような価格変動が即座に反映されない
    • 顧客は次の同期時刻まで古い価格を見続ける
    • チェックアウト時に価格が異なり、信頼性が低下

レガシーアーキテクチャ図

[顧客ブラウザ] → CloudFront CDN → [DAレイヤー (キャッシュ)] → [価格エンジン]
                        ↑ 1時間遅延    ↑ Cronワーカー (毎時)

この構造は**「権威(Authority、価格エンジン)」** と 「顧客」 の間に明確な 「非同期化レイヤー(Desynchronization Layer)」 を生み出していました。

CloudFront edge caching architecture with Lambda streaming for ecommerce Technical Structure Concept

解決策:ステートレスストリーミングアーキテクチャ

SamsungチームはAWS TAM(Technical Account Manager)と協力し、中間ストレージ層を完全に排除する新しいアーキテクチャを設計しました。それがBulk Arbitration Engineと呼ばれるステートレスオーケストレーションレイヤーです。

核心:Lambda Response Streaming

Lambda Response Streamingは従来のバッファリング応答方式と異なり、データが準備でき次第即座にクライアントにストリーミングします。これにより30個のSKUを並列に照会し、結果が到着するたびに画面にレンダリングできます。

実装3ステップ

Step 1: ストリーミングハンドラの実装

// Node.js Lambda ハンドラ - awslambda.streamifyResponse() を使用
const { pipeline } = require('stream/promises');
const { Transform } = require('stream');
const { createGzip } = require('zlib');

exports.handler = awslambda.streamifyResponse(async (event, responseStream, context) => {
    // 1. リクエストからSKUリストを抽出
    const skus = parseCompressedQueryString(event.queryStringParameters.g);
    
    // 2. NDJSON変換ストリーム生成 (Newline-Delimited JSON)
    const ndjsonTransform = new Transform({
        objectMode: true,
        transform(chunk, encoding, callback) {
            this.push(JSON.stringify(chunk) + '\n');
            callback();
        }
    });

    // 3. GZIP圧縮ストリーム (Z_BEST_SPEED = Level 1)
    const gzipStream = createGzip({ level: 1 });

    // 4. 全SKUを並列に照会し、結果をストリームで渡す
    const pricingPromises = skus.map(sku => fetchPricingForSKU(sku));
    const results = await Promise.allSettled(pricingPromises);
    
    // 5. パイプライン構成: 結果 → NDJSON変換 → GZIP圧縮 → 応答ストリーム
    await pipeline(
        Readable.from(results.filter(r => r.status === 'fulfilled').map(r => r.value)),
        ndjsonTransform,
        gzipStream,
        responseStream
    );
});

// 並列リクエスト用ヘルパー関数
async function fetchPricingForSKU(sku) {
    const response = await fetch(`https://pricing-engine.internal/${sku}`, {
        // コネクションプーリングを活用
        agent: new https.Agent({ keepAlive: true, maxSockets: 30 })
    });
    return response.json();
}

Step 2: GETリクエストへの圧縮

CloudFrontでキャッシュするにはGETリクエストが必須です。Samsungチームは複雑なリクエストデータを圧縮されたクエリストリング形式に変換しました。

// 圧縮クエリストリング例
// 元JSON (3~4KB) → 圧縮 (約800バイト)
// 形式: g=group1(p=SKU-A:1:p=SKU-B:2)...

Step 3: CloudFrontキャッシュポリシー設定

  • デフォルトTTL: 5分 (鮮度とキャッシュ効率のバランス)
  • クエリストリングをキャッシュキーに含める (異なるSKU組み合わせ = 別キャッシュエントリ)
  • ヘッダー許可リストでカスタム価格バリエーションをサポート
  • GZIP圧縮を自動有効化

Network latency optimization phases from baseline to production with HTTP/2 and VPC peering

パフォーマンス最適化結果:4フェーズの進化

フェーズ説明P90レイテンシ改善倍率
Phase 1 (Baseline)グローバルVPN、バッファリング応答、圧縮なし4,500ms1x
Phase 2 (VPC)VPCピアリング + Provisioned Concurrency1,000ms4.5x
Phase 3 (HTTP/2)HTTP/2多重化 + GZIP圧縮218ms20x
Phase 4 (Production)CloudFrontエッジキャッシュ (95% Cache Hit)50ms90x

重要なインサイト

  • Phase 2 → Phase 3の改善幅が最大です。 HTTP/2多重化で30の並列リクエストのTCP接続を再利用し、GZIP圧縮で応答サイズを76%削減した効果です。
  • Phase 4の95%キャッシュヒットは、わずか5%のリクエストだけがLambdaを呼び出すことを意味します。ブラックフライデーのようなピーク時でもコストとレイテンシを最小限に抑えられました。

本技術の限界と注意点

  1. Lambda実行時間制限: 30SKU制限はLambda実行時間を5秒以内に保つための設計です。50SKU以上が必要な場合はクライアントで複数バッチに分割する必要があります。
  2. 部分障害処理: ストリーミングアーキテクチャは一部のSKU照会が失敗しても残りの結果を継続して送信するため、クライアント側で部分障害を適切にUI表現する必要があります。
  3. GETリクエスト長制限: 圧縮クエリストリングでもURI長制限(通常8KB)を超えないよう注意が必要です。
  4. セキュリティ: SKU情報は公開データですが、価格エンジンのビジネスロジックはVPC内部で安全に保護する必要があります。TLS 1.3、VPCエンドポイント、CloudTrail監査ログを必須で適用してください。

次のステップ学習方向

  • Lambda Response Streaming公式ドキュメントを読み、Node.js以外にPython、Javaでの実装方法を学びましょう。
  • CloudFront Cache Policyの詳細設定(Query String含む、ヘッダー許可リスト)を実際に試してみてください。
  • NDJSON(Newline-Delimited JSON) フォーマットを他のAPIでも応用できます。例えば、リアルタイム検索結果やソーシャルメディアフィードに活用できます。

合わせて読みたい記事

根拠資料: 本記事はAWS Architecture Blogの原文を基に分析・再構成したものです。

本コンテンツは、信頼性の高い情報源をもとにAIツールを活用して作成され、編集者によるレビューを経て公開されています。専門家によるアドバイスの代替となるものではありません。