Mercari Engineering Blog

We're the software engineers behind Mercari. Check out our blog to see the tech that powers our marketplace.

メルカリチャンネルにおけるFirebaseの利用例

Mercari Advent Calendar 2017 の16日目は@sota1235がお届けします。

この記事では私のチームが開発しているメルカリチャンネルでFirebase Realtime Databaseを使うにあたり行っている工夫をご紹介します。

同じ文脈の話を今年のPHPカンファレンスでも発表したのですが、この記事ではその時お話できなかったもう少し細かい工夫を4つ紹介したいと思います。

Realtime Databaseへのリクエストを間引く

Realtime Databaseは非常に高トラフィックな通信を捌くことができます。 とはいっても無尽蔵にデータ更新処理をしたり読取処理をできるわけでは当然ありません。 メルカリチャンネルでは以下のように多くの用途にRealtime Databaseを利用しています

f:id:sota1235:20171215142303p:plain

これらを全て素直にRealtime Databaseに書き込むとすぐに負荷が上がることが予想されました。 そこでメルカリチャンネルではお客さまの体験を損ねない範囲で、書き込み処理を意図的に間引いています。

例えばいいねの加算処理は毎回送るのでなく、人間が見て違和感のない頻度での更新をしています。 この際、書き込み処理をするかしないかの判断にはmemcachedに特定のキーが存在するかどうかで判断しています。

実際のコードイメージはこのような感じです。

<?php

$memcachd = new \Sample\MemcachedClient();
$firebase_client = new \Sample\FirebaseClient();
$like_counter = new \Sample\LikeCounter();
$live_id = \HttpParameter::get('live_id');

$like_count = $like_counter->increment($live_id);

if ($memcached->get('key-prefix-like-' . $live_id)) {
    // Skip
} else {
    $firebase_client->updateLikeCount($like_count);
    $memcached->add('key-prefix-like-' . $live_id, true, 10); // 10秒間有効なキーの挿入
}

上記の例ではmemcachedに値が無い場合のみRealtime Databaseへの更新処理、及びmemcachedへのキー追加を行っています。 毎回、このキーで値が存在しているかチェックし、存在している場合はいいね数の加算処理だけ行い、Firebaseへの書き込みはしないようにしています。 こうすることによりFirebaseへの書き込み回数を抑えつつ、いいね数の加算処理をすることができます。

変更に強いコード

当然ながらFirebaseはクラウドサービスであり、Realtime Databaseも当然クラウドサービスです。 リリース初期のフェーズではその時の最善策としてクラウドサービスを選択しましたが、将来的に利用サービスを変更したり、自社実装に切り替える可能性も0ではありません。 この可能性を考慮し、メルカリチャンネルではリアルタイムメッセージング回りの通信を抽象化しています。

<?php

interface MessagingPublisher
{
    public function sendComment(string $comment): void;

    public function updateLikeCount(int $like_count): void;

    // ...
}

ビジネスロジックはこのInterfaceに依存することで中身の処理が変わってもコード変更が無いようにしています。 実際、このように実装したことで気軽に具象クラスを書き換えることができていますし、先日出てきたCloud Firestoreへの移行なんかも前向きに検討ができるコード品質になっています。

本体APIサーバへの影響軽減

メルカリの本体APIは非常に多くのトラフィックを捌いています。 なのでもし仮にFirebaseのAPIのレスポンスがタイムアウトするようなことが頻発するとメルカリも引きずられて落ちてしまうリスクがあります。

メルカリチャンネルではこのリスクを避けるため、Firebaseとの通信部分は全てジョブキューとワーカーによる非同期処理で送っています。 ただし、ジョブキューが詰まって実行が遅れると困るような通知(ライブ終了通知等)は同期処理で送っています。

choconの利用

前述の通り、メルカリチャンネルではFirebaseへのリクエストのほぼすべてをジョブキューとワーカーにより非同期で送っています。 しかし、多数のジョブワーカーがFirebaseへの接続を個別に管理していたため、接続にかかるオーバーヘッドが大きいことがわかりました。 そこでid:kazeburo さんの作成したHTTP通信を効率化するミドルウェアであるchoconを利用して、Firebaseへの接続にかかるオーバーヘッドを削減することでパフォーマンスの改善をすることができました。

choconは以下の記事が詳しいです。

tech.mercari.com

最後に

このようにメルカリチャンネルでは愚直な改善を重ねてRealtime Databaseを利用しています。

私はリリースから6ヶ月間の間に渡り、メルカリチャンネルを運用しています。

その期間を経て感じているのは、クラウドサービスをpros/consを正しく理解して付き合うことの重要さです。

Realtime Databaseは非常に強力、かつ優れたソリューションです。

だからといって何も考えずに使ってしまうと良さをきちんと引き出せなかったり、思わぬ罠を踏むことになります。

今後もクラウドサービス始め、何か外部サービスを利用する際はその特徴をきちんと捉えた上で運用していきたいと思います。

17日目はid:masudaKさんです。次回もお楽しみに!