Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

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

概要

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

日本語タイトルは内容に即したものにしました。


  • 2018/05/16: 初版公開
  • 2021/07/13: 更新

ddnexus/pagy - GitHub

202107/13時点ののPagy 4.10.1ではRuby 2.5以上が必須です。それ以前のバージョンのRubyではPagy3を使います。

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ページに対して実施したテストの結果を見ると、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_pagenateは、2020/02/25に3.3.0がリリースされています。また、2021/07/13時点でオープンされているプルリクはゼロです。

今仮に新規プロジェクトがスタートすることがあったとしても、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のデータを用いております。

関連記事

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

kaminariでRubyの地雷を踏んだ


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。