色々有識者の方に相談に乗ってもらったのでシェア。
要件的にはまず完成を目指して、チューニングはちょっとあとからということで進んでいたのだけど、 せっかく作るのだから少ない労力である程度の規模までは戦えるようにしたいよねーくらいなレベルで、少し考えて永続化するミドルウェア・サービスを選定してみることにした。 *1
RDBMS
まずこれは一番扱いやすいしすでに資産としてある。 技術者も多いので真っ先に思い浮かぶんだけど、有識者と相談して、
- 「メッセージングで利用するのであれば、すぐに限界来るだろうし...」
- 「トランザクションが不要な要件ではもったいない気がする...」
- 「とりあえずちょっと考える時間あるし、他あたってダメそうなら…」
というまぁ当たり前の結論になって次の選択肢を検討した。
Redis
これも候補として上がってきた。 Pairy何かが最初期はRedisでチャットのメッセージを保存していたようだ。(後にDynamoDBに移行している。)
あとは、これは組み合わせで利用する用途だけど、まずアプリケーションはRedisにメッセージを書き込んで、 一定時間毎にRDBMSなんかにBulkInsertするような仕組みもいいね、という話にもなった。
ただデータの永続化が難しいこと、AzureのRedisCacheでクラスターをシュッと立てるにしてもかなりお高い値段なのと、管理が大変そうなので、現実的ではない... なので、「Redisだけ」という選択肢はすぐに外れた。 また、SidekiqあたりでRDBMSへの書き込みを非同期に行うケースも結構ありなのなとは思った。
HBase
正直、キャッチアップしている余裕もないしスモールスタートできなそうということで候補からはあっさり外した。
DynamoDB
プライシングでもスモールスタートできるし、 データベース構造でもメッセージングに最適だと思っていて、初めて登場してからしばらく立っていて実装も枯れている。*2 RubyのSDKもあり、今回の案件で自分がが求めているものに一番近いように感じた。
ただ、社内的な制約があって弊社ではメインのクラウドサービスにAzureを利用していて、 できればAzure内のサービスで完結させておきたいという気持ちがある。そこで候補に上がったのがCosmosDBだった。
CosmosDB
AzureでのDynamoDBの代替サービスだけど... 自分は100%そうじゃないと思っていて、理由としてはCosmosDBが複数のAPI(接続方法)に対応しているからである。 その中でサポートしているものから、候補を模索していくことにした。
MongoDB API
結構最終候補くらいまで残っていた。
理由はRDBMSよりもスケールアウトしやすいのと、そこまでの整合性が必要ではなかったこと。 Mongoはメインで利用している言語(Ruby)のサポートもそれなりにあって、結構知見があったことから。 ただ、CosmosDBでもいくつかの接続APIがあり、それが異なることによって、スループットの差が「多少」生じるとのことだった。 Mongoはスキーマレスだけど、大量Write,Readのあるようなチャットに向くソリューションかどうかわからないので、一旦他の方法を検討してみることに。
Cassandra API
MongoDB互換だけでなく、Cassandra互換も用意されていた。
Cassandraの構造が一番メッセージングに適している、かつマネージドなので非常に良い選択肢として模索していた。 が、Cassandraの構造からメッセージングサービスのデータ構造を設計するに妙に難しさを感じていて、 心が折れてしまったので、ほかを当たってみることにした。
あとは扱えない技術者が少ないのがネックかも。
Firebase Firestore
Firebase RealTime Databaseのクエリ周りをいい感じにしたプロダクト。
RealTime Databaseよりもクエリ周りが便利なのと、WebフロントエンドやiOS, AndroidのSDKからも書き込みが直接可能で、 Realtime Database共々、チャットアプリを作成するのによく利用されているイメージがある。
更に永続化だけではなく、 フロントエンドから読み込みを行うことでリアルタイムで追加・更新・削除処理が簡単に行えるので、 Websocketあたりのミドルウェアを自前で用意する必要もなくなった。 永続化のサービス・ミドルウェアの選定というところから少しスコープが外れているかもしれないが、これは本当に大きかった。
ただ、永続化サービスという意味ではちょっと違うかもしれない…?
いろいろ考え、有識者に相談した結果
RDBMSとFirebase(Firestore)の併用案にひとまず落ち着いた。 基本的にはメッセージの参照・読み込みをFirestoreに、それ以外のリソースに関してはAPIを経由して、RDBMSを参照するようにしている。 メッセージの書き込みをAPIを経由して書き込み、APIサーバから非同期でFirestoreにWriteを行っている。
やはり、チャットの大部分を占める読み込みをFirestoreに任せることで、 Websocketの実装や管理が不要だったのが一番大きいかなと。
別にFirestoreだけでもいいじゃんということなんだけど、Firestoreだけでの運用は、
- やっぱりベンダーロックインが怖いこと。
- フロントエンドから色々書き込むのは処理が煩雑になりがちになってしまうこと。
- 既存のアプリケーションの1機能として入れるので、サーバーサイドを通したほうが実装と管理がラク。
- チャットに投稿できるかどうかをサーバーサイドで判定してから、Firestoreに流すとかやりたい。
- RealtimeDatabaseに比べてクエリ周りで便利になったとは言え、検索周りでAPIあったほうが今後便利では
ということになりこの案を採用して現在は動いている。*3
そんな感じで、現在はメッセージングアプリケーションを作っている最中だ。 サーバーサイドの観点では結構手抜き構成にしてあったのが幸いして、 苦労しがちなフロントエンド周りに時間を掛けることができて非常に助かっている。