> [!abstract] 概要(HOTOS 25 abstract の日本語訳)
> 本論文は、publish-subscribe(pubsub)システムがメッセージング抽象化とハード状態のストレージ層の両方を一体化(bundle)しており、これが様々なユースケースにおいて pubsub の堅牢性・性能・正しさを損なっていると論じる。pubsub はプロデューサとコンシューマを分離するという中心的な目標の達成に失敗し、エンドツーエンド原則に違反し、限定的な力しか持たないビスポーク(bespoke)な API によるアドホックなストレージ抽象化を露呈させる。
> 我々は、pubsub を (1) 耐久性のあるストア(durable store)と、(2) ストアへの変更をコンシューマへ通知する watch システムとにアンバンドリングすることで、これらの問題を修正する方法を説明する。このアプローチは、コンシューマに対して pubsub システムそのものではなくストアに対する保証を提供することで、エンドツーエンド論を尊重する。我々は、このモデルが様々なユースケースについて pubsub システムがもたらす課題に対処し、新たな研究領域を切り拓くことを示す。
## 論文情報
- タイトル: Understanding the limitations of pubsub systems
- 著者: Atul Adya(
[email protected])、Phil Bogle(
[email protected])、Colin Meek(
[email protected])。3 名とも [[Databricks]] 所属。
- 媒体: Workshop in Hot Topics in Operating Systems(HOTOS 25)、2025 年 5 月 14–16 日、Banff, AB, Canada。ACM, New York, NY, USA, 7 pages(pp.165–171)。
- DOI: https://doi.org/10.1145/3713082.3730397、ACM ISBN 979-8-4007-1475-7/25/05。Creative Commons Attribution International 4.0 でライセンスされている。
- コード URL: 記載なし(本論文自体はビジョン・ポジションペーパーであり、実装は proprietary な Snappy システムとして進行中と述べるのみ)。
## 概要
本論文は HOTOS(Hot Topics in Operating Systems)というワークショップの性質どおり、実験による定量評価を伴わないポジションペーパーである。pubsub システム(Kafka・Google Pub/Sub・Azure Service Bus・Pulsar 等)が「メッセージング」と「ストレージ」という 2 つの異なる関心事を 1 つの抽象化に押し込めていることを構造的な欠陥として指摘し、この bundle を解いて「明示的なストレージ」+「watch API」という 2 層構成へ再設計することを提案する。著者らは Databricks で MySQL/TiDB 向けの外部 watch サブシステム Snappy を実装中であると述べており、実運用の知見に基づく議論である。
## 問題設定
pubsub モデル(Figure 1)では、プロデューサがトピックへメッセージを発行し、コンシューマがトピックを購読する。pubsub システムは、コンシューマがメッセージの出所を意識せずに済むよう、発行されたメッセージを購読コンシューマへ配送する責務を負う。データセンター向け pubsub システムは、数万のプロデューサ・コンシューマと、トピックあたり毎秒数百万件のメッセージを扱える。
**Figure 1: Pubsub model**
![[_attachments/Understanding-the-limitations-of-pubsub-systems/fig01-pubsub-model.png]]
(Figure 1. プロデューサがトピック内の Log へ publish し、コンシューマがそのトピックを subscribe する。Pubsub System は複数の data source から複数の Producer 経由で受け取ったメッセージを、トピックごとの Log に蓄積したうえで Consumer へ配送する。)
pubsub は 2 種類のコンシューマモデルをサポートする。**consumer group** はメンバー間でメッセージを分配し、各メッセージがグループ内のいずれか 1 コンシューマへルーティングされ、確認応答(acknowledge)されることを保証する(メッセージのパーティションやキーによってランダム/決定的に振り分けられる)。**free consumer**(用語は [26] に依拠)はトピックまたはトピックパーティション内の全メッセージを処理する。いずれのアプローチも、動的にシャーディングされたコンシューマがキー空間の任意の部分範囲を購読することを許さない。
pubsub の代表的なユースケース(2 節、[33][34] のサーベイに基づく)は次の 4 つである。
- **イベント取り込みとファンアウト(event ingestion and fanout)**: ログ・センサーデータ・ウェブサイト活動などのイベントを受け取り、関心を持つ全システム・デバイスへファンアウトする。イベントのストレージを ingestion storage と呼ぶ。
- **ストア間レプリケーション(replication across stores)**: CDC システムがソースストアの変更イベントを pubsub 経由でターゲットストアへ配送する。レプリケーションのソースストアを producer storage と呼ぶ。
- **キャッシュ無効化/鮮度維持(cache invalidation/freshness)**: producer storage がオブジェクト ID や更新済みペイロードの更新を発行し、分散キャッシュノードへ伝播する。
- **ワークキューイングと負荷分散(work queueing and balancing)**: タスクを表すメッセージをキューイングし、consumer group のワーカーへ配分・処理・確認応答させる。
## 提案手法
### アーキテクチャ: ストレージと watch の分離
著者らは pubsub のアンバンドリングを 2 軸のマトリクスとして図式化する(Figure 3)。X 軸はストレージシステム(producer storage か ingestion storage か)、Y 軸は通知メカニズム(ストレージシステム自体が組み込みで実装するか、その上に外部レイヤーとして実装するか)である。
**Figure 3: Separation of storage and notifications**
![[_attachments/Understanding-the-limitations-of-pubsub-systems/fig03-storage-notification-matrix.png]]
(Figure 3. 縦軸が built-in watch / external watch system、横軸が producer storage / ingestion storage の 2×2 マトリクス。Spanner の Change Data Streams は producer storage × built-in watch、著者らが実装中の Snappy(MySQL/TiDB 上)は producer storage × external watch system、time-series database/data stream management system は ingestion storage × built-in watch に位置づけられる。ingestion storage × external watch system が「Storage with watch system」の一般形にあたる。)
具体例として、Spanner は producer storage としても機能し、組み込みの watch 機構である Change Data Streams を持つ(Kubernetes API server も watchable な etcd ストアに支えられている点で同様)。MySQL・TiDB は key-value ストアとして扱いつつ、その上に外部 watch サブシステム Snappy を構築中である(Snappy は本論文執筆時点で未公開)。time-series database・data stream management system・その他の構造化ストアは ingestion store として使え、時系列データへの効率的なアクセスを提供する。改良版の Kafka のような pubsub システムは、API に変更を加えて暗黙のストレージ層をより明示的にすれば、右下象限(built-in watch を持つ ingestion storage)に収まると著者らは述べる。
保証(guarantees)の性質も pubsub と根本的に異なる。提案アプローチでは、コンシューマは watch 対象のストレージ(永続的か一時的かを問わず producer storage または ingestion storage)に対する保証を受け取る。これに対し、従来の pubsub は問題のある中間ストレージ層を介在させる。
### Watch API の詳細
Watch API(Figure 4)は、キーとトランザクションバージョン(例: 「account A は version 40 時点で残高 $20」)で組織化された変更イベントを配送する。簡略化のための前提として、真実の源(source of truth)は単調増加するトランザクションバージョン(Spanner の TrueTime タイムスタンプ、TiDB の TSO タイムスタンプ、MySQL の gtid 等)を持つとする。
**Figure 4: Unbundled architecture: Storage with watch**
![[_attachments/Understanding-the-limitations-of-pubsub-systems/fig04-unbundled-architecture-watch.png]]
(Figure 4. Cache(コンシューマ)が Watch system に対し watch() を呼び、onEvent()/onProgress()/onResync() のコールバックで通知を受ける。Watch system は Store に対し append()/progress() で変更を取り込み(4.2.2 の Ingester インタフェース)、必要に応じ read() でストアから直接読み出す。)
- **Consumer API(4.2.1)**: コンシューマ(watcher と呼ぶ)は、`Watchable.watch(Key low, Key high, Version version, WatchCallback callback)` を通じて、あるトランザクションバージョン以降のキー範囲の状態を要求する。`WatchCallback` は 3 種のコールバックを持つ。
- `onEvent(ChangeEvent event)`: `ChangeEvent{Key key; Mutation mutation; Version version;}` — 要求バージョン以降にストア内で発生した変更。
- `onProgress(ProgressEvent event)`: `ProgressEvent{Key low; Key high; Version version;}` — 監視対象キーの一部または全部に影響する変更イベントが、あるバージョンまで供給し尽くされたことを示す。
- `onResync()`: watcher が知っているバージョンがもはや保持されていないことを示す。この通知を受けた watcher はストアから最新のスナップショットを読み、そのスナップショットバージョンから watch リクエストを再発行してキャッチアップする。stale なスナップショットを読むことは許容されるため、レプリカから読むことで基盤ストレージへの負荷を減らす最適化も可能である。
- アプリケーションは watch コールバックインタフェースを直接実装するか、[2] と同様のプロトコルを話す linked cache を利用できる。
- **Ingester API(4.2.2)**: ストアが watch 契約を直接実装してもよいが、range-scoped な progress イベントにより、パーティション化されたログの進捗を独立した watch システムへ伝えることもできる(`Ingester{void append(ChangeEvent event); void progress(ProgressEvent event);}`)。ストアが特定バージョン以下の更新をあるキー範囲に対しすべて適用し終えたことを確認すると、watch システムへ progress イベントを送る。progress イベントはグローバルや静的パーティションに紐づくのではなくキー範囲にスコープされ、各レイヤーが独立に進化可能なパーティション境界を定義できる。watch システムはメモリを超えるデータ構造を保持するために独自のストレージシステムを使ってよいが、pubsub と異なり追加のハード状態を導入するわけではない(削除されても回復可能なソフト状態であり、失われるのはレイテンシや鮮度の劣化のみで、データ・一貫性の損失は生じない)。
### キャッシュ・レプリケーションへの適用と知識領域(knowledge region)
watch モデルは、控えめな能力しか持たないキャッシュやストレージレプリカでも、動的にシャーディングされていてもスナップショット一貫性のあるクエリを提供できるようにする。progress イベントを使うことで、完全な知識を持つキー範囲・バージョン窓を追跡できる。
**Figure 5: Knowledge by key and version for a watcher**
![[_attachments/Understanding-the-limitations-of-pubsub-systems/fig05-watcher-knowledge-regions.png]]
(Figure 5. 縦軸が Versions(v0〜v10 超)、横軸が Keys。各青色矩形は watcher が知っている key range × version window の「知識領域(knowledge region)」を表す。Progress threshold(上限)と Compaction threshold(下限)の間に緑色で示された「Complete snapshot」帯が、全キー範囲について完全な知識が揃うバージョン窓である。知識領域は不変(immutable)——あるバージョンで一度書かれた値は変化しない——であり、これがデータの動的なレプリケーション・再パーティショニングを一貫性を損なわずに可能にする。)
図中の各知識領域は、その範囲内でスナップショット一貫性のあるクエリに応答できることを示す単一 watcher の知識を表すが、複数 watcher の知識領域を組み合わせて、より広いスケールでスナップショット一貫性のあるクエリを提供することも構想できる。
### ユースケースごとの適用(4.3)
- **キャッシュとレプリケーション**: progress イベントによりキャッシュ・レプリカが動的シャーディング下でもスナップショット一貫性のあるクエリを提供できる。
- **イベント取り込みとファンアウト**: producer は ingestion store(時系列データベース等)を公開し、consumer はキー範囲の全部または一部を watch して新規イベントを検知する。必要ならストアに直接クエリもできる。これにより pubsub モデルが引き起こすバックログ・効率性の問題に対処する。
- **ワークキューイングと負荷分散**: auto-sharding システム([3][27])を用いて、affinitized かつ動的にシャーディングされたワーカーへキー範囲を割り当て・複製する。各ワーカーは最初にデータベースへ割り当てエンティティを問い合わせ、その後 watch で他のエンティティを識別する。イベントの並びを追跡するのではなく現在状態を観測することで、head-of-line blocking を根本的に回避できる。オンラインデータ処理ワークロード向けの仮想マシン provisioning コーディネータの例が挙げられている: pubsub モデルではワークロード追加時にワークフローの各ステップ(VM 確保・イメージのブートストラップ・ネットワーク設定・処理開始)を都度エンキューするが、実際にはコーディネータは利用可能な計算資源と設定済みワークロードの集合を常時 reconcile し続ける必要がある。望ましい構成(desired configuration)と実際の構成(actual configuration)の両方を watch することで、コーディネータは正しく実際の状態を望ましい構成へ前進させられる。
## 新規性
本論文の新規性は、Kubernetes・Spanner が個別に普及させてきた watch 抽象化([21])を、pubsub システムが抱える構造的問題への一般解として体系的に位置づけ直した点にある。既存研究との差分:
- **従来の pubsub 研究([19][20][32][38] 等のサーベイ)** は pubsub をメッセージングパラダイムとして分類・評価するにとどまり、「なぜ pubsub がストレージ層を暗黙に持ってしまうのか」という構造的欠陥そのものを問題視していない。
- **Kubernetes API server・Spanner の Change Data Streams** はそれぞれ個別プロダクトの watch 実装だが、本論文はこれらを「ストレージ + watch」という一般アーキテクチャパターンとして抽出し、任意の producer storage / ingestion storage に適用可能な Consumer API・Ingester API として仕様化している。
- pubsub の暗黙のメッセージログストアと比較して、明示的に露出したストアは**少なくとも同等以上に強力**であり(SQL/NoSQL の標準 API・インデックス・スキャンが使える)、watch 機構は従来の subscribe 機構よりも**厳密に強力**である(progress・resync という pubsub には無い信号を持つ)と主張する。
## 実験設定
本論文は HOTOS のポジションペーパーであり、定量評価を伴うベンチマークやシステム実装の性能測定は行っていない。根拠として提示されるのは、著者ら(Databricks)における実運用の事例と、Snappy という進行中の実装(MySQL/TiDB 上に構築する外部 watch サブシステム、未公開)である。
## 実験結果
定量的な実験結果はないが、実運用上の具体的な障害事例が根拠として述べられている。
- **キャッシュ無効化の巨大バックログ事例**: あるキャッシュ無効化ユースケースで、実際のコンシューマがデータセンターのメンテナンスにより数日間利用不能になった。これにより緊急でリソースを追加した後でも巨大なバックログが生じ、キャッシュ無効化に数秒ではなく数時間かかるようになり、ユーザーにとって実質的に無意味になった。
- **キャッシュ無効化のレース条件**(Figure 2、3.2.2 節): オブジェクト x の無効化と、auto-sharder による x の pod p_old から p_new への再割り当てとの間のレースが図示される。
**Figure 2: Invalidation eventual consistency failure in the presence of auto-sharding**
![[_attachments/Understanding-the-limitations-of-pubsub-systems/fig02-invalidation-race.png]]
(Figure 2. ① p_new が x の再割り当てを先に知り、② p_new が Store から x0 を fetch する。一方 Store 側では③ x0 → x1 へ Update され、④ Pubsub system へ update が通知され、⑤ Pubsub system が Invalidate x を送るが、⑥ この Ack を返すのは(pubsub system がまだ再割り当てを知らないため)⑦ で再割り当てされる p_old であり、p_new ではない。結果として p_new は更新後の値 x1 を永遠に受け取れない。⑧ で最終的に pubsub system にも再割り当てが伝わるが手遅れである。)
- pubsub システムは一般に、最も遅いコンシューマでも数日程度の保持期間(retention period)で十分だと仮定するが、一部のコンシューマが処理し終えていなくてもメッセージは最終的にガベージコレクションされ、信頼できる配送に依存するアプリケーションの正しさを犠牲にする。
- 一部の pubsub システム([24] = Kafka)はトピックコンパクションをサポートするが、通知なしにコンパクションが起きるため、subscriber は未見のイベントがコンパクションされたことに気づけない。コンパクションはメッセージ損失を「先送りする」だけで解消しない。
- システムオペレータは、バックログ削除・TTL によるキャッシュ無効化フォールバック・バックアップからのレプリケーション復元といった、アドホックで手作業の復旧手順に依存しており、これは toil を要するだけでなく正しさ・可用性・レイテンシを犠牲にする。
## 考察
著者らは pubsub の限界を大きく 3 系統に整理する(3 節)。
1. **疎結合の失敗(3.1)**: pubsub は「decoupling」を謳うが、実際にはコンシューマが過大なバックログを蓄積してもそれを通知せず、遅延コンシューマが source of truth から失われた状態を回復する仕組みも提供しない。メッセージはほぼ発行順に配送されるため、過大なバックログはサイレントな停止と見分けがつかない。affinitized なコンシューマ(効率的なキャッシングに必要)についても、既存の pubsub のコンシューマアフィニティ機構(メッセージキーやパーティションに基づく)は、疎結合アプリケーションコンシューマの独立かつ動的なシャーディングをサポートしない。
2. **エンドツーエンド論への違反(3.2)**: pubsub はその層で順序・少なくとも一度配送・トランザクションといった保証を提供するが、これらは authoritative なデータソースに対する意味のある end-to-end 保証にはならない。ストア間レプリケーション(3.2.1)では、producer store がイベント順序とトランザクション境界の権威であるにもかかわらず、pubsub 層が競合する順序やトランザクションを持ち込むと複雑さとコストが増すだけで、target store/cache からソースの一貫したビューを得る役には立たない。並行に変更イベントを publish/apply するとスケーラブルにはなるが、insert/update/delete の順序が乱れると stale な状態で上書きしたり、削除済み行を復活させたりして結果整合性(eventual consistency)に違反しうる。バージョンチェックとトゥームストーンでレプリケーションエラーの一部は解消できるが、それでもスナップショット一貫性違反のリスクは残る(グループからメンバーを削除した後にそのグループへドキュメントへのアクセス権を与える例が、順序が逆転すると target store 上で一度も存在しなかった状態——削除済みメンバーがドキュメントへアクセス可能な状態——を一時的に記録してしまう、という具体例が挙げられている)。トピックを行ごとに単一パーティションへ静的に割り当てシリアルに処理する戦略でもバージョンチェック・トゥームストーンは不要になるが、複数パーティションにまたがるトランザクションはアトミックに適用されずグローバルなトランザクション順序が破られうるため、スナップショット異常は依然として起こりうる。
3. **アドホックなストレージ API(3.3)**: pubsub システムはスキーマ・バージョン・トランザクションなどの機能を段階的に追加してきたが、各システムが独自のビスポーク API とスペシャルな拡張(GCP の「replay and snapshot」、Azure の「dead-letter queues」)を発展させており、標準化されていない。著者らは、時系列データベースや新しいロギング抽象化([7][28])がログ様のストレージに、Bigtable のような NoSQL や Spanner・CockroachDB・TiDB のような SQL システムが構造化ストレージにより適していると主張する。
4.4 節では unbundled アーキテクチャの利点を、pubsub との対比で 6 点に整理している: (1) より良いバックログの扱い(watch は正しさのために無制限のバックログを必要とせず、resync 信号で遅延コンシューマを回復させる)、(2) ストレージと watch のアンバンドリング(アプリケーションは用途に最適なストアを自由に選べる)、(3) エンドツーエンド正しさ(キー範囲 watch により分割コンシューマは必要なイベントのみ受け取れ、キー範囲 progress イベントにより動的シャーディング下でも一貫したスナップショットを提供できる)、(4) 効率性(追加のハード状態メッセージログが不要)、(5) 標準的で強力なストレージ・通知 API(アドホックな pubsub API に依存しない)。
## 強み / 弱点・課題
**強み**:
- pubsub の「decoupling」という謳い文句がなぜ現実には破綻するかを、エンドツーエンド論(→ [[エンドツーエンド論]])という確立した設計原則に接続して構造的に説明している。ただし本論文自体は Saltzer らの原論文を参考文献として明示的には挙げていない(end-to-end argument への言及は本文中のみ)。
- Kubernetes/Spanner の watch という既存の局所解を一般アーキテクチャパターンへ格上げし、Consumer API・Ingester API という具体的なインタフェース仕様まで踏み込んでいる点は、単なる批判ではなく建設的な代替案になっている。
- 3.2.1〜3.2.2 節の失敗事例(順序反転によるスナップショット一貫性違反、auto-sharder との競合)は具体的で、pubsub ベースのレプリケーション設計者にとって実運用上の教訓として有用。
**弱点・限界**(著者らが 5 節「Areas of future research」で自ら認めている未解決課題を含む):
- 定量評価が一切ない。バックログ削減や end-to-end レイテンシの改善が実際にどの程度になるかは示されていない。
- Snappy(MySQL/TiDB 向け外部 watch サブシステム)は本論文執筆時点で未公開であり、提案アーキテクチャの実運用での検証がまだない。
- スタンドアロンの watch システムを多様なストレージシステム・スケール要件へ一般化するには、さらなる研究が必要と著者ら自身が認めている。
- 動的な再パーティショニングに追従する auto-sharded キャッシュがスナップショット一貫性を保つには、複数キャッシュサーバにまたがる知識領域の効率的な結合(stitching)のためのプロトコル・データ構造設計が今後の課題として残る。
- 異種ストア間レプリケーションでの watch ベースアプローチは「有望な方向性」と述べるにとどまり、実用化にはまだ多くの作業が必要と明言している。
## 5 章: 今後の研究領域(著者ら自身の指摘)
- **スタンドアロン watch システム**: Ingester・Watchable 契約を実装する、ネイティブに watch をサポートしないストレージシステムへ watch 機能を追加できるスケーラブルな独立システム。著者らは特定の分散ストレージシステム向けに Snappy を構築中だが、多様なストレージシステム・スケール要件への一般化にはさらなる研究が要る。
- **スナップショット一貫性をサポートする auto-sharded キャッシュ**: watch 契約は auto-sharder([3][27])による動的な再パーティショニングの下でもスナップショット一貫性のあるビューを露出できるようにするが、複数キャッシュサーバにまたがる知識領域を効率的に結合するプロトコル・データ構造の設計は今後の課題。
- **異種ストア間のレプリケーション**: pubsub は異種ストア間のレプリケーションによく使われるが、スケーラビリティが乏しい、意味論が弱い、あるいはその両方に苦しむことが多い。Spanner のような同種データベース展開の read-only replica では内部プロトコルでスナップショット意味論を達成済み([14])だが、watch ベースのアプローチは多様で現実世界の異種ストレージシステム間のレプリケーションに、強い意味論と低レイテンシを保ったまま拡張する有望な方向性を示す。ただし実用化にはまだ多くの作業が残る。