tva
← コラム

FastAPIとTimescaleDBを使ったAmazonデータウェアハウスの構築

AmazonのSP-APIからは、注文、在庫、精算、カタログの実績など、大量の業務データにアクセスでき、広告データは別のAPIであるAmazon Ads APIから取得できます。ただし、実際に役立つのはクエリで取得できる形のデータです。Amazonのレポートには保存期間とレート制限があり、複数マーケットプレイスで事業を運営する出品者にとって重要な問いに答えるようには設計されていません。何が起きたかは確認できます。しかし、すべてのマーケットプレイスで現在何が起きているか、あるフルフィルメント地域の特定ASINについて過去90日間の需要がどう推移したかを、簡単に調べることはできません。

本記事では、当社がAmazonデータウェアハウスに採用しているアーキテクチャを説明します。API層にはFastAPI、時系列データの保存にはTimescaleDB、非同期のSP-APIレポートパイプラインにはCeleryを使用します。目指すのは、当社がAmazonの各APIから取得するデータを、自社で管理しクエリを実行できる形で保存するシステムです。需要予測や業務分析に必要な構造を備えながら、保守にデータエンジニアリングの専門チームを必要としない構成にします。

Amazonの標準ツールでは不十分な理由

Seller Centralのレポートは、小規模なカタログを一つのマーケットプレイスで扱う場合、業務上の要件の大半を満たします。規模が大きくなると不足が明らかになります。レポートは必要に応じて生成され、有効期限があります。永続的にクエリを実行できる層はありません。レポートを実行してファイルをダウンロードすると、そのファイルがデータの実体となります。6か月後に同じレポートを実行しても、得られるのはその時点のデータであり、今日のデータではありません。

そのため、過去の分析には、必要なレポートを適切な間隔で誰かがダウンロードして保存しているか、取引単位のエクスポートから履歴を再構成する必要があります。ただし、そのエクスポートにもレート制限があり、ページ分割して取得できる期間が限られ、一括分析より個別レコードの取得を想定した構造になっています。

AmazonのBusiness Reportsダッシュボードは集計済みの傾向データを提供しますが、その粒度と保持期間は分析上の要件ではなく、Amazonの製品仕様によって決まります。広告コンソールには独自のデータモデルがあり、フルフィルメントデータと容易には結合できません。精算データは2週間周期で届き、注文データと照合するには複雑な解析が必要です。

独自のデータウェアハウスは、永続保存とクエリ実行の課題を同時に解決します。保存先を自社で管理し、保持期間を定め、Amazonのダッシュボードが想定した問いではなく、自社の業務で本当に答える必要のある問いに対応したクエリを記述できます。

技術の選択

主な保存先にTimescaleDBを選んだのは、Amazonの業務データの多くが本質的に時系列データだからです。注文にはタイムスタンプがあり、在庫スナップショットは特定時点の状態を示し、精算期間には開始日と終了日があります。広告費やインプレッション数も時間とともに蓄積します。TimescaleDBはハイパーテーブル拡張を備えたPostgreSQLで、SQLとの完全な互換性を保ちながら、大量データに対する時間範囲クエリを標準のPostgreSQLより大幅に効率化します。新たなクエリ言語を学ぶ必要はなく、運用方法も標準的なPostgres環境と大きく変わりません。

FastAPIは、SP-APIからデータを取得するCeleryワーカーと、データウェアハウスを照会する社内の利用システムをつなぐAPI層です。最小限の定型コードで、認証、リクエストの検証、レスポンスのシリアライズを処理します。インターネットに公開しない社内サービスでは、FastAPIが自動生成するOpenAPIドキュメントが特に役立ちます。操作して確認できるインターフェース定義が得られるため、データベーススキーマからデータモデルを逆算しなくても、新しい利用システムを容易に実装できます。

Celeryは、非同期のSP-APIレポートパイプラインを管理します。SP-APIのレポート生成は非同期処理を前提としており、レポートを要求し、状態をポーリングし、準備できた結果をダウンロードしてファイルを処理します。この流れは、要求を開始するタスク、状態を確認する定期タスク、生ファイルをダウンロードして保存するタスク、構造化データを解析してTimescaleDBに読み込むタスクというCeleryのタスクチェーンに自然に対応します。RedisはCeleryのブローカーと結果バックエンドとして機能します。

データモデル

主要テーブルは、SP-APIの主なデータ領域に対応しています。設計原則は、複数のレポート形式をまたぐ結合を必要とせず、各テーブルが特定の種類の業務上の問いに答えられることです。

注文は、purchase_dateでパーティション分割したハイパーテーブルです。最も一般的な分析粒度が、ASIN別、フルフィルメントチャネル別、マーケットプレイス別の商品単位であるため、各行は注文全体ではなく、一つの注文明細を表します。注文単位の集計は明細レコードから算出します。ハイパーテーブルの分割間隔は1週間に設定し、需要予測で使う移動期間のクエリで良好な性能を得ています。

在庫スナップショットには、取得時点の在庫レポートの出力を保存します。FBA在庫はイベントストリームではなく、継続的に変わる状態です。返品、調整、移動は対応する注文なしに在庫を変えるため、注文レコードだけでは完全に再構成できません。当社は在庫スナップショットを1日2回取得し、特定時点のレコードとして保存します。TimescaleDBの継続集計機能は、全履歴をクエリで走査せずに、これらのスナップショットから日次・週次の移動平均を計算します。

精算には、解析済みの精算レポートを明細ごとに1行として保存します。精算データモデルは、SP-API関連の処理でも特に解析が複雑なものの一つです。Amazonはマーケットプレイスごとに異なる取引種別コードを使用し、手数料明細の命名規則も地域間で完全には一致しません。当社では、各市場の手数料種別名を統一した社内分類に正規化するマッピングテーブルを維持しています。

カタログには、ASIN、タイトル、カテゴリー、バリエーション間の関係などの商品カタログのスナップショットを保存し、週次で更新します。主に他のテーブルの参照情報として使い、データに意味のある時系列特性がないため、ハイパーテーブル構造は使用しません。

TimescaleDBの実際の使い方

ハイパーテーブルはTimescaleDBの中核機能です。標準のPostgreSQLテーブルから作成するには、時間列と分割間隔を指定する一つのコマンドを実行します。その後は標準SQLのクエリを変更せずに使用できます。TimescaleDBのクエリプランナーは、対象の時間範囲外のパーティションを読み飛ばすチャンク除外を自動適用し、大量のデータでも時間範囲を限定したクエリを高速化します。

当社が広く活用するもう一つの機能は継続集計です。継続集計は、ハイパーテーブルに新しいデータが入るとTimescaleDBが差分更新するマテリアライズドビューです。需要予測では、ASIN別・マーケットプレイス別の売上を日次の継続集計として維持し、そこから直近7日、30日、90日の売上合計を計算します。この集計は定義したスケジュールで自動更新されます。クエリはハイパーテーブル全体ではなく事前計算済みデータを参照するため、ダッシュボードでの分析も対話的に使える速さになります。

CREATE MATERIALIZED VIEW daily_sales_by_asin
WITH (timescaledb.continuous) AS
SELECT
  time_bucket('1 day', purchase_date) AS day,
  asin,
  marketplace_id,
  SUM(quantity_ordered) AS units_sold,
  SUM(item_price_amount) AS revenue
FROM order_items
GROUP BY day, asin, marketplace_id;

このビューは、1時間ごとに実行するよう設定したTimescaleDBのバックグラウンドポリシーで自動更新します。予測に使う直近90日間の合計は、生の注文明細テーブルではなく、このマテリアライズドビューに対するウィンドウ関数のクエリで計算します。

Celeryパイプライン

SP-APIレポートパイプラインは、Celery Beatが管理するスケジュールに従うCeleryタスク群として動作します。レポートごとにスケジュールを設け、注文は1時間ごと、在庫は1日2回、精算はAmazonの精算期間に合わせた2週間周期で取得します。

各レポートのパイプラインは同じ構造です。定期タスクがSP-API経由でレポートを要求し、リクエストIDをRedisに保存します。ポーリングタスクは数分ごとにSP-APIで状態を確認し、準備ができるとダウンロードタスクをキューに追加します。ダウンロードタスクはレポート文書を取得し、生ファイルをディスクに保存して、処理タスクをキューに追加します。処理タスクがファイルを解析し、構造化レコードをTimescaleDBに読み込みます。

重複排除はデータベースではなく、処理段階で行います。SP-APIは、期間が重なるレポートで同じレコードを返すことがあります。各レコードを識別するフィールドのハッシュを保持し、挿入前に既存レコードを確認することで、各レポート種別を順次処理する限り、重複行がデータウェアハウスに蓄積するのを防ぎます。並行処理する場合は、データベースにも一意制約が必要です。

エラー処理の方針はシンプルです。レート制限の応答や一時的なサービス停止など、SP-APIの一過性のエラーには、指数バックオフを伴うCeleryの再試行で対応します。形式が不正なレポート文書、予期しないスキーマ変更、解析失敗などの継続的なエラーは、詳細な状況とともに記録し、他のパイプラインに影響を与えず、そのレポート種別の処理を停止します。精算照合のデータに影響する場合を除き、緊急インシデントとして扱わず、週次の運用レビューでエラーログを確認します。

需要予測の基盤

データウェアハウス自体は、需要予測を直接実装しません。予測を可能にするデータ基盤を提供します。ASIN・マーケットプレイス・日単位に整理した販売の時系列データと、季節性の把握や統計的に意味のある傾向推定に十分な履歴です。

前述の日次の継続集計から、直近7日、30日、90日の需要指標を生成します。これをシンプルな予測モデルに入力します。安定した商品には加重移動平均と線形トレンドの外挿を使い、季節性が分かっている商品には手動の調整係数を加えます。モデルはFastAPIのエンドポイントとして動作し、継続集計を照会して、信頼区間を伴う点予測を返します。

データ基盤が整えば、より高度な予測手法も扱いやすくなります。当社では、明確な季節周期を持つ商品にHolt-Winters指数平滑法を試し、広告費や価格変更などの外部要因が需要に大きく影響する商品には、これらの要因を用いた単純な回帰分析も試しました。しかし、実際には、十分に調整した加重移動平均と比べた複雑な手法による精度向上は、多くの商品でわずかです。モデルの複雑さより、データの品質と履歴の長さのほうが重要です。

実現できること

このデータウェアハウスで実現できる機能を具体的に挙げます。レポート作成が少し速くなるだけではなく、業務でできることが質的に変わるためです。

複数マーケットプレイスをまたぐ在庫計画が扱いやすくなります。ASIN・マーケットプレイス単位の販売速度と、スナップショットテーブルの最新FBA在庫を使えば、すべてのマーケットプレイスの各SKUについて在庫日数を計算できます。市場ごとに手動で確認する代わりに、再発注の推奨を自動生成できます。

精算照合を監査できるようになります。注文と精算のデータを同じシステムに保存すれば、すべての注文明細が想定した料率で精算されたかを検証できます。手動で明細を照合する出品者にしか見えにくかった手数料の不一致、不正確な補填、精算の誤りを特定できます。この確認は手作業ではなく、データウェアハウスに対する週次レポートとして実行します。

ASIN単位で広告分析ができるようになります。共通の識別子で広告データと注文データを結合すれば、広告費と自然検索経由の売上が時間とともにどう連動するかを追跡できます。Amazonの広告コンソールは広告に帰属する売上を示しますが、その前後の自然検索経由の売上は示さないため、この問いには直接答えられません。結合したデータが示すのは相関です。自然検索順位の変動を広告に帰属させるには、順位データと統制された比較も必要です。


関連記事

関連記事