That was fast! Another?

Indeed. I have to confess that I spent the weekend reading through “Deploying with JRuby 9k” by Joe Kutner and following some of the links in the book, which led to a lot of cool discoveries.

book cover

If you’re looking at JRuby for more than a small test app, I do recommend it, although as it has a lot of examples and commands I fear may become outdated fast as tools evolve. So that’s why you should get it while it’s hot (no commission here, just a happy camper)!

So without further ado…

jruby-lint: see how ready your ruby code is for the J

The jruby-lint gem—also maintained by the JRuby developers—looks at your code and Gemfile and warns you of things that may pose issues when using JRuby:

./Gemfile:24: [gems, warning] Found gem 'oj' which is reported to have some issues:
Try gson, json or json_pure instead.
Try any one of the following JRuby-based servers: Trinidad, Mizuno, Kirk or Puma (though make sure to use the JRuby-native version of the gem).
./Gemfile:43: [gems, warning] Found gem 'bson_ext' which is reported to have some issues:
bson_ext isn't used with JRuby. Instead, some native Java extensions are bundled with the bson gem.
./Gemfile:59: [gems, warning] Found gem 'nokogiri' which is reported to have some issues:
For best results, use the pure-Java version of Nokogiri (default after v1.5).
./app/controllers/api/user_controller.rb:25: [nonatomic, warning] Non-local operator assignment is not guaranteed to be atomic

The developers are still hoping to further improve this gem, but it’s a good starting point for a project looking at going the JRuby way.

bundler install --binstubs, or having your cake and eating it too

In my previous post on JRuby I mentioned that running commands with bundle exec meant longer start-up times, as that meant booting up a JVM with JRuby for bundler, which would then fork another JVM with JRuby for the target command.

I suggested as a workaround to not run the commands inside bundler, but by doing that you’re giving up a lot of bundler’s goodness.

But little did I know that there was an alternative, even better solution: the --binstubs option! When running bundle install --binstubs, bundler creates small ruby wrappers for each command supplied by the installed gems (e.g. rspec, pry, puma, etc) in a bin/ folder inside your project directory.

Using these wrappers to run commands from gems, you get the same environment and setup you’d get from bundler, but avoid the slow behavior that you’d get from using bundle exec. So you get a correct environment—check ✔—and speed—check ✔.

You do have to remember to run, e.g. bin/rspec instead of bundle exec rspec. But for that I suggest having your shell do the work for you:

# Example code for .bashrc

functionBer() {
  if command -v bin/rspec; then
    bin/rspec $*
  else
    bundle exec rspec $*
  fi
}

alias ber=functionBer

This creates a ber alias for running rspec that will run the command using the binstub if possible, and fall back to bundler if there isn’t one (e.g. maybe you don’t want to have binstubs on all your ruby projects… yet).

jruby has its own built-in profiler

You can run JRuby with the --profile, --profile.graph or --profile.html options to get a performance report at the end of the application run.

$ JRUBY_OPTS="--profile --dev" pry -e "puts 'hello'; exit"
[1] pry(main)> puts 'hello'; exit
hello

main profile results:
Total time: 2.11

total   self children calls  method
---------------------------------------------
 1.48   0.02     1.46   894  Kernel.require
 1.23   0.01     1.22  4060  Array#each
 1.00   0.00     1.00     1  Gem::ExecutableHooks.run
 1.00   0.00     0.99   839  Proc#call
 0.95   0.07     0.88    26  Kernel.load
 0.93   0.00     0.93   159  Kernel.require
 0.78   0.00     0.78     1  Noexec#check
 0.78   0.00     0.78     1  Noexec#setup
 0.64   0.08     0.56   151  Kernel.eval
 0.48   0.08     0.40  4840  Class#new
 0.39   0.00     0.39     1  Noexec#candidate?
 0.31   0.01     0.31   158  Gem::Specification.load
...

You can also use --profile.api to get access to an api to selectively turn profiling on and off in certain parts of your code.

See the JRuby wiki for more details on these options.

getting some jruby magic in jconsole

By running JRuby with the --manage option, you’ll enable several JRuby jmx extensions, allowing you to do things such as executing ruby code on a running JRuby instance, or getting a full ruby stack trace for all running ruby threads.

You can then access them using jconsole, a tool already included with most JDK installs.

Running jconsole with the JRuby --manage option

you can also use good ol’visualvm

The VisualVM tool is also installed by default on most JDK distributions (start it with visualvm or jvisualvm) and allows you to inspect a running VM, trigger garbage collection or heap dumps, and analyze cpu and memory consumption both via a profiler and a sampler.

Inspecting JRuby application with JVisualVM

Furthermore, if you include -J-Djruby.reify.classes=true as one of your JRUBY_OPTS then JRuby will generate a Java class for each ruby class, resulting on this very beautiful result from a sample application using sidekiq:

Inspecting JRuby heap dymp with reify.classes enabled

Just… amazing…

Update, 17th January 2017: Note that the reify.classes option is experimental so carefully test it before using it in a production environment.

that’s all very great, old chap, but i deploy using heroku

Hey, what a coincidence, I do too. The heroku exec plugin has you covered.

You read that right. You can connect tools such as visualvm to a running heroku dyno and monitor it. And take heap dumps. And execute ruby snippets. Securely.

**mindblown** **manly tears**

Obviously, with great power comes great responsibility, but every tool is welcome on my toolkit when debugging hard production issues.

Did you know you can deploy .war files (generated with warbler) directly to heroku?

And did you know you can deploy docker images to heroku? See the heroku-container-tools and documentation.

– I didn’t.

Until next time!