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.