Rails:「Pagy」gemでRailsアプリを高速ページネーション(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Rails:「Pagy」gemでRailsアプリを高速ページネーション(翻訳)

私はRuby on Railsがバージョン0.8の頃から開発者として使っていることもあり、その間に誇大広告気味のRubyGemを星の数ほど目にしてきました。

成熟したソフトウェア開発コミュニティと同様、特定の用途では事実標準のgemとして永らえているライブラリもありますが、他の多くのライブラリは誰もサポートしてくれないまま、採用に値しない瓦礫の山を築いています。

用途によっては1つや2つgemをあえて追加することもありますが、ほとんどどの機能にもそれぞれに相応しいチャンピオンが君臨しているものです

Railsの進化がたどった道

以下の図は、ここ10年の間に新しいライブラリやアイデアにコミュニティが貢献した様子を示しています。

年ごとのRubyGem数

当初のgem数は増加していますが、2015年をピークに以後は減少しています(成熟したとも言えるでしょうか)。私は、2018年のgem数は2009年とほぼ同レベルであろうと睨んでおり、この傾向が続くならば新しいgemの数は今後も減少し続け、安定期に入るでしょう

ここからさまざまな疑問が湧き起こります。「Railsの潜在能力は2015年がピークだったのか?」「Railsというプラットフォームの採用も減少しているのか?」「むしろこれは成熟の証で、Railsは大企業が採用してよいものになったのだろうか?」

私としては、このデータだけでは何とも言えません。むしろそうした視点とは違うアプローチによって、今日の私たちの仕事に大きなインパクトをもたらす部分に着目してみたいと思います

新しいgemの数が減少するに連れて、今の私たちの仕事に大きく影響するような画期的なものはなかなか出現しなくなるだろうと考えられます。しかしそんなことはありません。それを今から証明いたします。

Pagyとの出会い

Ruby on Railsで開発したことがある方なら、アプリのindexページをwill_paginateやKaminariでページネーションしたことがおそらくあるでしょう。Rails経験の浅い方なら、今後そうしたページネーションgemを探すことになるでしょう。

PagyはRails向けの新しいページネーションライブラリです。パフォーマンスを念頭に置いて開発されており、かつ新規または既存のRailsアプリの使いやすさを損ないません。

はい、今皆さんが思っていることを当ててみせましょう。

「お、今度のページネーションgemこそ俺たちが求めていたもの…なわけないだろ!」

そこについては私も同感です。オープンソースでソリューションが分散してしまうのはよくないと思います。しかも、このPagy gemによってもたらされる改良点のいくつかは、既にKaminariでも実装が提案されたのですが、リジェクトされました。おそらく、改善にはKaminariのコア部分で大規模な変更が避けられなかったからでしょう。

何かが華々しく登場したら、ときにはそれが本当にベストなのかどうか一歩身を引いて考える必要があると、私は固く信じています。そしてページネーションについて言うなら、このgemには疑いの余地はありません。理由を説明しましょう。

pagy-kaminari-will_paginate-memory-used-per-page-shown

この図がすべてを物語っていると私は信じています

アプリが数百〜数千ものユーザーを同時にさばいている状態では、ちっぽけな機能を1つ足すだけでもリソースに大きな影響を及ぼすことは容易に想像が付きます。20ページに対して実施した1つのテストの結果を見ると、will_paginateはうまくやっている一方、Kaminariのメモリ容量は著しく増大しています。しかしPagyはwill_paginateよりさらに優秀です。数千人ものユーザーをさばこうとするとき、この違いがもたらすインパクトを今一度考えてみてください

さらに詳しく見てみましょう。今度はそれぞれのgemのメモリフットプリントを比較します。

pagy-kaminari-will_paginate-total-memory-used

Kaminariと同じジョブを、Pagyは遥かに少ないメモリで実行できます。will_paginateもKaminariより省メモリではあるものの、Pagyと比べると7倍にものぼります。さらに皆さまに考慮いただきたいのは、will_paginateが最後にリリースされたのは1年も前のことで、リポジトリにはプルリクが何十個もたまったまま、最終コミットの日付は2017年7月のままになっている点です。

今仮に新規プロジェクトがスタートすることがあったとしても、will_paginateは選択肢には含めないでしょう。

はい、皆さんはここできっとこう思うでしょう。

「よっしゃ気に入った。ところでPagyが他のgemと比べてメモリフットプリントがここまで少ない理由が謎なんだけど、どうやってんの?」

よくぞ聞いてくださいました。ご説明いたしましょう。

pagy-kaminari-will_paginate-number-of-objects-created

実は私も、当初同じ疑問を抱いたのでした。

「Kaminariが生成するオブジェクト数が6,000個超えってどういうこと?」

もちろんwill_paginateの方が省メモリではあるものの、それでも20ページのページネーションでオブジェクト数が3,000個超えというのはあんまりです。一方Pagyのオブジェクト数は400個を下回っています。

Pagyの方が優秀な理由

pagyのどこがそんなに違うのでしょうか?ご説明いたします。

  • PagyはRubyオブジェクトではなくintegerで計算している
  • Pagyのコアコードは60行にも満たない
  • Pagyはアプリのモデルに癒着しておらず、HTMLやURLや複数形化(pluralization)や式展開を独自に生成する。
  • Pagyは、一般的なヘルパーではなく完全に特殊化したコードを用いている。
  • コードが専門化していることで、作者が1行ずつ丁寧にベンチマークを取っている(コードが100行もなければ十分可能です)。

上の理由のおかげで、pagy gemのコードはきわめて理解しやすいという実にうれしいおまけまでついています。

しかも拡張で機能を追加することも可能で、いくつかの拡張は組み込み済みなのですぐにでも使えます。

さらなる考察

私がRailsアプリを開発した当初はwill_paginateを使っていましたが、その理由は当時はその業務にベストだったからです。数か月後にKaminariがリリースされると、そちらに乗り移りました(なにぶんあまりにも昔のことなので、乗り換えた理由はよく思い出せません)。そしてつい数週間前にPagyがリリースされるまで、Kaminariを使い続けていました。

私が初めてPagyを知ったときに心底愕然としたのは、私がwill_paginateからKaminariに移行したときにパフォーマンスのことをまったく考慮していなかったことでした。後者は前者よりずっと遅かったにもかかわらず、です。今の私はパフォーマンスを考慮することを覚えました。

これまで私たちが設計・リリースしたアプリで、いったいいくつのgemを取り入れてはパフォーマンスを下げまくっていたことでしょう。

will_paginateやKaminariからPagyへの移行は実に簡単で、必要なコードはわずか数行で済みます。次回の私の記事では皆さまに関心を持っていただけるよう、具体的な移行方法を解説することをお約束いたします。

免責事項: 本記事の画像およびパフォーマンスはPagyの作者が行ったベンチマークテストを用いています。ベンチマークのソースコードはddnexus/pagination-comparisonでご覧いただけます。RubyGem採用の推移チャートには、こちらでご覧いただけるRubyGems.orgのデータを用いております。

訳注: Pagyリポジトリ

関連記事

Rails: パーシャルと`collection:`でN+1を回避してビューを高速化(翻訳)

kaminariでRubyの地雷を踏んだ

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の半分ほど、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れてそれぞれ一部を翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好き。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ