RailsConf 2019

JRuby on Rails: From Zero to Scale

JRuby on Rails: From Zero to Scale

by Charles Oliver Nutter and Thomas E Enebo

The video presentation titled "JRuby on Rails: From Zero to Scale" by Charles Oliver Nutter and Thomas E. Enebo, delivered at RailsConf 2019, explores the benefits and functionalities of JRuby, particularly its application within Ruby on Rails projects. The speakers emphasize the importance of JRuby for companies seeking to improve the performance and scalability of their Ruby applications. They outline essential aspects of implementing and deploying JRuby, as well as tackling common challenges encountered during its integration. Key points discussed include:

  • Introduction to JRuby: JRuby is a Ruby implementation that runs on the Java platform, leveraging the performance and tools of the JVM. It supports pure Ruby code and offers access to Java libraries.
  • Benefits of JRuby: The presenters highlight the advantages of using JRuby such as better CPU utilization due to the lack of a Global Interpreter Lock (GIL), allowing for native threads and improved concurrency.
  • Getting Started: They guide viewers on how to install JRuby, including prerequisites like having a Java Development Kit (JDK) and the steps to set up JRuby through various installation methods.
  • Performance Considerations: Start-up times can be slower compared to C Ruby due to the necessity of JVM optimizations. The speakers discuss methods to mitigate this delay, including using flags and features like Class Data Sharing.
  • Migration to JRuby: There are minor changes required when migrating applications from C Ruby to JRuby, but these transitions can be supported through community resources and tools.
  • Scalability: JRuby applications can be scaled more efficiently due to their single process, multithreading capabilities, which can lead to significant cost reductions as demonstrated by a case study involving an application that optimized its server usage dramatically.
  • Future of JRuby: The presenters discuss ongoing developments in JRuby, including support for upcoming Ruby versions and optimizations for performance, particularly related to startup time and memory usage.

In conclusion, the speakers encourage developers to consider JRuby not just for new applications but also as a powerful option for scaling existing Ruby on Rails applications, reiterating that as applications grow, the efficiencies gained through JRuby become increasingly beneficial. They invite the audience to engage, experiment with JRuby, and participate in community efforts around improving its capabilities.

00:00:20.390 Today, we're going to talk to you about JRuby and why it might be of interest to you.
00:00:26.759 Before we get started, how many of you are running JRuby for something today? Cool, about a third of the room.
00:00:32.759 We're going to show you basically how to get up and going with JRuby, highlight some of the reasons it may be interesting to consider deploying Rails or other applications using JRuby, and hopefully encourage you to try some things out.
00:00:38.430 I am Charles, and this is Tom. We are the JRuby guys. For about 12 or 13 years, we have been working on this, trying to make JRuby run well and to do Ruby on top of the Java platform.
00:00:51.150 Before we get going, I'd like to mention one of our former contributors, Ola Bini. If you've heard of him, he was one of the core contributors back in 2006-2007 who greatly helped us get Rails going. He wrote a lot of the core libraries we still use today. Unfortunately, due to his connections with Julian Assange and the fact that he was living in Ecuador, he has been arrested illegally and is being held without trial for 90 days. It's a horrible situation. If you want to follow his story, check out the 'Free Ola Bini' account on Twitter. We're trying to raise some funds to help with his legal defense so hopefully we can get him out of the Ecuadorian prison.
00:01:12.210 Tom, take over.
00:01:19.050 Alright, I have a large number of slides to get through—24 minutes worth of slides, and this is going to happen in 20 minutes. So, what is JRuby? It's a Ruby implementation that allows you to run any pure Ruby code, and it should just work. If it doesn't, please file a bug report unless it involves something like fork. We don't support native C extensions, and we will discuss this further in the talk.
00:01:55.470 JRuby is built on top of the Java platform, which means you inherit both its benefits and some limitations. There are countless tools available for the JVM. For example, here are some screenshots from Visual VM, which allows you to see garbage collection in action, giving you a live view of your application's health. There are many tools like this that are free and built into the JVM. Importantly, JRuby does not have a global interpreter lock, meaning that our native threads can easily saturate and utilize all CPU cores effectively—they can operate concurrently.
00:02:22.170 The JVM has a library for almost everything in the world. So, if you ever find yourself in a situation where a Ruby gem doesn't provide a particular functionality, there's likely a Java library you can rely on. Currently, JRuby supports Ruby 2.5.3, and this is the only branch we are actively maintaining. We had a JRuby 9.1 release for Ruby 2.3, but that is now end-of-life.
00:02:48.780 We also have a Ruby 2.6 branch, but we are contemplating whether to skip ahead to Ruby 2.7. We did this once before with Ruby 2.4, and we received no complaints. The rationale here is that we want to spend more time improving JRuby rather than maintaining multiple releases. Bootstrapping a new Ruby compatibility level is additional work that may not be necessary.
00:03:19.590 Are there any of you currently using Ruby 2.6 or eager to move to it? Let’s just see how many hands go up. If you're interested in tracking our progress, we'll share a URL at the end of the talk that shows ongoing updates regarding our Ruby 2.6 features.
00:04:02.250 So, how do you get started with JRuby? It looks like there is a good group here that hasn't used it before. The process isn't very complicated. You can go to the JRuby website, where you will find tarballs, zips for Windows installations, or you can unpack it into your path, and you're ready to go with JRuby. You basically need a JDK—Java Development Kit. We still recommend using Java 8. Java 9 and higher have various security restrictions that may generate warnings, but they work well. We are mainly testing on Java 8.
00:04:44.840 To install JRuby, you can use one of these methods: tarball, your regular Ruby installer, or an official Docker image. That's really all there is to getting JRuby up and running. The only prerequisite is having the JVM available, and then the usual installation methods cater to that.
00:05:14.680 We are now using IRB with JRuby. As you can see, we have a bit of code that's actually calling some core JVM classes, just like you would call any Ruby code. Everything available at the JVM level is also accessible from Ruby, giving you a nice way to leverage those libraries.
00:05:37.510 That said, one of the main issues you may encounter when using JRuby is the startup time; it is not quite what we'd like it to be. This creates a challenge in performance—fundamentally, if you look at CRuby, everything is compiled natively, so it boots up much faster. On the other hand, JRuby's parser, compiler, and internal interpreter are all written in Java and compiled into JVM bytecode.
00:06:05.400 The JVM has to warm up and decide what is 'hot' to optimize, meaning that everything in JRuby starts cold after parsing, which causes the initial delay where nothing is running at peak speed. For example, if you run 'gem list' under C Ruby, you can see much faster speeds compared to a normal start of JRuby without any tweaks, which can be four to six times slower.
00:06:51.760 However, the good news is that once the JVM warms up, it optimizes the performance significantly, as demonstrated in benchmarks where JRuby becomes faster following the initial delay. Right now, we're exploring several strategies to reduce this startup time.
00:07:28.900 The first method is the '--dev flag', which disables our internal JIT compilation. This adjusts the JVM optimization and can cut the startup time down by a good 30 to 40 percent for major commands. You should keep in mind not to report terrible performance on a benchmark if this flag is enabled.
00:08:14.420 The second method involves Java's Class Data Sharing feature found in OpenJDK from version 9 and higher. This allows us to share our JRuby class information across JVM runs, improving startup time. To utilize this, there’s a JRuby startup gem you can install that comes with options to generate Application Class Data Sharing.
00:08:55.410 Additionally, IBM has a JVM feature called OpenJ9 which incorporates a quick-start mechanism. By passing two flags, it not only shares our core class information but also the compiled code across runs, taking us closer to optimized warmup performance.
00:09:35.370 Furthermore, there are ongoing developments to improve ahead-of-time compilation of JVM applications, including the Growl VM project, which aims to compile JRuby's core and run it natively from the outset while optimizing Ruby code dynamically.
00:10:12.040 When making this transition, ensure that you don't mix gem paths; different Ruby installers have different platform gems, which can lead to confusion when analyzing gem paths.
00:10:44.480 Dot ruby version files are also something to keep in mind. If you are dealing with an existing Ruby application that’s compatible with Ruby 2.5.3, it may not switch to JRuby without issue. To avoid such situations, mindfulness is important when switching back and forth.
00:11:10.650 Finally, if you need help, we have our GitHub project, where we are monitoring three different chat rooms. Our IRC channel, Gitter, and we're exploring JRuby on the Matrix open-source chat forum.
00:11:29.440 Now, let's shift our focus to JRuby on Rails. We've been working on this for a long time—13 years since the early versions of Rails 1.x. Just last week in Fukuoka, Japan, we had discussions with attendees about the utilization of JRuby in production. I can confidently say there are numerous companies successfully running JRuby in production with large multi-node applications, often leveraging JRuby as a solution for scaling when CRuby falls short.
00:12:45.240 JRuby can act as the last mile for those scaling issues they encounter when trying to get CRuby to function at larger scales without significant costs.
00:13:04.230 The differences at the application level when using JRuby are typically minimal. A plain generated application will have a few different gems along with various database drivers and back-ends. Unlike CRuby, the JVM database libraries do not support UNIX sockets, which means you will need to configure them to use local ports instead.
00:13:35.760 When scaling JRuby applications, it is not concerning how many processes or workers you will throw at it, but rather the number of threads. You can effectively utilize as many threads as needed, allowing you to saturate all available cores of your system.
00:14:08.490 For the migration process, the JRuby app generator creates the correct gem files—JRuby-specific versions are generated which work out of the box. Rails does support JRuby, so existing applications can be migrated with minor modifications.
00:14:39.310 For Rails 5.3, the JRuby support has been satisfactory, with only some minor issues in action cable. Rails 6 also appears promising; we have been running it with minimal issues, so things are looking good!
00:15:15.200 We are actively working to resolve any remaining issues in the Rail ties, including some weird floating-point discrepancies. Our team plans to submit pull requests to address these test discrepancies.
00:15:39.230 When looking at the Active Record support for Rails 5, we've established a dedicated project called Active Record JDBC. It handles certain tests, particularly for PostgreSQL, where differences in date representations due to their underlying systems may yield discrepancies.
00:16:05.380 As we continue to enhance JRuby support for Rails 6, we recognize that a few tests still present conflicts that we would like to address effectively. Action cable faces additional challenges due to its reliance on Postgres specific APIs. We already simulate certain functionalities of PG through Active Record JDBC, and we anticipate that these are relatively straightforward to resolve.
00:16:48.170 The migration process can seem daunting. I personally am working to convert the Discourse application to JRuby. Despite being a substantial application with over 520 gems and a quarter of a million lines of code, I have thus far only faced five unsupported gems.
00:17:18.740 Importantly, the application now runs, although it does display complications, likely related to extensive JavaScript files that require adjustments. This can serve as a practical reference for tackling your own applications.
00:17:43.520 To facilitate the transition, I suggest using the JRuby lint gem. It will analyze your directory and provide a report with helpful suggestions, particularly regarding unsupported gems. Furthermore, it offers static analysis that aids in identifying potential threading issues.
00:18:20.840 In addition, it highlights unsupported functionalities like fork, which should be avoided. You will need to devise a strategy when encountering unsupported gems; for instance, some gems might not matter to you, allowing you to remove them. Alternatively, you could change your implementation to purely Ruby, or opt for the Foreign Function Interface for binding shared libraries.
00:19:03.810 Another exciting feature is the ability to interact with Java classes and objects in a Ruby-like way. This interaction allows for smoother operations within your JRuby environment, thereby enhancing the JRuby experience. When working on porting gems, such as the mini_racer gem, you can find existing Java libraries that cater to your requirements.
00:19:43.800 The last strategy revolves around using the Java Native Extension API, which parallels C extensions used in CRuby yet utilizes Java. This approach demands more effort, but it often results in optimal performance. For example, I've been casually extracting functionality from the oj gem for JSON parsing to JRuby.
00:20:24.240 Developers can utilize annotations to streamline this process and easily bind Ruby methods. The benefit of porting common gems means that once they're available for JRuby, the entire community can leverage that technology.
00:21:01.610 After migrating and establishing JRuby with Rails, most standard tools remain operational. We endorse using Puma as your server, which functions seamlessly like the default Rails server. Deployment options include running as a single archive or deploying into a Java application server without losing any of the Rails experience.
00:21:54.110 It's crucial to note that the JVM expects to be the sole application on a server and could consume maximum memory unless told otherwise. I will address some benchmarks on how JRuby scales Rails applications versus CRuby. This has been a common challenge faced by many companies, which often turn to external solutions.
00:22:36.710 The primary issue arises from the lack of concurrent threads in CRuby, limiting the application’s performance capabilities, especially when faced with CPU-bound tasks. This makes scaling CRuby applications suboptimal as each new Ruby process adds overhead, recycling memory across multiple runtimes. In contrast, JRuby facilitates a single process equipped with threads that can make efficient use of your application's resources.
00:23:37.080 For instance, one success story involved a very large Rails application that initially required 40 extra-large EC2 instances to maintain performance, achieving 100,000 to 150,000 requests per minute. Following a migration to JRuby, alongside system adjustments, they reduced their instances to only 10 while also experiencing improved performance.
00:23:55.000 As JRuby starts to optimize further, it has become a solution for performance issues previously faced in CRuby applications. This optimization leads to substantial operational cost savings and alleviates the burdens associated with managing numerous Ruby processes.
00:24:46.080 You may wonder how JRuby performs in terms of Ruby on Rails. We have been consistently monitoring benchmarks of Rails applications and observed that JRuby has recently begun to outperform CRuby under certain conditions. The simple blog applications we tested showcased JRuby scaling remarkably well even in a single-threaded scenario.
00:25:49.440 We must ensure warmup times do not hinder performance. Our benches typically allow enough warmup time for applications to stabilize before observing results. When initially tested on my personal laptop, the JRuby application still exhibited variable performance metrics.
00:26:52.720 This prompted a desire for a realistic testing environment to understand how JRuby performs under payloads similar to production scenarios. Transitioning to rubygems.org for testing proved to be a fruitful approach,
00:27:36.360 even with the initial challenges I experienced with benchmark tools like 'wrk'. Eventually, after addressing inconsistencies with keep-alives in Puma, we began to gather exploitable data.
00:28:26.640 The goal was to saturate the CPU on my i7 machine with a benchmark duration of around 35 minutes. This allowed us to observe that, in comparison to CRuby, JRuby maintained an impressive growth trajectory in performance and stability. One important takeaway was that JRuby’s memory utilization proved superior to CRuby in these settings.
00:29:13.360 Tracking memory allocation during my runs showed that JRuby stabled out with less memory in comparison to CRuby in similar scenarios, showcasing the efficiency of threading over sub-processes.
00:29:46.360 Despite the JVM's overhead, JRuby consistently delivers low memory yield while efficiently handling processes. As the journey continues, we see opportunities for JRuby to further excel in speed and performance while maintaining low overhead, especially in large applications.
00:30:30.960 To summarize, while JRuby launches with slower initial performance out of the box, enhanced JVM features, concurrent multi-threading capabilities, and various support tools build an attractive framework for JRuby.
00:31:35.950 For future JRuby enhancements, we are optimistic about transitioning to support Ruby 2.7 and configuring the use of native compiling technologies to improve startup times dramatically.
00:32:15.370 Many thanks for your engagement today. We hope to help you get started on your JRuby journey.