Mcrouter

とりあえずインメモリにキャッシュしておきたい時に使える、良い意味で枯れた技術に Memcached があります。Redis ほど高機能ではなくシンプルで、プロトコルも単純。各プログラミング言語のクライアントの実装もそれなりに枯れており、プロダクションにも投下しやすい。MySQL や PostgreSQL などのバックエンドのパフォーマンスボトルネックを軽減する時に、Memcached クラスターを投下するケースは結構あります。

単体インスタンスでもそれなりにスケールするので、AWS Elasticache for Memcached といったマネージドサービスの進化も相まって、サクッと導入しやすい。そう、「それなり」のスケールまでは。

Mcrouter は元々、 Facebook (Meta) から発表された "Scaling Memcache at Facebook" という論文で紹介されているサービスの一つです。Facebook や Instagram のピークタイム時に 5 億 Request Per Second (RPS) を裁く必要があった Facebook は、キャッシュプラットフォームでも数々の工夫を凝らす必要がありました(そりゃ RDBMS 層をいくら頑張ってもこのスケールは捌けないです)。

Mcrouter は、要するに Memcached の ASCII プロトコルを理解する、色々な機能が盛り込まれた優秀なプロキシです。facebook/mcrouter で開発されています。

クライアントから見るとサーバーのように、サーバーから見るとクライアントのように、Transparent に見えるのが売りです。要するに、Memcached Servers 側に数々の独自実装を加えたりフォークして管理したりする必要がなく、間に挟むだけで割といい仕事をしてくれる良いやつなんです。

Mcrouter architecture

スケーラビリティの向上から耐障害性の向上などを目指して、 connection pooling, flexible prefix routing setup, transparent large values composition, auto failover など数々の機能が実装されています。

例えば、その機能のうちの一つである Connection Pool。これは、複数の Memcached インスタンスを "Pool" で管理しましょうというもの。マッピングは全て Mcrouter に閉じており、Consistent Hashing で良い感じにルーティングしてくれます。まあこれは特別珍しい機能では無いです。

Mcrouter Connection Pool

ルーティングも柔軟な設定が可能です。例えば Prefix Routing は、オブジェクトのキーに対して Longest Prefix Match でルーティングしてくれます。これによって、ワークロードによって異なるキャパシティのプールを割り当てることができます。具体的には、「Rails の Page Cache は Workload 1 に」とか「ActiveRecord の Cache は Workload 2 に」と言った感じです。プールに割り当てるインスタンスサイズや設定を変えておけば、ワークロード間で最適なプールを割り当てて更なるパフォーマンス向上を目指すこともできるでしょう。

Mcrouter Prefix Routing

ネットワーク障害やハードウェア障害など、一時的な障害が起こってリクエストを捌けないプールが出てきた時に、フェイルオーバーする仕組みもあります。エラーレスポンスが帰ってきたときに、事前に指定した避難先に一時的にリクエストをプロキシする、という単純なものではあるのですが、Gutter サーバーという特別なプールを用意しておくという使い方をすることができます。これは元の論文でも紹介されている手法です。プラットフォーム全体が影響を受けるような障害には対応できないですが、メンテナンス事故などを含めた小さいスコープでの障害に対しては強いです。Gutter サーバーのおかげで、緊急時もバックエンドのデータベースに対して不要な負荷をかけずに済みます。

Mcrouter Failover to Gutter

キャッシュを取り扱う時の問題の一つに、Cold Cache 問題がありますよね。要するに、インスタンスを立ち上げたばっかりの時は何もキャッシュされていないので、そのインスタンスに対するリクエストは基本全て Cache Miss。したがって元データを取得するために全てのリクエストがバックエンドにあるデータベースに向かいます。アプリケーションのワークロードやビジネス要件によっては、またはデプロイやロールアウトのタイミングなどが最悪の形で重なると、バックエンドが捌けない負荷が集中して障害を引き起こしてしまうケースもありますよね。一つの解決策としては、単純な発想ですが、Cold Cache を本番に投入する前に事前に"暖めて"おけば良いですね。Mcrouter は Cold Cache の暖気も良い感じにやってくれます。

Mcrouter Cold Cluster Warmpup

内部実装としては C++11 で実装されています。マルチスレッドで、One thread per Core。マルチコア環境でデプロイするには不満のない設計がされています。Fiber を用いて、スケジューリングを OS に任せずに自分たちで管理していますね。Fiber の数やサイズは設定可能です。本番環境の実績を見ながらチューニングできる要素の一つですね。

2023-07-25