読み切れる規模で、監査できる正直さ
promovolveはApache 2.0のオープンソースです。セルフホストできます。そしてドキュメントは、意図的に単純にした部分も、試して捨てた案も含めて、実際の動作を説明します。
アクターが、状態の単一の書き手になる
分類、オークション、選択、ペーシング、フロア — すべてサイト単位のステートフルな処理です。その状態をどこに置くか。promovolveの答えはアクターです。現実の対象ひとつにつきアクターひとつ、それぞれが自分の状態の唯一の書き手になります。
Apache Pekkoのクラスターシャーディング上で、各エンティティはIDで宛先指定され、同時にはひとつのノードにだけ存在します。ノードの参加・離脱に応じて、シャードコーディネーターが配置と移動を行います。
SiteEntity— 検証、スロット、分類AdServer— 配信、ペーシング、クリエイティブ統計、フロアAuctioneerEntity— オークション、ページキャッシュ、承認キューCampaignEntity— 入札、クリエイティブ、予算AdvertiserEntity— キャンペーン、承認、日次予算CategoryBidderEntity— カテゴリ × シャードごとの需要レジストリ
あるサイトの配信経路全体 — ペーシング状態、トンプソンサンプリングのウィンドウ、フロアの掃引 — は、メッセージを1件ずつ処理する単一のアクターです。ロックも、キャッシュコヒーレンスのプロトコルも、read-modify-writeの競合もありません。競合する相手が存在しないからです。
代償はサイトあたりのスループット上限 — アクターひとつのメールボックス — ですが、単一アクターは1日数百万インプレッションのサイトを余裕で吸収します。これは事故ではなく、設計上の立場です。
配信時の読み取りは、ネットワークを越えない
候補プールは ServeIndex に置かれます。Pekko Distributed Data(CRDT)上に構築され、全ノードが完全なレプリカを持つので、配信時の参照はローカルのマップ読み取りです。エントリは site|slot のハッシュで32個の名前付きマップに分散されます — レプリケーションはマップ単位で動くため、巨大なマップひとつでは変更のたびに全体が再ゴシップされてしまうからです。
整合性は意図的に非対称です。書き込みはローカルでackして外へゴシップします(WriteLocal)— オークション結果が他ノードで数十ミリ秒遅れて見えるのは無害です。例外はキー全体の削除で、こちらはリトライ付きの多数派書き込みを使います。ゴシップ競合に負けた削除は、消したはずの候補を復活させてしまうからです。高速な経路は結果整合、破壊的な経路は確実性の代金を払う。
レプリカは永続化されます(DDataが指定キーをローカルのLMDBに保存)。再起動したノードは世界を再ゴシップせずディスクからキャッシュを復元し、起動時の分類リプレイと合わせて、クラスター全体の再起動は約1分で自己修復します。
オークションを、ホットパスから外す
RTBはインプレッションごとに約100msの締め切りつきオークションを走らせます。その結果、参加者全員がすべてを事前計算し、結局はキャッシュで応答することになります。
promovolveはオークションをホットパスから完全に外しました。分類時と変更イベント時に実行し、配信はローカルキャッシュの読み取りとベータ分布からのドローだけです。代償は候補プールが数分古くなりうること。1秒のデバウンスつきイベント駆動の再オークションが、重要な箇所で陳腐化の上限を抑えます。
構成
- コア — Scala / Apache Pekko(クラスターシャーディング、Distributed Data)
- ダッシュボード — Go、サーバーサイドレンダリングのテンプレート(SPAフレームワークなし)、パスキー認証のみ
- フロントエンド — TypeScript
- データストア — PostgreSQL / TimescaleDB
- インフラ — Kubernetes、Docker
- 外部依存 — Cloudflare R2、LLM APIキー(Gemini / OpenAI / Anthropic)
ノードは役割を持ちます — api(HTTP)、entity(シャード化アクター)、singleton(クラスター全体のディレクトリ)、crawler(ランディングページ解析ワーカー)。ブロッキング処理(Playwright、LLM呼び出し、JDBC)は専用ディスパッチャで動くため、負荷のかかったエンティティが配信経路を枯渇させることはありません。
開発状況
promovolveは1.0以前の活発な開発段階にあります。インターフェースやデプロイ設定に破壊的変更が入る可能性があります。ローカル開発にはJDK、sbt、Go、Node.js、Docker、Cloudflare R2、そしてLLM APIキーが必要です。