another round of jruby goodness
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.
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.
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.
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:
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.
and finally, somewhat related
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!