Kontakt os


Det korte svar: Ifølge den paginerings-benchmark, som Pagys forfatter har offentliggjort, er Pagy med stor afstand den letteste af de tre mest udbredte Ruby on Rails-gems. Ved rendering af den samme navigation over de samme records allokerer den 184 objekter hvor will_paginate allokerer omkring 3.200 og Kaminari omkring 6.400. I en applikation med tusindvis af samtidige brugere betyder den forskel noget for din hukommelsesgrænse pr. instans, hvilket direkte påvirker antallet af instanser, du skal betale for.
Lad os først få én ting på plads, da meget af det materiale, der stadig florerer, er forældet: Pagy er ikke længere den lille udfordrer. Den udkom første gang i februar 2018 og er i dag et modent og meget udbredt bibliotek. Det, der har ændret sig, er API'et. Pagy 43 var en total rekonstruktion fra bunden, så de fleste vejledninger, du finder (inklusive vores egne indtil nu), viser kald, der ikke længere eksisterer. Jeg har bygget Rails-apps siden version 0.8 og har brugt alle tre gems til paginering i produktion; denne artikel er skrevet om til den aktuelle version.
Inden vi ser på benchmarken, skal vi se på vedligeholdelsen, for "hvilken er hurtigst" betyder mindre end "hvilken bliver der stadig taget hånd om", når du skal binde en kodebase til den i årevis.
| Gem | Nuværende version | Seneste udgivelse | Vedligeholdelsessignal |
|---|---|---|---|
| Pagy | 43.x | 2026 | Aktivt udviklet; hyppige udgivelser; fuldt redesign i v43 |
| Kaminari | 1.2.2 | December 2021 | Stadig den absolut mest installerede, men ingen udgivelser i over et år |
| will_paginate | 4.0.1 | Juni 2024 | Officielt i vedligeholdelsestilstand: ingen nye funktioner, og README henviser selv brugere til alternativer |
Et par ting kan udledes af den tabel.
will_paginate er ikke død, men dens egen vedligeholder har sat en stopper for videre udvikling. Den fik understøttelse af Rails 7 i 4.0-udgivelsen og modtog en rettelse i 2024, så påstanden om, at "sidste commit var i 2017", som man ser i ældre indlæg (inklusive vores egne), er ganske enkelt forkert. Den korrekte grund til at undgå den i nye projekter er ikke manglende vedligeholdelse. Det er, at vedligeholderen eksplicit har sat den i vedligeholdelsestilstand og er holdt op med at tilføje funktioner. Et fair grundlag for en beslutning; men et forkert grundlag, hvis du henviser til en commit-dato fra 2017.
Kaminari er den etablerede spiller, og det betyder stadig noget. Den har den største installerede base af de tre med stor margin, en omfattende dokumentation, view-skabeloner, som du kan generere og tilpasse, samt konfiguration pr. model. Men dens seneste udgivelse var i december 2021. Den virker, den er stabil, og på et admin-panel med lav trafik vil ingen af de performance-diskussioner, der følger herunder, nogensinde være synlige for dig. Momentum ligger dog tydeligvis et andet sted.
Pagy er der, hvor den aktive udviklingsenergi befinder sig. Udover rå hastighed leverer den nu adskillige paginerings- teknikker i én gem (offset, countless, keyset, keynav, calendar og search), plus rendering på server- eller klientsiden samt interaktive udviklerværktøjer. Den bredde, og ikke kun hukommelsesforbruget, er en stor del af grunden til, at folk vælger den nu.
Antallet af nye RubyGems, der blev udgivet hvert år, voksede gennem starten af 2010'erne, toppede omkring 2015 og har været faldende siden. Det betyder ikke, at der ikke er noget nyt, der er værd at give opmærksomhed. Det betyder, at de virkelig nyttige nyheder er sjældnere nu og lettere at overse. Pagy var en af dem i 2018; pointen med en opdatering som denne er, at "sjælden og let at overse" også beskriver de ændringer i et godt bibliotek, når først fællesskabet er holdt op med at skrive om det.
2010 til 2026
Bekræftede tal: 2014 (~27.000) og 2018 (~10.600 / ~29 pr. dag), fra Infinums RubyGems-analyser.
Hvis du har arbejdet med Rails før, har du sikkert brugt will_paginate eller Kaminari til at paginere indekssider i en applikation. Paginering er arbejdet med at opdele en lang liste af poster i nummererede sider og rendere links imellem dem. Hvis du er ny i Rails, vil du ret hurtigt få brug for en løsning til paginering.
Pagy er et pagineringsbibliotek bygget med ydeevne som højeste prioritet, uden at det er besværligt at bruge i nye eller eksisterende applikationer. Det er framework-agnostisk: Det fungerer med Rails såvel som med lettere Ruby-frameworks som Sinatra og Padrino, og det paginerer både Active Record-relationer samt almindelige arrays og andre samlinger.
Det var rimeligt at være skeptisk over for endnu en paginerings-gem tilbage i 2018. Fragmentering er ikke godt for open source, og nogle af de forbedringer, Pagy bragte med sig, blev oprindeligt foreslået til Kaminari og afvist med den begrundelse, at de ville kræve væsentlige ændringer af selve kernen i det gem. Den afvisning er halvdelen af grunden til, at Pagy eksisterer. Otte år senere er skepsissen for det meste forsvundet: Antallet af downloads og stjerner placerer Pagy solidt blandt de mest populære valg frem for at være et nicheprodukt.
Tænk på hver gem på en request-sti som bagage. Du bærer rundt på den på hver tur, uanset om du åbner den eller ej, og paginering er den taske, der følger med på næsten hver eneste listeside i dit produkt. En tung taske er ikke et problem på én tur. Det er et problem, når du foretager ti tusinde af dem i timen.
Tallene herunder stammer fra den benchmark-suite, som er udgivet af forfatteren til Pagy, der renderer den samme Bootstrap-paginering over de samme records med hver gem efter tur.
Suiten registrerer to ting pr. gem: den hukommelse, der allokeres til at udføre arbejdet, og antallet af Ruby-objekter, der allokeres. Den måler ikke databaseforespørgslen bag siden, som er den samme, uanset hvilken gem du bruger. Den måler heller ikke slutbrugerens sideindlæsningstid, som domineres af forespørgslen, viewet og netværket. Og da den er udgivet af forfatteren til en af de tre gems, bør den læses som en direkte sammenligning af paginerings-overhead snarere end som et uafhængigt forsøg.
To ærlige bemærkninger til tallene herunder. For det første benchmarker den publicerede suite stadig legacy-versionerne (Pagy v3, will_paginate 3.1.7, Kaminari 1.1.1), så betragt dem som overheaden for hver gems klassiske implementering. For det andet hævder Pagys nuværende v43 yderligere forbedringer i forhold til sin egen v3-baseline, så hvis noget, er den moderne forskel større, ikke mindre. Tallene er overheaden og intet andet.
| Gem (testet version) | Allokerede objekter | Samlet allokeret hukommelse | Hukommelsesvækst, 2 til 20 sider |
|---|---|---|---|
| Pagy (v3.0.0) | 184 | ~19 KB | Flad (ingen målbar stigning) |
| will_paginate (3.1.7) | ~3.198 | ~341 KB | ~2x |
| Kaminari (1.1.1) | ~6.445 | ~704 KB | ~4x |
Objektallokering er der, hvor forskellen er mest markant. Kaminari opretter cirka 6.400 objekter for at rendere navigationen; will_paginate omkring 3.200. Begge er for tunge til opgaven. Pagy gør det med 184.
Vækstkolonnen betyder lige så meget som totalerne. Når du udvider pagineringslinjen fra 2 til 20 links, forbliver Pagys hukommelsesforbrug stort set uændret, mens will_paginate omtrent fordobles, og Kaminari omtrent firedobles. På en travl listeside med en bred pagineringslinje betales den pris ved hver eneste forespørgsel.
Der er også en historie om kodestørrelse bag historien om hukommelse. For at producere det identiske output er Pagy på omkring 4 filer og ~140 linjer; Kaminari spænder over ~36 filer, will_paginate ~23. Mindre kode til at udføre det samme arbejde betyder normalt færre allokeringer og mindre at forholde sig til, og her viser det sig direkte i profilen.
Samme navigation, samme poster. Lavere er bedre.
Ruby-objekter allokeret til at generere det samme pagineringsresultat. Kilde: Pagy-forfatterens benchmark-test (ddnexus/pagination-comparison), ældre gem-versioner. Pagy 43 hævder yderligere forbedringer i forhold til v3-udgangspunktet vist her.
Hvert allokeret objekt er hukommelse, der optages og derefter frigives af garbage collection (det baggrundsarbejde, Ruby udfører for at frigøre hukommelse, der ikke længere er i brug), og det hele sker på en sti, der kører på de fleste listesider i produktet. At reducere allokering pr. forespørgsel med en størrelsesorden gør ikke en sideindlæsning mærkbart hurtigere. Det øger antallet af samtidige forespørgsler, en given instans kan håndtere, før du skal tilføje en ny. På en infrastrukturregning, der er baseret på antallet af instanser, er det her, besparelsen ligger, og den migrering, der muliggør det, måles i timer frem for i sprints.
Vi ser dette mønster i vores eget leveringsarbejde. Da vi genopbyggede FlippedNormals, en markedsplads for computergrafik, kunne den gamle WordPress-platform ikke længere følge med trafikken, så vi migrerede den til en tilpasset Ruby on Rails-applikation og flyttede den til AWS for at få en mere skalerbar infrastruktur. Hele migreringen tog to måneder og løftede trafikken med 4 % efter relanceringen. Det er et nyttigt eksempel til denne diskussion netop på grund af dens form: et katalog med mere end 28.000 produkter betyder, at liste-, søge- og browsesider er på den kritiske sti, som rammes ved næsten hver eneste forespørgsel. Det er præcis de sider, hvor en paginerings-gems omkostning pr. forespørgsel ganges med din trafik, og hvor det valg, der blev truffet tidligt, i det stille fastsætter ressourceloftet senere.
Der er en stor sidegevinst ved alt dette: Koden er oprigtigt let at læse, hvilket er grunden til, at forfatteren har været i stand til at profilere den linje for linje.
Paginerings-gems er ét eksempel på et bredere mønster, og det er netop dette mønster, vi leder efter, når vi gennemgår en Rails-stack. Vi kalder det en gem resource audit. Tre trin.
1. Lav en oversigt over, hvad der kører ved hver forespørgsel. Ikke hele din Gemfile, kun de gems, der bliver kaldt på de sider, hvor du rent faktisk har trafik: index- og listesider, søgeresultater og dashboards. Paginering, serialisering, autorisations-tjek og view-helpers ender typisk på denne liste. En gem, der kun bruges én gang om dagen i et baggrundsjob, er ikke værd at måle på.
2. Mål allokering, ikke svartid. Antallet af objekt-allokeringer pr. forespørgsel er det tal, der bedst forudsiger, hvordan en applikation opfører sig under belastning, og det er stabilt nok til at sammenligne to forskellige implementeringer. memory_profiler og benchmark-ips giver dig både allokering og tidsforbrug for en enkelt handling. Forskellen mellem to gems til den samme handling er som regel tydelig allerede ved første kørsel.
3. Modeller resultatet i instanser, ikke megabytes. En besparelse bliver først et stærkt argument, når den udtrykkes som råderum: hvor mange flere samtidige forespørgsler en instans kan håndtere, og dermed hvor mange færre instanser den samme mængde trafik kræver. Det er det tal, en teknisk beslutningstager kan handle ud fra, og det, der retfærdiggør indsatsen ved en migrering.
De fleste Rails-applikationer slæber rundt på to eller tre af disse "tunge" gems. Paginering er den, vi oftest støder på, fordi den vælges tidligt, aldrig bliver revurderet, og ligger præcis på de sider, der får mest trafik.
Det er her, ældre guides vil lede dig på vildspor: Pagy::Backend / Pagy::Frontend includes, items: nøgleordet og pagy_nav hjælperen er alle fra før v43. Her er den aktuelle opsætning, som følger den officielle quick-start guide.
1. Tilføj gem'en, låst til minor-versionen (v43 bruger et spring i versionsnummeret, så en låsning til minor-versionen beskytter dig mod breaking changes):
# Gemfile
gem 'pagy', '~> 43.6'Pagy 43 kræver Ruby 3.3 eller nyere.
2. Inkludér det enkelte entry-point modul i din application controller:
# app/controllers/application_controller.rb
include Pagy::MethodDen ene include erstatter det gamle modul med to dele (Backend plus Frontend) opsætning. Metoder indlæses automatisk, når de bruges.
3. Paginer i controller-handlingen. Vælg en paginator: :offset er den klassiske metode, :keyset er den hurtigste til store, sorterede samlinger:
# Offset pagination (the familiar page-number behaviour)
@pagy, @records = pagy(:offset, Product.all, limit: 20)
# Keyset pagination (fastest for large, ordered datasets)
@pagy, @records = pagy(:keyset, Product.order(:id).all, limit: 20)Bemærk limit:, som erstattede det gamle items: nøgleord.
4. Gengiv navigationen i viewet. Hjælpefunktionerne er nu metoder på @pagy objektet:
<%# Server-side navigation bar %>
<%== @pagy.series_nav %>
<%# Or a client-side, responsive variant, styled for Bootstrap or Bulma %>
<%== @pagy.series_nav_js(:bootstrap) %>
<%== @pagy.input_nav_js(:bulma) %>
<%# A "showing 476-500 of 1000" style summary %>
<%== @pagy.info_tag %>Det var hele hurtigstarten. Hvis du vil tilpasse din app's tema interaktivt eller stille Pagys indbyggede AI et spørgsmål direkte fra din egen app, aktiverer en enkelt linje i dit layout head udviklerværktøjerne:
<%== Pagy.dev_tools %>Flytningen foregår i controlleren og viewet (filen, der håndterer forespørgslen, og skabelonen, der renderer siden). Den officielle migreringsvejledning opdeler det i nogle få mekaniske trin, og i praksis svarer det næsten én-til-én.
Controller. Gamle kald kan oversættes direkte:
# will_paginate
# @records = Product.some_scope.paginate(page: params[:page], per_page: 15)
# Kaminari
# @records = Product.some_scope.page(params[:page]).per(15)
# Pagy
@pagy, @records = pagy(:offset, Product.some_scope, limit: 15)View. Udskift hjælperen:
<%# will_paginate: <%= will_paginate @records %> %>
<%# Kaminari: <%= paginate @records %> %>
<%== @pagy.series_nav %>Global konfiguration. Søg i din kodebase efter den gamle gems klassenavn (WillPaginate, Kaminari) og fjern dens initialiseringsfiler og eventuel monkey-patching. En global per_page bliver til Pagy::OPTIONS[:limit] i en Pagy-initialiseringsfil, eller en limit:pr. kald. Hvis du har benyttet dig af Kaminaris .page scope inde i model-metoder, fjern den der og anvend paginatoren i controlleren i stedet.
Kør derefter dine tests, eller klik dig igennem siderne. Eventuel tilbageværende legacy-kode vil fejle, så fjern den og prøv igen, indtil appen renderes fejlfrit. Hvis du brugte Bootstrap- eller Bulma-pagineringsstile, fungerer den eksisterende CSS med Pagys series_nav hjælpere.
Pagy er ikke en direkte erstatning: Du kan ikke bare udskifte gemmen og lade din kode være urørt. Hjælpernavnene er anderledes, så hver pagineret visning skal gennemgås, og samlingen dekoreres ikke længere med sidemetoder på samme måde, som Kaminari dekorerer en Active Record-relation. Enhver kode, der kalder disse metoder, skal ændres. Pagys hjælpere returnerer rå HTML-strenge, hvilket er præcis det, der holder dem hurtige.
I en applikation med en håndfuld indekssider er dette klaret på en eftermiddag. Hvis du har halvtreds, bør du planlægge det som en afgrænset opgave og behandle den, som du ville enhver ændring af kode, der kører ved hver forespørgsel: bag en review-proces og med tjek af siderne. Hvis du hellere vil uddelegere det, er dette den type afgrænsede arbejde, som vores Ruby on Rails-udviklingstjenester dækker.
En ærlig anbefaling kræver også et "hvornår man ikke bør".
Bliv ved Kaminari, når applikationen allerede er bygget tæt op omkring det (du benytter dig af de genererede visningsskabeloner og konfiguration pr. model), eller når de paginerede sider har så lav trafik, at forskellen i hukommelsesforbrug aldrig vil kunne ses på din regning. Kaminari er modent, veldokumenteret og stadig det mest installerede af de tre. Argumentet for Pagy er et ressource- argument, og det er stærkest, hvor paginering er en kritisk del af systemet.
will_paginate er sværere at anbefale til nye projekter, da vedligeholderen har lukket for nye funktioner. Men hvis du har en stabil app, der allerede bruger det, og du ikke oplever problemer med ydeevnen, så er "i vedligeholdelsestilstand" ikke det samme som "ødelagt". Der er ingen grund til at skifte bare for skiftets skyld.
Ressourceargumentet for Pagy er reelt, men det er et argument, ikke et bud. Tilpas det til din trafik.
Jeg startede med will_paginate, fordi det var det bedste værktøj på det tidspunkt. Jeg skiftede til Kaminari et par måneder efter lanceringen, og blev der, indtil et par uger efter Pagy dukkede op.
Det, der overraskede mig senere, var, at jeg var skiftet fra will_paginate til Kaminari uden overhovedet at tage højde for ydeevnen, og ifølge den offentliggjorte benchmark er Kaminari den tungeste af de to. Ingen benchmark fra min side. Bare rutine.
Det rejser et relevant spørgsmål om resten af stacken: Hvor mange gems slæber vi rundt på, som i det stille skader de applikationer, vi designer og leverer, fordi de er valgt på samme måde og aldrig er blevet målt siden? Det er spørgsmålet bag ovenstående audit, og i vores eget leveringsarbejde er det nu et fast punkt frem for en eftertanke, for omkostningen ved at stille spørgsmålet for sent er en infrastrukturregning, som ingen kan forklare. Logikken rækker langt ud over paginering og langt ud over Rails. Det framework, du vælger, sætter bunden. De biblioteker, du bygger ovenpå, sætter loftet.
Som en ansvarsfraskrivelse: Ydeevnetallene ovenfor stammer fra den benchmark-suite, som Pagys forfatter har offentliggjort (ddnexus/pagination-comparison), kørt på legacy-versionerne af hver gem.
Ja, i stor stil. Det er gået fra at være en nykommer til at være et af de foretrukne valg til paginering i Rails, med titusindvis af downloads, tusindvis af stjerner på GitHub og regelmæssige udgivelser gennem 2026. Den aktive udvikling på dette område sker hos Pagy; Kaminari har den største installerede base, men har ikke udgivet en opdatering siden 2021.
Ifølge forfatterens benchmark, ja, og med en stor margin hvad angår ressourceforbrug: Pagy allokerer omkring 184 objekter mod Kaminaris ca. 6.400, og bruger markant mindre hukommelse i alt. Den synlige forskel for brugeren ved en enkelt sideindlæsning er lille. Forskellen viser sig i, hvor mange samtidige forespørgsler en instans kan håndtere.
Tilføj gem 'pagy', '~> 43.6', tilføj include Pagy::Method til din ApplicationController, erstat hvert .paginate(...) / .page(...).per(...) kald med @pagy, @records = pagy(:offset, scope, limit: N), og erstat view-helperen med <%== @pagy.series_nav %>Det er ikke en direkte udskiftning, og hver pagineret visning skal besøges, men ændringen er afgrænset og let at rulle tilbage.
Ja. Beregningerne kører på heltal frem for Ruby-objekter, så omkostningerne vokser ikke med samlingens størrelse. For meget store, sorterede tabeller kan Pagys :keyset paginator helt undgå den dyreOFFSET -scanning. Den begrænsning, du til sidst rammer, er database-tællingen, ikke paginerings-gem'en, og det gælder for alle tre.
Pagy er framework-agnostisk. Det fungerer med Rails og lettere frameworks som Sinatra og Padrino, og det paginerer Active Record-relationer såvel som almindelige arrays og andre samlinger. En ORM (object-relational mapper) er det lag, der omdanner databaserækker til objekter; Active Record er den, der er indbygget i Rails, og Pagy arbejder sammen med den frem for at koble sig direkte på dine modeller.
Det var et komplet redesign. Opsætningen blev reduceret til en enkelt include Pagy::Method, konfigurationen blev skåret drastisk ned, og hjælpefunktionerne blev flyttet over på @pagy -objektet (@pagy.series_nav i stedet for den gamle pagy_nav(@pagy)). Det tilføjede også nye paginatorer (countish, keyset, keynav, calendar og search), rendering på klientsiden og interaktive udviklerværktøjer. Hvis du følger en ældre vejledning, skal du forvente, at metodenavnene er anderledes.
Hos Imaginary Cloud arbejder vi med en bred teknologistak, herunder Ruby on Rails. Hvis paginering er en kritisk del af din applikation, eller hvis du har mistanke om, at andre gems koster dig mere, end de burde, kan vi foretage den ovenfor beskrevne gem-ressourceaudit af din stak og fortælle dig, hvor ressourceforbruget reelt ligger. Kontakt os så kigger vi på det sammen.


CEO hos Imaginary Cloud og medforfatter til bogen Product Design Process. Jeg nyder mad, vin og Krav Maga (ikke nødvendigvis i den rækkefølge).
LinkedIn
People who read this post, also found these interesting: