1 of 36

自作MQTTライブラリを�Boostに提案してみた

近藤 貴俊 redboltz

2 of 36

発表者について

  • 近藤貴俊 redboltz
  • C++
    • Boost Libraries コントリビュータ
    • MQTT (IoT分野でよく使われる軽量プロトコル)�ライブラリ作者�async_mqtt, mqtt_cpp�
    • 軽量シリアライズフォーマット MessagePack の CおよびC++ 版�msgpack-c メンテナ
  • Rust
    • MQTT関連crate作者
      • mqtt-protocol-core, mqtt-endpoint-tokio, mqtt-broker-tokio, mqtt-client-wasm�

2/36

3 of 36

発表の目的

  • Boostに自作ライブラリを提案するという、あまりない経験をした�のでそこで得たことや気付きを共有したい。

  • MQTTについて
    • IoT分野で利用されるPub/Subモデルの通信プロトコル。
    • brokerと呼ばれるサーバソフトウェアがクライアント間の�メッセージを中継する。
    • 規格はOASIS(v5.0, v3.1.1)やISO(v3.1.1)に登録されている。
    • 本日は、プロトコル詳細を技術的に深掘りはしません

3/36

4 of 36

免責事項

  • 本日触れる人物間のやり取りの内容は、客観的視点となるように�努めていますが、あくまでも私が理解した内容を日本語で表現したものです。�ニュアンスや意図の違いなどが含まれる可能性があります。

4/36

5 of 36

主要登場人物

5/36

joaquintides MultiIndex作者

Klemens Morgenstern Process作者の一人

Ivica Siladić async-mqtt5作者

redboltz async_mqtt作者(私)

Vinnie Falco Beast作者 精力的に活動

Robert Ramey Serialization作者 割とBoost初期メン

6 of 36

相関図

6/36

joaquintides

redboltz

Robert Ramey

Vinnie Falco

Ivica Siladić

Klemens Morgenstern

Endorse

Slack上でレビュー

Slack上およびFormal レビュー

並行レビュー提案

競合ライブラリ

7 of 36

redditにasync-mqtt5をBoostに

  • Ivica Siladić氏から、自社製のMQTT clientをBoostライブラリに�提案したいとの表明あり
  • https://github.com/mireo/async-mqtt5
  • 特徴
    • Asioスタイルの完全サポート
    • エンベデッド活用を想定した省リソース設計
    • MQTT v5.0を完全サポート
    • C++20ベース(後にC++17ベースに変更される)

7/36

2023/09

8 of 36

joaquintides氏からメッセージが届く

8/36

joaquintides

数年間あなたのプロジェクトを見てきました。�良い印象を持っています。�MQTTライブラリを作っているようですが、�Boostに提案する計画はありませんか?

redboltz

そうしたいと考えています。

Boostの提案のプロセスから理解しようとしています。

2023/09

2023/09

9 of 36

BoostMLにasync-mqtt5正式提案

9/36

2023/11

async-mqtt5をBoostライブラリに正式に提案します。

これから、プロセスを進めていきます。

このタイミングでは準備が間に合いませんでした。

提案には時期尚早なので、開発を進めます。

2023/09

11

Ivica Siladić

redboltz

joaquintides

10 of 36

Boostライブラリ採用までの流れ

  1. Boostについて学ぶ。MLや他のライブラリを参考に。
  2. 提案予定のライブラリが扱うテーマに関心があるか聞いてみる。
  3. 開発を進める。
  4. 議論、洗練、書き直し、を満足いくまで繰り返す。
  5. レビューの推薦(Endorse)を得る。
  6. Review Managerを探す。
  7. Formal Reviewを行う。
  8. Boost web siteに登録する。
  9. People Pageに自分の情報を登録する。
  10. メンテナンスを継続する。自身がメンテできなくなった場合は、�新しいメンテナを募集する。

10/36

2023/11

2023/11当時の情報です。最新はこちら

11 of 36

BoostMLにasync_mqtt正式提案

11/36

2024/05

完成度が上がり、提案できるレベルになったと思います。

まだ関心があれば推薦お願いします。

async-mqtt5のレビューが、MLを見る限り全然始まらないまま、半年経過。

OK!

2023/09

11

2024/05

redboltz

joaquintides

12 of 36

なぜ提案をぶつけたのか?

  • async-mqtt5と比較して、async_mqttの方が、優れている�という強い確信があった。
    • 具体的に何が優れているのかは後述します。

  • 今提案せずに、async-mqtt5がBoostに採用されたとして、�その後に提案した場合の仮想シナリオを考えてみた。
    • なぜこのタイミングで提案するのか?
    • async-mqtt5のレビューを見ていたのなら、何らかのアクションを�取ろうと思わなかったのか?

  • そんなわけで、提案をぶつけることにした

12/36

13 of 36

Slack上での洗練

  • ここで、先行するasync-mqtt5の議論が進んでいるslackに�参加するように促され、join。
  • 鬼のようなダメ出しを喰らう
    • Executorの使い方がなっていない。
      • ライブラリ内部で、bind_executor()を用いていたがそれはダメ
        • co_composedという仕組みの複雑な仕様に翻弄される。
      • Associated Characteristicsの伝搬がなされていない
        • allocator, cancellation slot, など
    • エラーの伝搬の仕方がBoost.Asioの流儀に則ってない
    • などなど

  • これらを、ひとつひとつ調べながら全て対応
    • その間も、slackでいろいろ指摘され、放置もできないので、�素早い反応を優先すべきか、修正を優先すべきか、�考える時間がもったいないとか、焦る。
    • 結局、まずは反応することを優先した(多分正解)

13/36

Vinnie Falco

Klemens Morgenstern

14 of 36

印象に残った指摘

  • 当初、少し(日本人的な?)遠慮があり、先行するasync-mqtt5と比べて何が優れているのかを示す「評価軸」を提示を行っていた。
  • Vinnieより�「もし私が新たに提案する側(つまり私)ライブラリの作者で、競合相手と共にレビューに臨む状況であったとしたら、本の少しでも改善を実現するために、24時間休むこと無く必死で作業を続けるだろう。。。まあ私はかなり競争心が強い方だがね」�「たとえば、ライブラリドキュメントに「比較」セクションを設け、競合ライブラリを徹底的に分析して比較するだろう。レビュワー自身に判断させる前に先回りして答えを提示するのが良いだろう

14/36

15 of 36

そんなわけで比較を追加

15/36

これが後述すると言っていた、優れている項目の一部

16 of 36

見つからないReview Manager

  • BoostML上でReviewManagerを募集
    • 誰もなってくれない
  • Slack上で募集。個人メッセージなども送る
    • 既にasync-mqtt5側のReview Managerを引き受けているので�利益相反となるので受けられないという人も。ごもっとも。
  • どんどん時間が進んでしまう。
    • BoostConなどであったことのある人などに、メール、メッセージなど
      • いきなりどうかと思うところもあったが、背に腹は代えられないので、�これも経験、と鈍感力を発揮!
  • 重鎮のRobert Rameyが引き受けてくれそうになったが、�逆に私と近い(友人)ためにフェアな評価ができないと、�コミュニティに2つのライブラリ同時レビューなどを提案。

16/36

17 of 36

見つからないReview Manager

17/36

I've been contacted by my friend and sometime collaborator Takatoshi Kondo regarding his submission of a boost library:

https://github.com/redboltz/async_mqtt. It has received two endorsements. He has asked me to be review manager and I'm inclined to accept

this request as he was very helpful to me during the refinement of the boost serialization library. There is also mqtt client library that is

proposed to the Boost. https://github.com/mireo/async-mqtt5 It also has endorsements and a review manager. A cursory examination of the git hub pages suggest considerable overlap between the two submissions. In general, boost has discouraged the acceptance of multiple libraries with this much overlap - and for good reason. An accepted library often becomes the canonical implementation in large parts of the C++ world. Having two high quality libraries that do almost the same thing is not where we would like to be. I would like to propose that we review both libraries simultaneously in order to try to reach a consensus as to which, if either we want to accept. I know this is a difficult task, but I think it is important that we do this. It's going to be tough to reject a well written library because a better one has been accepted. But it would be worse to reject a well written library merely because another one was submitted first. Normally I don’t participate as a review manager as I'm pretty bogged down in Boost stuff as it is. But I'm willing to make an exception in this case due to the importance of this case and my strong personal ties to Takatoshi. BTW - who is the review wizard these days? I presume that he will be the one making the decision about this.

友人で時折コラボレーター(共同制作)も務めている近藤貴俊さんから、彼が提出したBoostライブラリ(https://github.com/redboltz/async_mqtt)について連絡がありました。このライブラリはすでに2つの支持(endorsements)を得ています。彼は私にレビュー・マネージャーを務めてほしいと依頼しており、以前Boost Serializationライブラリのブラッシュアップの際に彼が非常に力になってくれたこともあって、私はこの依頼を引き受けたいと考えています。

一方で、別のMQTTクライアント・ライブラリ(https://github.com/mireo/async-mqtt5)もBoostに提案されています。こちらも支持を得ており、すでにレビュー・マネージャーも決まっています。

GitHubのページをざっと確認したところ、これら2つの提案にはかなりの重複があるようです。一般的にBoostでは、これほど重複する複数のライブラリを採用することは推奨していません。それには正当な理由があります。一度採用されたライブラリは、C++界の大部分において「標準的な実装」となることが多いからです。ほぼ同じ機能を持つ高品質なライブラリが2つ存在するというのは、私たちが望む状況ではありません。

そこで、どちらか一方を採用するか、あるいは両方見送るかのコンセンサスを得るために、2つのライブラリを同時にレビューすることを提案したいと思います。これは困難な作業になるとは承知していますが、実行する価値のある重要なことだと考えています。優れたライブラリを「他にもっと良いものがあるから」という理由で却下するのは心苦しいものですが、「単に他方が先に提出されたから」という理由で却下するのはそれ以上に避けるべき事態です。普段、私はBoost関連の仕事で手一杯なため、レビュー・マネージャーを引き受けることはありません。しかし、今回の件の重要性と近藤さんとの強い個人的な信頼関係から、例外的に引き受けるつもりです。

ところで、最近の「レビュー・ウィザード(Review Wizard)」はどなたが担当されているのでしょうか? 最終的な判断はその方が下すことになるかと思います。

18 of 36

紛糾したが先着優先ルールに決定

  • Review Wizardによる決定がなされた
    • 私(redboltz)の提案の影響で、先行提案のあった、async-mqtt5のレビューが�進まない状況が半年以上経過している
      • async-mqtt5の提案者にとってこれはフェアではない
    • 同時にレビューという案を検討したが、結局だれもそれはできない�というコミュニティの反応であった。
      • 可能性として、両方採択、片方採択、両方却下についても検討したが、�レビュー前に方向を決めるのはどうか、という議論もあり、現状が�レビューを困難にしているという見解
    • そこでデフォルト先着優先とする
      • コミュニティ(もちろんredboltzもコミュニティの一員)から、�これを超える現実的かつ説得力のある提案があれば、この限りではない。�それは都度判断する。�また、コミュニティがこのルールに納得せず、レビューをボイコットするのも自由である
      • なお、これは今後の同様のケースにも適用されるルールとする。
    • 2024年10月16日~25日 で Formal Review 決定

18/36

2023/09

11

2024/05

2024/10

19 of 36

ここで私はいったん傍観者に

  • レビューアーとしての参加も一瞬考えたが、フェアなレビューを�行うことや、それをコミュニティに納得させることが困難
  • まずはレビュー結果を待って今後のことを考えよう

19/36

20 of 36

恐ろしく紛糾したレビュー

  • Boost.Asioベースってそもそもどうなん?という指摘
    • 後ほど詳細に触れる
  • ライブラリビルドすることでコンパイル時間を短縮できるオプションはないのか?
    • template ベースの header only libraryであったとしても。
  • そんな感じでレビュー期間が終了(約2週間)
  • 結果はConditionally Accepted(条件付き採択)
    • 条件は比較的些細な修正の実施程度の軽いもの。
    • 後半、作者が厳しい指摘に明確な回答が無いまま時間切れ。なぜ質問に答えない?
    • Slack上でその辺りは議論されていたとのコメント(作者によるものでは無い)
    • BoostML以外の場所での議論はレビューでは考慮されるべきでは無い。�Slack上で議論があったとしても、それをBoostMLで無視せず質問に答えるべき。
    • さらにさらに燃える。
    • OKわかったよ。結果は認める。今後の改善に期待するしかない。

20/36

21 of 36

Sans-I/Oなプロトコルライブラリという問題提起

  • Boost.Asio上で動作する各種通信ライブラリに関する�懸念が指摘された。
    • 既存のBoost.Redis, Boost.MySQL, など。
    • これに、MQTTなど、どんどん増やしていって良いのか?
    • Boost.Asioよりもパフォーマンスの優れた非同期メカニズムを�利用しているが、そこで使えないのが問題。
  • I/O independentな純粋なプロトコルライブラリが必要
    • 俗に言う Sans-I/O なライブラリ
    • Sansとは、「○○の無い」という接頭語

21/36

22 of 36

レビュー指摘を(勝手に)反映する

  • 自分がレビューを受けていたとしても、厳しい指摘はあっただろう
  • せっかくなので、レビューの指摘を、自ライブラリに当てはめ、�全部対応しておくことにする
  • 今後、どうするかは未定だが、ここまでの経験を活かすには�良い進め方という気がする

22/36

23 of 36

async_mqttをSans-I/O化

23/36

include/async_mqtt/protocol

asio_bind

Sans-I/Oのプロトコルマシン

Boost.Asioからプロトコルマシンを使う

24 of 36

Sans-I/O化

24/36

Protocol State Machine

受信バイト列通知

送信API呼び出し

設定API呼び出し

コールバック呼び出し

全て同期間数

25 of 36

Sans-I/O化 (プロトコルで規定されたタイムアウト)

25/36

Protocol State Machine

送信API呼び出し

設定API呼び出し

タイマ発火通知

コールバック呼び出し

受信タイムアウト10秒を設定せよ

利用者側でタイマ設定

受信バイト列通知

タイマ時間経過したら

その旨を通知するAPI呼び出し

全て同期間数

26 of 36

Sans-I/O化 (プロトコルで規定されたタイムアウト)

26/36

Protocol State Machine

送信API呼び出し

設定API呼び出し

タイマ発火通知

コールバック呼び出し

受信タイムアウト10秒を設定せよ

利用者側でタイマ設定

受信バイト列通知

タイマ時間経過したら

その旨を通知するAPI呼び出し

全て同期間数

例えば、Boost.Asio。あるいは、RTOS周期処理など。

27 of 36

コールバック引数の戻り値化

27/36

Protocol State Machine

送信API呼び出し

設定API呼び出し

タイマ発火通知

コールバック呼び出し

別の同期間数呼び出し

受信バイト列通知

全て同期間数

同期間数が戻る前に内部でコールバックは呼ばれる

コールバック連鎖でstack overflowするかも?

28 of 36

コールバック引数の戻り値化

28/36

Protocol State Machine RV(Return Value)

送信API呼び出し

設定API呼び出し

タイマ発火通知

受信バイト列通知

全て同期間数

Protocol State Machine

イベント variant

コールバック引数を格納

イベントvariant vectorに追加

コールバック呼び出し

コールバックは 0..N 回呼ばれる

戻り値としてvectorを返す

注意点

(戻り値化する前の)コールバック内で、ユーザに副作用のある処理の完了を期待する設計にはできない。

全てのコールバックを呼び終わって、その引数をイベント化して、順序通りvectorにして返すため。

今回そういう設計ではなかった

29 of 36

Sans-I/O化したメリット

  • Boost.Asio以外からも使える
    • 同期間数+コールバックという原始的な仕組みのため、�OSの無い環境などからでも使える
  • プロトコルに関するテストが簡単に書ける。
    • テストを網羅しやすい。
    • タイマがすれ違いで発火して。。。とかが、�単純に同期間数を並べるだけで表現可能。
  • 論理的なプロトコルと、それを使って実際に動かすための�メカニズム(例えばタイマやソケット)を�明確に意識できるようになった。
    • 意識せざるを得なくなった。

29/36

30 of 36

その他レビュー指摘対応(余談)

  • ヘッダオンリーライブラリと、ビルド済みでリンクする�いわゆる普通?のライブラリの共存を実現
    • Sans-I/O部分は比較的容易
  • Boost.Asioでは、any_io_executorや、any_completion_handler�といった型消去された型をライブラリ内部で利用し、�そこをプレビルド可能なライブラリ境界とする
  • テンプレートは、(あまり見かけない)template構文を使って、�強制的に実体化させる
  • 想定が可能なテンプレート引数(MQTTのパケット型など)に関しては、Boost.Preprocessorを用いて、全組み合わせを生成
  • 想定不可能な部分はヘッダオンリー部分に残す。
    • 如何に型消去部分に流し込むかがポイント
  • コンパイル時間 28.9秒から15.6秒に短縮
    • プレビルド部分は、1分以上かかる(1回だけ)

30/36

31 of 36

その後の展開

  • Boost.Asio周りは騒がしい印象
    • VinnieによりAsioのFork提案がなされたが、その方向では進まなかった
      • 背景には、Asioプロジェクトのコミュニケーション問題がある
      • issue PR放置や、独自の方法でのマージなど
    • Capy、Corosioなど、CoroutineベースのAsio代替ライブラリをVinnieが提案
      • なるほど、そう来たか、という印象
      • これらの開発に生成AIを活用していて、著作権、訴訟リスクなど、弁護士を交えた�議論が進んでいる模様
  • async_mqttをBoostに再?提案はしないことにした
    • protocol部分だけ切り離せるが、asio_bindも含めないと実際通信できない
    • Asioが上記のように微妙。
  • ここまで得た知見を活かしてRustに移植することにした
    • Sans-I/OのMQTT ライブラリ(crate)無いみたいだし

31/36

32 of 36

Rust版

32/36

include/async_mqtt/protocol

asio_bind

mqtt-protocol-core

mqtt-endpoint-tokio

mqtt-client-wasm

mqtt-broker-tokio

C++

Rust

Cargoとcrates.ioという環境があり、ライブラリ(crate)配布や分割が容易

mqtt-protocol-core

mqtt-endpoint-tokio

mqtt-client-wasm

mqtt-broker-tokio

依存関係

33 of 36

mqtt-protocol-core

  • https://crates.io/crates/mqtt-protocol-core
  • Sans-I/OのMQTTプロトコルcrate。
  • 同期メソッドで構成され、戻り値としてEventのenumのVec。
  • コールバックは所有権などの扱いが難しくなりそうなので、�直接戻り値で実装。

  • C++とRustの対応は、vector→Vec、variant→enum

33/36

34 of 36

mqtt-endpoint-tokio

  • https://crates.io/crates/mqtt-endpoint-tokio
  • mqtt-protocol-coreを使って実際に通信するcrate。
  • async/awaitによる非同期。
    • C++のAsioのCompletionTokenは、コールバック、future、coroutineなど�いろいろ考えないといけなかった(かなりAsioが吸収するが意図せぬコピーなど落とし穴多数)のでそれと比べてとても楽。
  • TCP, TLS, Websocket, Quicに対応
  • endpointという名前が示すように、クライアントとサーバ(broker)側両方で使える(async_mqttと同じ)
  • これを使ってbrokerを作ってみたのが、mqtt-broker-tokio

  • C++とRustの対応は、asio→tokio

34/36

35 of 36

mqtt-client-wasm

  • https://crates.io/crates/mqtt-client-wasm/
  • mqtt-protocol-coreを使ってbrowserやNode.jsで通信するclient
  • Websocket (含むTLS)対応
  • Node.jsでは、TCP, TLS,も対応
  • wasm_bindgenという仕組みを使ってJavaScript用の�インターフェースを作っているが、�enumやtraitといったRustの言語要素を、JavaScriptで使えないため�それなりの規模のアダプタコードを書く必要がある。
  • プロトコル部分は、Rustでテスト済みのmqtt-protocol-coreを�使えるので、安心感はあった。�

35/36

36 of 36

まとめ

  • あのBoostに新規ライブラリ提案した経験は貴重なものだった。
  • Review Wizardによる最終決定の仕組みを確認できた。
    • 決めることの重要性、納得性のある(反論の隙が少ない)表現、フェアネス
    • いろいろ紛糾したが、代案がなければ先着優先という、�明確なルールを制定するきっかけになれた。
  • コミュニティの皆さんに感謝。
    • Boost.Con(C++Now)やCppConで会った人々が、いろいろ助けてくれた。
    • 高橋晶さんは、実装の相談に乗ってくれたり、Asioのバグを一緒に探してくれたり、多岐にわたり協力してくれた。
  • ライバルのレビューを自分事として受け止めることで、�自身が大きく成長したり、ライブラリの品質も改善された。
  • Sans-I/Oの仕組みを導入したことで、Rustへの移植が比較的容易で、wasmという展開にもつながった

36/36