HTTPキャッシュを活用してAPIのパフォーマンスを3倍にした話

Ubiregi Advent Calendar 2018 17日目の記事です。

 

1日目の記事「ユビレジ開発チームのご紹介 - katorieのブログ」で紹介されてるように、今はリライアビリティチームが結成されて日々サーバーの安定性・パフォーマンスを気にかけてくれていますが、2年前くらいまではほぼ自分1人でコツコツとパフォーマンスチューニングなどをしてました。その頃の昔話です。

 
ユビレジAPIの概要

ユビレジAPIは、主にユビレジのiPadアプリ上のデータを最新に保つために差分を取得して同期を取る目的で作られています。

雑に説明すると、`?since=2018-12-17T00:00:00:Z` みたいな「いつ」以降の差分が欲しいかをパラメータで指定し、それ以降の差分があればJSONで返す、みたいなものです。

 
HTTPキャッシュを使う様にした

元々の実装では差分が存在しない場合でも条件通りにDBのクエリを流して結果が空なら空配列をレスポンスで返す、の様な実装になっていましたが、差分がない事が分かれば色々処理をすっ飛ばして304 Not Modifiedを返せばいいのでは!と思い立ってレスポンスヘッダでLast-Modifiedとキャッシュ関連ヘッダを返す様にして、リクエストでIf-Modified-Sinceを送ってもらう様にしました。

参考: ブラウザのキャッシュコントロールを正しく理解する - Qiita


ありがたい誤算

クライアントのiOSアプリ側も改修が必要かと思っていましたが、NSURLRequestは勝手にNSURLCacheを使ってくれる様で304が返ってきたらキャッシュ上のレスポンスが返ってきたかの様に振舞ってくれたのでサーバー側の改修のみで済みました。

 
結果

この改修の結果、平均300ms近くかかっていた重い処理(グラフのは特に重いAPI)が、平均100msを切るほどの改善! Herokuのdyno数も3分の1ほどまで減らせました。

f:id:pomu0325:20181213181304p:plain

NewRelic

 

オマケ(没案) 

タイムスタンプより厳密なE-Tagを使おうと思ったけどActiveRecordのcollectionからいい感じのkeyを生成するのが難しそう(&重そう)なのでやめた。

参考: Use collection ids and timestamps to generate truly unique collection cache keys by christos · Pull Request #21503 · rails/rails · GitHub

 

 

最後に

ユビレジでは柔軟な発想でパフォーマンス改善をできたりする様なRailsエンジニアも募集していますよ

go.ubiregi.com