こんにちは。LINE Plus CorporationのContents Service Engineering組織でバッ クエンド開発を担当しているKang Hyun Yangと、Byungchan Leeです。私たちが所属しているContents Server EngineeringではTech Groupという組織を運営しています。Tech Groupでは、さまざまな組織が直面する共通の課題に取り組み、特に大量のトラフィックを効率的に処理する方法を模索してきました。その模索の中で、キャッシュプロセスの高度化を行い、その結果、「req-shield」という内部ライブラリを開発しました。
この記事では、req-shieldを開発した背景と詳細機能を詳しく紹介したいと思います。
一般的にアプリケーションがキャッシュを利用する方法
通常、アプリケーションは以下のようにキャッシュを利用します。
クライアントがリクエストを行うと、そのリクエストに対応するコンテンツがキャッシュにあれば、クライアントはすぐにレスポンスを受け取ります。もしキャッシュに対応するコンテンツがない場合は、バックエンドのデータストア(DB、サーバー、ファイルなど)からデータを取得し、キャッシュに保存します(上の図では便宜上「MySQL」と表現されている)。
このような仕組みで、仮にN個のクライアントのリクエストが入っているとしてみましょう。このとき、キャッシュにN個のリクエストに対応するコンテンツがなければ、N個のリクエストはすべてバックエンドデータストアにデータをリクエストし、N個のリクエストがすべて同じ値を求めていた としても、N回のリクエストがそのまま行われます。さらに、特に何の措置も取らなければ、バックエンドデータストアから受け取ったデータをキャッシュにN回記録する作業も行われます。これはバックエンドストアとキャッシュの両方に負荷を引き起こす原因となり、「Thundering Herd問題」と呼ばれます。
req-shieldの開発は、その「Thundering Herd問題」を解決する方法を模索することから始まりました。
req-shieldのアイデア
この問題を解決するカギは、クライアントからバックエンドデータストアへのリクエストをどのように減らすかです。私たちはキャッシュにヒットするかどうかによって、2つのアイデアを適用できると考えました。
リクエストされたデータがキャッシュに存在しない場合
リクエストされたデータがキャッシュに存在せず、バックエンドストアから取得してキャッシュにロードする必要がある場合、アプリケーション側でローカルまたはグローバルロック(lock)を使って特定のリクエストだけをバックエンドストアに送ります。他のリクエストは、ロックを取得したリクエストがキャッシュにデータを作成するまで待てば、問題を解決できます。