Debugging

Summarized using AI

Ruby & JVM: A (JRuby) Love Story

Yarden Laifenfeld • October 20, 2022 • Helsinki, Finland

The video titled "Ruby & JVM: A (JRuby) Love Story" features Yarden Laifenfeld, a software engineer at Rookout, discussing the benefits and considerations of using JRuby, a Java implementation of the Ruby programming language, in software projects. Laifenfeld reflects on his journey with JRuby, highlighting its historical context and advantages such as performance improvements, seamless integration with Java libraries, and access to JVM features like threading. The discussion covers significant points including:

  • JRuby Overview: JRuby was created in 2001 as a solution to Ruby's performance issues, succeeding in running Ruby on the Java Virtual Machine (JVM).
  • Advantages of JRuby:
    • Access to Java libraries and code compatibility.
    • Enhanced thread management compared to standard Ruby.
    • Beneficial for long-running applications, with JVM optimizations.
  • Challenges with JRuby: Factors to weigh include the smaller community compared to CRuby, which affects support and feature availability.
  • Personal Experience: Laifenfeld recounts the challenges he faced while implementing JRuby in a debugger, including installation issues and the necessity of switching approaches to accommodate JVM intricacies.
  • Debugging JRuby: He shares insights from his debugging experience, including running an app, setting breakpoints, and data collection challenges, illustrating how JRuby works differently from standard Ruby concerning stack traces and variable handling.

In conclusion, Laifenfeld emphasizes that while JRuby has its strengths, the decision to use it should be based on project needs, performance requirements, and developer familiarity. His learnings highlight the innovation behind JRuby and the commitment of its developers, promoting a well-informed decision on integrating JRuby into projects.

Ruby & JVM: A (JRuby) Love Story
Yarden Laifenfeld • October 20, 2022 • Helsinki, Finland

In this talk I will dive into why you should - or shouldn’t - use JRuby in your upcoming projects. We’ll compare the language to Ruby and other JVM languages, discuss its limitations and restrictions, and even dive into how such a strange concoction of languages came to be.

To watch with closed captions, view the livestream recording: https://www.youtube.com/watch?v=m2qelRkp1CY&t=16350s

EuRuKo 2022

00:00:04.860 Foreign.
00:00:11.059 Thank you so much for having me. I'm really excited to be here.
00:00:15.900 Let me preface this session by saying that all credit is due to the team that is working on JRuby, the individual contributors, and everyone involved. All credit goes to them, so big kudos to them; they're working on amazing features.
00:00:20.220 Now, I'm not a contributor, so let's get that out of the way. But thank you to me because I'm going to talk about Java at a Ruby conference, so that takes some courage, right?
00:00:38.579 So yeah, I'm just here to spread the word and tell you guys about my story with JRuby. Let me start by introducing myself. I'm Yarden Laifenfeld, a software engineer at Rookout. I have a background in low-level programming, C, and Linux IoT environments, and I jump around between languages a lot.
00:01:02.579 At Rookout, we're developing a debugger for production, supporting six different languages. This means I can code in up to six, or maybe even seven, different languages—not in a day, but maybe in like a month or two. I mainly work with Ruby, Java, and Go, but I also delve into C#, Python, JavaScript, and C++.
00:01:28.439 Then, one day, my boss came to me with a task: support JRuby. I was like, "What is JRuby?" So that's what I did; I looked it up. Now, let me take you back in time to the year 2000.
00:01:50.040 The panic about the Y2K bug was dying down, and maybe I could walk, but certainly not code. A lot of people were working on a very big problem that exists in Ruby even today, which is Ruby's performance. In 2007, YARV was created to improve performance to be about four times faster than Ruby. But in 2001, JRuby was created for the same reason.
00:02:20.640 JRuby is a hundred percent Java implementation of the Ruby programming language; it is Ruby for the JVM. Okay, that’s a very general thing to say, and let’s just jump right to it. Should we use JRuby? Should you use JRuby today? Let’s talk about the advantages.
00:02:46.680 First of all, JRuby is written in Java, which means the core libraries and everything work seamlessly with Java libraries. You can also call Java code from Ruby and Ruby code from Java. You may not enjoy coding in Java, but other people do, and they might write code that's really useful for you, so you could just write Ruby and integrate with their code using JRuby.
00:03:36.060 In addition, JRuby runs atop the JVM, giving it access to features like threads. Threads don't exist or aren't managed well in Ruby, but in Java, they work great, and you should definitely use them. So if your code is going to utilize threads, JRuby is probably better performance-wise. The JVM is known for handling long-running applications well; the longer an application runs, the more optimizations the JVM can apply.
00:04:36.780 For short-running applications, Ruby does just fine, but if your application is intended to run for a long time, the JVM excels in optimizing it based on previous function calls. There are amazing stories about people running services in JRuby for ten years, which is quite long.
00:05:07.680 JRuby has its advantages, and I found a GitHub gist from 2010 that I was proud of because, well, 2010 was a long time ago. Jordan Sissel was asked why he uses JRuby in Logstash, and he gives a few reasons. I won’t name them all, but he mentions that Ruby had some undocumented breaking changes between versions 1.8 and 1.9.
00:05:32.340 He noted that with JRuby, you're not using the Ruby core implementations, so you won't encounter those breaking changes and could therefore trust JRuby more than Ruby. Also, his code needed threads for asynchronous operations.
00:05:45.660 Now, in 2010, people chose JRuby, and not much has changed since then. However, CRuby also has its advantages, largely due to having a larger community. More people use CRuby than JRuby, which leads to more features being built. There are more contributors finding bugs, which means that if there’s an edge case in JRuby that only you experienced, it might take longer to fix.
00:06:05.040 A large community is important when talking about programming languages because a larger community translates to more support and more features. The latest version of JRuby is still being developed today and is compatible with Ruby versions 2.6 and down.
00:06:48.300 So these are the things to consider. Performance-wise, JRuby delivers, but in terms of syntax support, CRuby has its advantages. This isn’t to say that the JRuby community isn’t supportive, but it doesn't operate at the same scale.
00:07:35.160 To the question of whether you should use JRuby, my answer is: I don't know, you decide. Just like the choice of any language for your next project, choosing between JRuby and regular Ruby shouldn’t be any different. Consider performance, support, bugs, and how long you expect your code to run.
00:08:13.620 One major advantage of JRuby is that you don’t need to learn a new language. I’m guessing most of you know Ruby, so transitioning to JRuby isn't that difficult. But I'm getting ahead of myself because my original task wasn’t to learn what JRuby is; it was to support JRuby in a debugger for production.
00:08:37.500 So let’s discuss that. I split this task into subtasks. First was running an app, but this wasn’t just running an app with JRuby—it was running an app with JRuby while one of our debuggers was attached. The second part was adding a breakpoint; just being able to manipulate the code and stop it at a certain point. The third aspect involved collecting data, like a stack trace or local variables.
00:09:25.440 If you just add a breakpoint and stop the code without collecting anything, that’s not useful. So, it was decision time. We support both Java and Ruby; each has its own product. We have a Java debugger and a Ruby debugger, and JRuby is this unique mix of both. What should I choose? Everyone was telling me to try to support JRuby along with Java, but I did the only logical thing I could do: I ignored them.
00:10:05.760 I started working on Ruby. Just to bring you up to speed, I created a simple to-do app running on Rails. It’s running smoothly, and I use RVM (Ruby Version Manager) which comes with support for JRuby, making it really convenient. I could simply run 'rvm install jruby' and 'rvm use jruby', and voila, I had JRuby on my computer.
00:10:56.160 I ran the bundle install, and everything was working perfectly until... So, bundle install didn’t work as smoothly as I expected. I spent three full business days trying to install my gems. I swear it was three days because I remember it distinctly. I was contemplating whether I should even include this part in my talk.
00:11:26.100 But if even one of you goes home and tries JRuby and runs into this bug, it will save you so much time and frustration. Apparently, as a young Ruby developer, someone advised me to set 'force_ruby_platform' to true globally on my computer. This works great if you’re working with Ruby, but that’s not the case with JRuby.
00:12:14.700 In essence, JRuby doesn't support native extensions. When I set 'force_ruby_platform' to true, I was instructing bundler to utilize native extensions, which JRuby doesn't support. This led to an endless loop of nothing working, which was really frustrating.
00:12:51.600 The big fix is with the command 'bundle config unset force_ruby_platform'. This slide is the one you should be taking a picture of. It's the bug you don’t want. It’s quite straightforward to fix, and after that, the bundle install functioned smoothly.
00:13:53.760 I could run my Rails app with JRuby just like I did with Ruby. Everything was running well, and you could see right there that it was indeed running with JRuby. I made no changes to my code at all, so if you’re considering moving to JRuby, you might wonder about performance and how long it would take to start your application. Do it! It really is that easy.
00:14:23.520 Next, I wanted to run the app with the debugger attached. This is very easy; our debugger is just a gem. You add it with 'bundle add ruckout', and then you have to add an initialization line. After that, I was running again, and Ruckout was there, functioning perfectly with JRuby.
00:15:03.900 Now it was time to add a breakpoint. In Ruby, we handle this with something called the TracePoint API. The TracePoint API is quite useful; it allows you to trace your code by capturing various events, such as each time a line of code is executed or when a thread starts or ends.
00:15:30.480 You can sign up to listen for many events, allowing you to trigger actions when an event occurs. With the line event, for example, you can create a new TracePoint that subscribes to the line event and enables it so that every time a new line is executed, your code breaks and runs a callback.
00:16:24.480 However, we needed to consider performance, since this is a debugger for production. We don’t want to stop at every line; we only want to stop at the lines of interest. So, we give the enable method two parameters: one for the target method we want to pause at and one for the specific line number.
00:17:20.460 I tried integrating this in Ruby and JRuby but kept running into an error: 'wrong number of arguments calling enable'. I then realized that JRuby did not support the target parameter for enabling events.
00:18:08.040 So, for now, I could only use tracepoint.enable without those parameters, but that seriously affected performance, meaning placing a breakpoint in this way wasn’t viable. I had to reconsider my approach and ultimately decided to switch to Java.
00:18:54.120 With Java, we had to revisit our original three tasks. Running an app with a Java agent is a bit more complex. A Java agent is a Java program running alongside another app, which means it’s not useful by itself.
00:19:37.740 The agent uses the Java agent flag along with a JAR file to attach to the application. Instead of starting at the main method, it runs at pre-main, which is a minor difference. The agent has access to the instrumentation API, which allows it to inject bytecode into a running application.
00:20:24.540 Since JRuby operates at the top of the JVM, we can also pass flags to the JVM. By using the Java agent flag, I managed to get everything up and running smoothly.
00:20:57.540 Now it was time to add a breakpoint. Language integration on the JVM works by compiling all languages into bytecode, and the JVM knows how to run and optimize that bytecode.
00:21:29.040 However, I placed a breakpoint in a function that creates a to-do task, but the breakpoint didn’t activate. The class was supposed to load; I created a task and expected the function to run, leading to the class loader and transformation.
00:22:25.200 Frustrated, I added additional tasks, but even this didn’t help. By repeatedly invoking the function, the breakpoint then registered. Sometimes, it seems that frantic activity makes things happen.
00:23:11.040 It seemed that JRuby utilizes three compilation modes: interpretation, JIT, and actual compilation. In interpretation mode, JRuby interprets code line by line, which doesn’t load classes. During compilation mode, however, every Ruby class is loaded like a Java class and transformed through the class loader.
00:23:58.200 The JIT mode serves as a balance, compiling the code running frequently. Early on, when I called a function once or twice, JRuby didn’t prioritize it for loading, but as I invoked it rapidly, JRuby identified it as significant and compiled it through the class loader.
00:24:50.040 Finally, with force compilation, I could ensure that the bytecode would be compiled when needed, hence the breakpoints began activating seamlessly.
00:25:18.240 Yet, data collection proved to be challenging. I set a breakpoint to collect local variables, expecting to retrieve both the task variable and 'self' from the TasksController.
00:25:44.760 However, when I checked the collected variables, it gathered way more than just those two variables. It logged class scope, self, and a slew of additional contextual variables I hadn’t anticipated.
00:26:25.320 Parsing through these collected variables was tedious, but eventually, I managed to isolate and access the pertinent information to support JRuby.
00:26:50.640 In conclusion, I was able to implement support for JRuby successfully. Thank you so much for your time!
00:27:33.840 Now, there’s always a saying: if you're going to present Java at a Ruby conference, you better start strong. I think you did a fantastic job with that!
00:27:46.560 Looking back at what you've done from different points of view, what have been the key learnings from your journey with JRuby?
00:28:12.420 First of all, it’s incredible how innovative JRuby is considering it was created in 2001, which feels like a long time ago. I had no idea how much work went into building a Ruby interpreter in Java, integrating features like interpretation and compilation.
00:28:57.260 Also, the team behind JRuby is impressive. They’re attentive and engaged; any questions in their chat receive prompt answers, and they clearly have a deep understanding of their code.
Explore all talks recorded at EuRuKo 2022
+6