Tuesday, February 13, 2007

Rails Support Status Update

Things are moving along well...so well I've found time to reimplement String with byte[], work on closures in the compiler, and, well those are posts for another day. Today I update you on the progress of supporting Rails in JRuby.

Rails is quite an interesting beast. I've learned more about Ruby looking through Rails code than from any other source...out of sheer necessity. They say the best way to learn a language is through immersion, right? Is debugging 50-deep stack traces on a questionable interpreter, digging for a reducible test case immersive enough for you? Yeah, I thought so.

We actually hit Rails support hard right at the beginning of this month, or the end of January. The early results were pretty solid, so our efforts have been distracted onto other large JRuby issues not necessarily Rails-related (but all still critical for an eventual 1.0). We also made the bold move of jumping to Rails 1.2.x for all our testing, to show we can keep up with the Rails development process. And to show that we've made great progress, I give you the following results.

ActiveSupport

ActiveSupport is the base module for Rails. It monkey-patches a number of core classes, provides a multibyte String wrapper for UTF-8 encoded text, and handles most of the details of running and configuring an application. It "supports" the other libraries, and is the first crucial leg needed to run Rails. And so we must "support" it well.

Here's the current results of a full test run:

498 tests, 1809 assertions, 16 failures, 6 errors

That's already in the 95% range, so we're in darn good shape. But it turns out a number of these errors are caused by a glitch in our parser related to KCODE. So if we work around that known issue, the results improve to:

498 tests, 1845 assertions, 12 failures, 1 errors

So more like 97% passing once we fix the KCODE parser problem. The remaining issues are almost all related to time-formatting bugs, with a couple multibyte and exception-handling issues tossed in. I haven't attacked the formatting bugs because Printf code is bloody painful, the multibyte issues are waiting on the KCODE parser fix, and the others...well, they're boring.

ActionPack

ActionPack is the brains of a Rails app, housing the mechanisms for controllers, views, and any code to support them. So it's leg two of the crucial three-legged support necessary to run a Rails application.

I hit ActionPack hard in the past, and a bit this month. Ola took off with it and completed most of the remaining failures. Running with the same KCODE workaround, we have the following results today:

1157 tests, 4811 assertions, 7 failures, 14 errors

That's above 98% passing. The remaining failures include a number of dupes (a single failure that breaks a number of tests), some additional string-formatting failures, and a couple that run ok outside of Rake. So it's damn close to perfect.

ActiveRecord

You should all know ActiveRecord by now, right? It's Rails' DB layer, based on the ActiveRecord pattern. It is the third core leg of the Rails platform, though you can certainly have apps that don't use AR for database support.

ActiveRecord is an extensive piece of code and it's the only part of Rails that usually requires a native library to run properly (though there is a pure Ruby impl of its MySQL support that nobody uses). In order to support AR, a number of the JRuby community members have cooperated over the past 9 months to build ActiveRecord-JDBC, a gem-installable module that provides ActiveRecord DB support via JDBC. It's a great bit of hackery, and runs surprisingly well considering our less-than-beautiful Java integration performance (under repair).

To limit the scope of this month's Rails work, we're targetting MySQL, since it's the de-facto standard for Rails apps in most quarters. But most of the work we're doing will apply equally well to the other databases, since JDBC is generally very consistent. Tom has been spending lots of time on Derby, for example, to the point that our ActiveRecord-on-Derby failures are almost entirely limited to SQL features it doesn't support yet.

So then ActiveRecord test results, minus the KCODE workaround (since Tom ran these for me):

1012 tests, 3417 assertions, 41 failures, 35 errors

Here our results dip to around 92% passing, but it's really the extended features of AR that have failures here. We've come a long way on this; results on the months-outdated JRuby wiki show Rails 1.1.6's ActiveRecord only passing about 60% of that release's tests, so there's been a ton of improvement since November. And we generally understand how to fix the remaining failures, so it's only a matter of (short) time.

ActionMailer

Can you guess what ActionMailer does? ActionMailer provides support for composing, formatting, and sending email from a Rails app. And that's about it. It's not a huge library, but it's essential for many apps.

64 tests, 142 assertions, 9 failures, 6 errors

That's about 75% passing, with a grand total of six test scripts. We haven't focused on this much, other than Tom's initial KCODE work a few months back. The current failures are almost all mail-formatting or SMTP-wrangling issues. I don't expect them to be hard to repair.

ActionWebservice

Because ActionWebservice's tests require some database setup, we haven't tackled them yet. But I would lay even money that they'll be comparable to ActiveRecord at their worst. For now, they all just fail because MySQL isn't set up correctly for them.

96 tests, 0 assertions, 0 failures, 96 errors

Any community member that wants to dive into ActionWebservice or ActionMailer would fast become a JRuby Hero.

Railties

Railties is the final piece of the Rails puzzle, and it...well..."ties Rails" together. I show the results here mostly for completeness; many of the libraries in Railties we'll never support (fcgi, for example) and most JRuby-on-Rails deployments will use alternative mechanisms for hitting the web.

5 tests, 19 assertions, 2 failures, 0 errors

Official Rails Support?

Because things are looking pretty solid, we've been looking for an answer to this question: What does Rails Support in JRuby mean? Do we have to pass all test cases 100% to "officially" support Rails? That might never happen, since there's POSIX and external library stuff we won't ever handle (nor will we need to for JRuby on Rails apps). So then is it a certain percentage? 95%? 98%?

I think the truth is that we could really announce support for Rails now. Almost all the visible, outstanding issues with actually *running* Rails apps have been resolved, and most apps and scripts work fine. There's ongoing work to improve ActiveRecord-JDBC's support for other databases, but that's an endless quest. And of course there's more work needed to support Grizzly, Mongrel, and WAR-based deployment of JRuby on Rails, but those are peripheral to the official announcement. Even when we do make an official announcement, it will be for a pre-1.0 version of JRuby, since we know there's another few months left on 1.0 fixes and features.

So what do you think, dear reader? At what point would you feel safe saying "Let's have our non-JRuby hackers try using JRuby on Rails"? You probably would *be* safe right now, since even if you found issues we've got a busy community ready to help solve them. And we'll probably tiptoe closer to "perfect" Rails support in JRuby over the next couple months, chasing the long tail of Ruby compatibility. But how do these numbers and this update make you feel about JRuby on Rails today?

And as always, we love to have additional contributors, so we'll bend over backwards to make it easy for you to help. Join the lists, join #jruby on freenode IRC, or toss us email privately. JRuby is an amazing community-driven success story, and the only thing missing is you.

6 comments:

Raphaël Valyi said...

Hi Charles,

I'm coding a Rails app with heavy matrix computations to work out some community properties in a community site. I know that in Java I could get some efficient data structures to do that a way faster than the current ruby libs (sparse matrix multiplications and searches). So JRuby might well be a solution when it will be mature enough. In doubt about that, I'm also thinking about using Rubyinline or even Swig to boost the speed with some frightening C/C++ code.

So I've got a few questions about JRuby and JRuby close future:
* Hotspot might indeed inline Ruby code to run it faster. But is that going to be faster only on very repetitive code (like fib) or even for Rails every day life method dispatching or can we take it for granted it will rather run much slower (so I might loose on one side what I'll gain on the other side)?
* Is that going to be faster on such Rails code after the server answer several time quite the same answer (not the exact same anwser though)?
* Is Rubyinline working in JRuby? Will it work?
* What are the conditions to use native libs in JRuby such as Mongrel, rb-gsl, Hpricot? Does it require extra work to get those native bindings working in JRuby or is the few remaining JRuby bugs that prevent to do that if not possible yet?

Thanks for any explanation and for your work with JRuby. I'm sorry, since it's a spare time app (I'm doing awful Struts stuff in the legal life), I'm not in position to contribute to JRuby by now. Still, I'm waiting the right time to advise my company switching from JEE to Rails and JRuby hopefully. We could hopefully try some back office prototypes in a few weeks.


NB: by the way, I'm very interested in running JRuby inside sandboxed applets (along with Swing libs such as JGraph). I think it's extremely important JRuby can run inside the security sandbox. If not applets will always loose against javascript Ajax which is sandboxed. If JRuby plays nice with the sandbox things might be different however. But I saw in Jira you were already taking care about that, that's important indeed.

Regards,

Raphaël Valyi

Anonymous said...

Raphael,

Because Hpricot uses Ragel to generate it's C code, and Ragel can also generate java, Hpricot works in Jruby.
See: http://ola-bini.blogspot.com/2007/02/hpricot-goodness.html

I guess since mongrel usesd Ragel, it could have the same treatment.

Charles Oliver Nutter said...

Raphaël Valyi: Some answers to your questions (in order)...

- The short-term goal is to have interpreted mode Ruby run at least as fast as the C Ruby equivalent. This is generally counting on HotSpot to accelerate the interpreter and runtime code for us, rather than directly accelerating the Ruby code. The medium-term goal, already in progress, is to compile as much Ruby to Java bytecode as possible, allowing that "last mile" of HotSpot optimization to kick in. Initial numbers for the compiler show real promise, and there are many levels of optimization left to explore in both interpreted and compiled modes.

- Like all Java code, it will take some time for HotSpot to really hit its stride. But we're also trying to make short-term execution as fast as possible, so that one-off scripts will get up and going quickly.

- RubyInline does not currently work in JRuby, but primarily because nobody has yet looked at making a Java version of its code generator. Obviously the compiled C and FORTRAN generators won't work in JRuby. I'd consider RubyInline a possible solution if you're willing to tie yourself to one implementation of Ruby, but it will hurt portability.

- Any libraries with native-compiled components would have to be reimplemented in Java. Of the examples you list, Hpricot already has a Java version, and Mongrel has had prototype versions in the jruby-extras project with a full Ragel-based version likely to come out soon. I have not looked at rb-gsl, but most extensions are fairly easy to port since Java already provides those features through built-in or third-party libraries. We've generally tried to work closely with extension authors to ensure a Java version is fully compatible as well as gem-installable (as in the case of Hpricot).

I hope you are able to contribute soon. There's plenty of interesting work to be done.

And yes, I want JRuby-in-applet to continue working well (it works already today, see my previous posts on that). I think there's real potential for Ruby-based rich-client development, and I'd much rather use Ruby than JavaScript any day.

dvae: Mongrel work is likely to start in the next day or two. Ola's going to tackle it, having used Ragel for Hpricot already, and he doesn't expect it to be very difficult.

Unknown said...

you said "That might never happen, since there's POSIX and external library stuff we won't ever handle"

Can you give an example of POSIX and external library stuff?

Raphaël Valyi said...

Hi Charles, thanks for your explanations.
For RubyInline, my point was more about if there might exist a JNI wrapping system that would mimic RubyInline taking the same C/C++ blokcs. I understand it's not the case currently and it might require some work. Indeed, I think portability doesn't matter that much on the server side. At least, I mean it's imporant to be able to achieve blazingly fast code at the price of portability, especially on the server side. And C ruby have a number of ways to make such a C/C++ wrapping dead simple both to writte and to compile. That's something that definitely attracted me to Ruby.

I just give a try to JRuby for my heavy graph clustering algorithm ("edge betweenness clustering"). It mainly consist in running a bunch of arrays and stack and summing the indexes on the way, dead simple. It was quite slow with pure Ruby (that's why I'm considering rewritting that part with RubyInline and C++). Unfortunately, at this point, JRuby runs is 1.75 times slower than CRuby. Quite understandable (and quite good actually!) for the JIT-less interpreter. I also tried with the jrubyc command but I got an error at the first line:
"Error -- Not compileable: Can't compile node: ClassNode [Colon2Node [ClusterTest]]"

Didn't you said jrubyc had an interpreted fall back behavior when it cannot compile a node? Please what shoudl I do, I'm curious to see if jrubyc is faster than C Ruby, because that algorithm should be mostly compilable just like your fib test.

Also I'm probably going to run my server with CRuby with C matrix calculus bindings (rb-gsl) and possibly 100 C++ lines if required for the moment, but if JRuby seems fast enough, then I might switch soon.

dvae, thank you for pointing out Ragel, I was unaware of it, but it looks like a killer piece of code!

Best regards.

Raphaël

Charles Oliver Nutter said...

Raphaël Valyi: Yes, the compiler can skip nodes it doesn't recognize, but only on a whole-method or whole-file basis and only with the JIT enabled. The jrubyc command will either compile everything or fail. You might try enabling the JIT, though the compiler work has been put on hold temporarily while we wrap up the remaining Rails issues.

Also, as you say, 1.75x is not bad, and it's actually many times better than we were a year ago (perhaps an order of magnitude better). Thanks for that information.