Improving Ruby on Rails-Based Web Application Performance
Abstract
:1. Introduction
2. Web Application Request Handling
- Explicit requests are directly initiated by the user. For example, those requests occur after the user has clicked the link or entered the Uniform Resource Locator (URL) into their browser’s address bar. Commonly associated with the GET HTTP request method.
- Implicit requests occur in the background, oftentimes asynchronously, without the direct knowledge of a user and are indirect result of user actions. For example, different tracking scripts can initiate requests of this type. Commonly associated with the POST HTTP request method.
- The HTML elements are being parsed into the DOM (Document Object Model) Tree;
- In a similar process, the CSSOM Tree arises from the CSS (Cascading Style Sheets) rules specified in the style sheets included in the document;
- Both the CSSOM and DOM Trees are then combined into the Render, or Style Tree, on the basis of which the computed styles, applied to each visible page element, are established;
- Render Tree information is then used to establish the position, height and width of each element, called the Layout;
- The final step, called Painting, involves painting the individual nodes as pixels on the screen.
3. Ruby on Rails Performance Considerations
3.1. Caching
3.2. Database Access
3.3. RoR Modules
4. Software Implementation
4.1. Rendering Strategy: Transition towards Client-Side Rendering
- The elements of View layer—Templates and Layouts need to be remade to take the input data either in the form of Javascript Objects or JSON notation and output HTML elements. In other words, the templating mechanism needs to be reimplemented in the JavaScript-compliant technology.
- Controllers need to be functionally divided into two categories: API Controllers, the primary role of which is to respond to requests from the frontend with the appropriate serialized data, and Pages, or Entry-Points Controllers, the role of which is to serve usually pre-determined and static at the point of server-side generation, resources, usually HTML documents with the included frontend scripts that are responsible for the frontend logic. For SPA, only a single such page needs to be provided at the first load of the application. The latter category of controllers is largely optional, as another parallel gateway application can be used instead [15].
- Routing and the state-management needs to be re-implemented in the Javascript frontend.
4.2. Backend Optimization
4.2.1. Resolving the N + 1 Query Problem and Preloading Optimizations
- Joins()—Executes a single database query that uses INNER JOIN;
- Includes()—Loads data from the associated table by either executing a separate query or using the single LEFT OUTER JOIN query, depending on whether the WHERE condition was given for the associated table;
- Preload()—Similar to Includes(), but always executes a separate query;
- Eager_load()—Similar to Includes(), but always executes a single LEFT OUTER JOIN query.
4.2.2. Using the Rails API-Only Subset
4.2.3. Database Access Improvements
- Baseline: When a comment or a post is upvoted or downvoted by a user, the number of its upvotes is updated in the database. This, however, does not affect the rating of a user who published the content. The aggregate query is executed each time to retrieve the rating.
- Caching: The number of upvotes is stored in the application cache, when the upvote is registered, the rating cache of the author-user becomes invalidated.
- Database storage: The user rating is stored in the database and is retrieved each time; upvotes will result in updating.
- Database storage and caching: Combination of two above approaches; the database query is only executed if the cache is not valid.
4.2.4. Introducing Caching to Serializers
5. Performance Measurements
- Time (VW)—Time spent in the View Layer of the application, directly obtained from the Rails internal monitoring for monolith applications and calculated as the difference between the moment the page was initially received and the moment when the page was fully rendered in Client-Side Rendering applications (marked with **);
- Time (DB)—Time spent in the Model Layer of the application, taken from the Rails monitoring;
- Time (DOM)—Time from the first load of the page until the page was fully rendered and interactive in the browser, measured with the use of Google Chrome’s profiling tool.
5.1. Results Analysis
- First was Resolving the N + 1 Query problem and preloading optimizations (marked as N + 1&Pre). After this step, due to more optimal resource loading and management, both Database Time and View Time decreased as predicted, and the page loading time decreased as well. This effect was most pronounced for the Feed (100) endpoint due to the large amount of data retrieved.
- Second was switching to the Client-Side Rendering strategy. This resulted in an overall decrease of Feed endpoint page loading time; however, due to another metric used for View Time, there appears to be an increase in the amount of time spent in the View layer, but it is not conclusive.
- Another step was using the API-only version of the Rails framework. This resulted in an overall increased efficiency of both communication with the database and serialization process, included in the Time (VW), which resulted in somewhat better page loading times.
- The fourth step was optimizing database accesses. This resulted in the most drastic increase in the database communication efficiency. Because of the improved performance of the object-level serializers, the View Time appears to have decreased sharply as well. A very data intensive Feed (100) endpoint has improved the most.
- The fifth step was introduction of the Object-level serializer caching. The Database Access time plummeted across all the endpoints due to only initial database access being necessary in order to produce a serialized object. Predictably, the most data-heavy endpoint has benefited the most, in part due to high degree of object interrelations. The least data intensive endpoint has benefited the least, due to the smaller time difference between retrieving the serialized object from the cache and building the same object based on information accessed from the database. In other words, the gain from serializer caching was highly correlated to the degree of object interconnectedness as well as the regular serializer process overhead.
5.2. Comparison with the Baseline Application
6. Conclusions
Author Contributions
Funding
Institutional Review Board Statement
Informed Consent Statement
Data Availability Statement
Conflicts of Interest
References
- Why Does Speed Matter? Web-Dev. Available online: https://web.dev/why-speed-matters/ (accessed on 21 June 2021).
- Ruby on Rails Official Page. Available online: https://rubyonrails.org/ (accessed on 21 June 2021).
- Most Popular Backend Frameworks-2012/2021. Available online: https://statisticsanddata.org/data/most-popular-backend-frameworks-2012-2021/ (accessed on 21 June 2021).
- Crawford, T.; Hussain, T. A comparison of server side scripting technologies. In Proceedings of the International Conference on Software Engineering Research and Practice (SERP 2017), Las Vegas, NV, USA, 17–20 July 2017; pp. 69–76. [Google Scholar]
- Populating the Page: How Browsers Work-Web Performance MDN. Available online: https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work (accessed on 22 June 2021).
- Caching with Rails: An Overview. Available online: https://guides.rubyonrails.org/caching_with_rails.html (accessed on 19 June 2021).
- Yang, J.; Yan, C.; Wan, C.; Lu, S.; Cheung, A. View-Centric Performance Optimization for Database-Backed Web Applications. In Proceedings of the 2019 IEEE/ACM 41st International Conference on Software Engineering (ICSE), Montreal, QC, Canada, 25–31 May 2019; pp. 994–1004. [Google Scholar] [CrossRef]
- Persson, K. Optimizing Ruby on Rails for Performance and Scalability. Degree Project in Computer Science. Master’s Thesis, KTH Royal Institute of Technology, Stockholm, Sweden, 2016. [Google Scholar]
- Optimizing Database Performance in Rails, Heroku. Available online: https://blog.heroku.com/rails-database-optimization (accessed on 21 June 2021).
- Nordén, M. To What Extent Does Ruby on Rails Effect Performance in Applications. Degree project in Computer Science. Master’s Thesis, Linköping University, Linköping, Sweden, 2015. [Google Scholar]
- Ruby on Rails API Gem, GitHub. Available online: https://github.com/rails-api/rails-api (accessed on 25 June 2021).
- Using Rails for API-only Applications, Rails Guides. Available online: https://guides.rubyonrails.org/api_app.html (accessed on 25 June 2021).
- Mukhamadiev, A. Transitioning from Server-Side to Client-Side Rendering of the Web-Based User Interface: A Performance Perspective. Degree Project in Media Engineering. Bachelor’s Thesis, Metropolia University of Applied Sciences, Helsinki, Finland, 2018. [Google Scholar]
- Risto, O. A Performance Comparison of Rendering Strategies in Open-Source Web Frontend Frameworks. Degree project in Computer Science. Master’s Thesis, University of Helsinki, Helsinki, Finland, 2021. [Google Scholar]
- Shklar, L.; Rosen, R. Web Application Architecture: Principles, Protocols and Practices, 2nd ed.; Wiley Publishing: Hoboken, NJ, USA, 2009. [Google Scholar]
- Marrs, T. JSON at Work: Practical Data Integration for the Web, 1st ed.; O’Reilly Media: Newton, MA, USA, 2017. [Google Scholar]
- Fast JSONAPI Ruby on Rails Serializer Gem, GitHub. Available online: https://github.com/jsonapi-serializer/jsonapi-serializer (accessed on 26 June 2021).
- Slim—A Fast, Lightweight Template Engine for Ruby, Slim Lang. Available online: http://slim-lang.com/ (accessed on 24 June 2021).
- Rails API Reference, Rails API Dock. Available online: https://apidock.com/rails/ActiveRecord/QueryMethods/ (accessed on 27 June 2021).
- Rails Performance Gem, GitHub. Available online: https://github.com/igorkasyanchuk/rails_performance (accessed on 27 July 2021).
- Timeline Event Reference, Google Developers. Available online: https://developer.chrome.com/docs/devtools/evaluate-performance (accessed on 27 June 2021).
Endpoint | Database Load | Page Element Count | Description |
---|---|---|---|
/feed | Very High. Every table in the database schema is involved. High number of resource-intensive join queries are executed. | High. For each of N posts, about 250 HTML elements are present. Total element number varies depending on the number of posts on a page. | Displays the timeline of posts either authored by the current user or by other users that are followed by the current user. For each post, comments are shown as well. The display of every post or comment also includes the information about the author or respective content as well as number of upvotes and display of number of reactions of different types. Information about the author of the content includes number of following and followed users, number of posts as well as rating, defined as sum of upvotes of every post authored by a user. |
/post/:id | Medium. Multiple tables in the database schema are involved. A few resource-intensive join queries are executed. | Low. Total page HTML element count is about 300. | Displays a single post along with relevant information, as described in the feed endpoint. |
/user/:id/followers | Low. A few tables in the database schema are involved. Single resource-intensive join query is executed. | Moderate. For every user, about 50 HTML elements are present. Total element number less than 2000. | Displays the table that contains all of the followers of the current user. The information of each follower is similar to that described in the feed endpoint. |
Preloading Function | View Time (ms) | Database Time (ms) | Total Time (ms) | # of sql Queries Executed |
---|---|---|---|---|
(1) Baseline (no preloading) | 2410.5 | 863.1 | 3273.6 | 1423 |
(2) incudes (:comments, comments: :user) | 2016.0 | 752.3 | 2768.3 | 1189 |
(3) eager_load (:comments, comments: :user) | 2235.2 | 825.1 | 3060.3 | 1197 |
(4) includes (:reactions, :comments, comments: :user, comments: :reactions) | 2495.0 | 855.5 | 3350.5 | 1317 |
(5) joins (:reactions, :comments, comments: :user, comments: :reactions) | 4094.1 | 185.7 | 4279.8 | 167 |
Storing and Retrieval Strategy | “Fetch + Get” | “Update + Fetch + Get” |
---|---|---|
(1) Baseline | 1388 ms | 5477 ms |
(2) Cache | 479 ms | 5457 ms |
(3) Database | 355 ms | 6107 ms |
(4) Database + Cache | 460 ms | 6361 ms |
Optimization Step | Endpoint Response Times | ||||||||
---|---|---|---|---|---|---|---|---|---|
Feed (100) | Feed (10) | Followers (30) | |||||||
Time (VW)/Δ% | Time (DB)/Δ% | Time (DOM)/Δ% | Time (VW)/Δ% | Time (DB)/Δ% | Time (DOM)/Δ% | Time (VW)/Δ% | Time (DB)/Δ% | Time (DOM)/Δ% | |
Baseline | 2326 | 844.8 | 4176 | 435.16 | 211.62 | 1240 | 176.42 | 107.34 | 789.6 |
Δ% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% |
N + 1&Pre | 2162.7 | 801.14 | 3910 | 428.68 | 208.3 | 1202 | N/A * | N/A * | N/A * |
Δ% | 92.97% | 94.83% | 93.63% | 98.51% | 98.43% | 96.93% | N/A * | N/A * | N/A * |
Client Side ** | 2767.2 | 790 | 2988 | 918.2 | 216.74 | 1126 | 604.6 | 123.66 | 817.4 |
Δ% | 127.94% | 98.60% | 76.41% | 214.19% | 104.05% | 93.67% | 342.70% | 115.20% | 103.52% |
API Only ** | 2632 | 771.42 | 2804 | 787 | 216.3 | 933.8 | 551.8 | 119.8 | 705.6 |
Δ% | 95.11% | 97.64% | 93.84% | 85.71% | 99.79% | 82.93% | 91.26% | 96.87% | 86.32% |
DB opt ** | 1557.2 | 270.34 | 1756 | 545.4 | 87.28 | 724.4 | 380.2 | 45.76 | 592.4 |
Δ% | 59.16% | 35.04% | 62.62% | 69.30% | 40.35% | 77.57% | 68.90% | 38.19% | 83.95% |
Srl. Cache ** (Cache H.R = 100) | 571.2 | 41.28 | 822.4 | 389.6 | 23.02 | 651 | 339.6 | 5 | 556 |
Δ% | 36.68% | 15.26% | 46.83% | 71.43% | 26.37% | 89.86% | 89.32% | 10.92% | 93.85% |
Publisher’s Note: MDPI stays neutral with regard to jurisdictional claims in published maps and institutional affiliations. |
© 2021 by the authors. Licensee MDPI, Basel, Switzerland. This article is an open access article distributed under the terms and conditions of the Creative Commons Attribution (CC BY) license (https://creativecommons.org/licenses/by/4.0/).
Share and Cite
Klochkov, D.; Mulawka, J. Improving Ruby on Rails-Based Web Application Performance. Information 2021, 12, 319. https://doi.org/10.3390/info12080319
Klochkov D, Mulawka J. Improving Ruby on Rails-Based Web Application Performance. Information. 2021; 12(8):319. https://doi.org/10.3390/info12080319
Chicago/Turabian StyleKlochkov, Denys, and Jan Mulawka. 2021. "Improving Ruby on Rails-Based Web Application Performance" Information 12, no. 8: 319. https://doi.org/10.3390/info12080319
APA StyleKlochkov, D., & Mulawka, J. (2021). Improving Ruby on Rails-Based Web Application Performance. Information, 12(8), 319. https://doi.org/10.3390/info12080319