$30 off During Our Annual Pro Sale. View Details »

A Whole New World: Rails on the JVM with JRuby!

headius
April 24, 2023

A Whole New World: Rails on the JVM with JRuby!

You probably know about JRuby. You might know it runs Rails. But why?! What does the Java platform do for Ruby and Rails developers that we can't get from regular Ruby? This talk will show how JRuby's integration with the JVM opens up new worlds for Rails developers.

Delivered at RailsConf 2023

headius

April 24, 2023
Tweet

More Decks by headius

Other Decks in Programming

Transcript

  1. A Whole New World!
    Rails on the JVM with JRuby!

    View Slide

  2. Who Am I
    • Charles Oliver Nutter


    • @headius(@mastodon.social)


    [email protected]


    • JRuby developer since 2004


    • Full-time JRuby and JVM language
    advocate since 2006

    View Slide

  3. View Slide

  4. View Slide

  5. What is JRuby?
    • An implementation of Ruby atop the Java Virtual Machine


    • Ruby implementation
    fi
    rst, JVM language second


    • Many bene
    fi
    ts from JVM ecosystem


    • Ruby code should "just work"


    • Different extension API, no forking, parallel threads


    • www.jruby.org

    View Slide

  6. View Slide

  7. Ruby Compatibility
    • JRuby 9.4 is out now!


    • Ruby 3.1 compatible, 98% of language specs passing


    • Nearly complete core + stdlib features from 2.7, 3.0 and 3.1


    • JRuby 9.3 (Ruby 2.6 compat)


    • Maintenance through 2023 as needed


    • Compatibility before performance!


    • Now we can refocus on optimization again

    View Slide

  8. Ruby 3.2 Coming Soon!
    • Checklist issue: jruby/jruby#7517


    • You can help us add missing features!


    • Hopefully release 3.2 compatibility in the next 6 months

    View Slide

  9. Why JRuby?
    • JVM GC, JIT, concurrency scales Rails extremely well


    • Rich tools for monitoring, pro
    fi
    ling, debugging


    • Wide platform support: desktop, server, mobile


    • Zero-compile cross-platform GUI support


    • Java ecosystem moving fast, with thousands of excellent libraries

    View Slide

  10. Getting Started

    View Slide

  11. View Slide

  12. JRuby Install
    • Install a JDK


    • Java 11+ recommended, there's many distributions out there


    • Java 8 supported for 9.4 and lower


    • Install JRuby


    • Recommended: system package, Ruby installer, Docker image


    • Download tarball/zip or Windows installer

    View Slide

  13. Test it out!
    [] ~ $ rvm use jruby


    Using /Users/headius/.rvm/gems/jruby-9.4.2.0


    [] ~ $ irb


    >> runtime = java.lang.Runtime.runtime


    => #


    >> runtime.available_processors


    => 8


    >> runtime.free_memory


    => 91420584


    >>

    View Slide

  14. JRuby on Rails

    View Slide

  15. JRuby Runs Rails!
    • Since 2007!


    • "Are there JRuby users running Rails applications?"


    • Oh yes! And at large scale!


    • Bene
    fi
    t from the JVM, libraries, languages


    • Great way to scale large Rails apps today!

    View Slide

  16. Rails 7
    • Rails 7 works on JRuby 9.4!


    • activerecord-jdbc for JRuby: SQLite, MySQL, PostgreSQL


    • Version 60.0 for Rails 6, 70.0 for Rails 7, etc


    • Oracle, SQL Server, DB2, are available, may need updates


    • Performance and compatibility looking good!

    View Slide

  17. Rails 7
    actioncable: 203 runs, 921 assertions, 0 failures, 10 errors
    actionmailbox: 92 runs, 238 assertions, 0 failures, 0 errors
    actionmailer: 230 runs, 516 assertions, 0 failures, 1 errors
    actionpack: 3550 runs, 16802 assertions, 1 failures, 1 errors
    actiontext: 80 runs, 145 assertions, 7 failures, 2 errors
    actionview: 2424 runs, 5395 assertions, 2 failures, 4 errors
    activejob: 368 runs, 837 assertions, 0 failures, 0 errors
    activemodel: 966 runs, 2853 assertions, 5 failures, 0 errors
    activestorage: 392 runs, 1121 assertions, 0 failures, 0 errors
    activesupport: 5188 runs, 12926 assertions, 43 failures, 28 errors
    99% passing!

    View Slide

  18. Minimal Con
    fi
    g Changes
    • Generate a new app, Rails will use JRuby defaults


    • Use threads in Puma instead of workers


    • 2n+1 is a good rule of thumb


    • Make sure to increase DB connections as well


    • Existing app: try bundling, replace unsupported libs


    • Let us know! We can prioritize adding JRuby support!

    View Slide

  19. View Slide

  20. Scaling Rails with JRuby

    View Slide

  21. Scaling Rails
    • Classic problem on CRuby/MRI


    • No concurrent threads, so we need worker processes


    • Processes duplicate runtime state and waste resources


    • JRuby is the answer!


    • Multi-threaded single process runs your entire site
    • Single process with leading-edge GC uses resources better

    View Slide

  22. Baseline Rails App
    • Scaffolded "blog" application on
    PostgreSQL, Puma


    • IBM VPC instance: 8 vCPU, 32GB


    • CRuby 3.2, 16 workers


    • JRuby 9.4: 16 threads


    • Database, siege benchmark driver
    on same instance

    View Slide

  23. Workers vs Threads
    • Workers duplicate a lot of work


    • N * GC overhead, single large heap is better


    • N * JIT overhead and memory usage


    • Unavoidable to have some duplicated memory


    • JVM and JRuby support real parallel threads


    • One process, one GC, one JIT scales all the way up

    View Slide

  24. Caveats
    • PostgreSQL local via Docker (<5% overhead)


    • Siege benchmark driver also local (<5% overhead)


    • Very minimal blog app, full end-to-end requests


    • JRuby tuning and warmup time are important

    View Slide

  25. Requests per second
    0
    450
    900
    1350
    1800
    60s siege iteration
    1 2 3 4 5 6 7
    JRuby 9.4 CRuby 3.2

    View Slide

  26. requests per second (higher is better)
    0rps
    450rps
    900rps
    1350rps
    1800rps
    1,280rps
    1,705rps
    JRuby 9.4 CRuby 3.2

    View Slide

  27. Memory
    • Baseline


    • JRuby: 3.4GB RSS


    • CRuby: 16x 103MB = 1.6GB


    • With tuning options


    • JRuby with 300MB heap: 955MB


    • CRuby YJIT: 16x 125MB = 2GB

    View Slide

  28. Requests per second
    0
    450
    900
    1350
    1800
    60s siege iteration
    1 2 3 4 5 6 7
    JRuby CRuby 3.2 JRuby 300MB heap CRuby 3.2 + YJIT

    View Slide

  29. requests per second (higher is better)
    0rps
    450rps
    900rps
    1350rps
    1800rps
    1,550rps
    1,643rps
    1,280rps
    1,705rps
    JRuby 9.4 CRuby 3.2 JRuby 300MB heap CRuby + YJIT

    View Slide

  30. JRuby on Rails Scales!
    • More requests per second with lower latency


    • Lower memory usage beyond 8-10 concurrent users


    • Deploy anywhere that has a JVM (which is basically everywhere)


    • Leverage JVM tools to monitor and optimize

    View Slide

  31. True Story
    • Large Rails application using 40 xlarge on EC2


    • 40 worker processes per server


    • 100k-150k req/min, 50-75ms response times


    • Migrated app to JRuby, made more use of threading


    • Down to 10 xlarge, 75% cost reduction


    • Consistently over 150k req/min, 30ms response times
    $

    View Slide

  32. Deploying JRuby the Ruby Way
    • Typical servers, devops tools in Ruby work
    fi
    ne


    • Usually no compilation required; install and go


    • Scales great with Puma


    • Rails using Puma by default


    • Small frameworks (e.g. Sinatra, Roda) scale even better

    View Slide

  33. Deploying with Warbler
    • gem install warbler ; warble


    • Bundle your app + libs + JRuby into a single
    fi
    le


    • Hand off to ops to deploy or run as executable (w/ built-in server)


    • One click deploy to Java clouds:


    • Elastic Beanstalk, Google AppEngine, etc


    • github.com/jruby/warbler

    View Slide

  34. But wait, there's more!

    View Slide

  35. So much more!
    • JVM and JRuby have much more to offer


    • We'll cover a few areas that make JRuby stand out


    • Some of these are early or experimental, but very exciting!

    View Slide

  36. JVM Tools

    View Slide

  37. JDK Flight Recorder
    • Low-overhead monitoring and pro
    fi
    ling for JVM


    • Always-on monitoring <1% overhead


    • Active pro
    fi
    ling around 5% overhead


    • Track memory, heap usage, GC, hot methods, JIT activity


    • Think New Relic, AppSignal, Sentry.io... but built-in and FREE

    View Slide

  38. JDK Mission Control
    • GUI control center and visualizer for Flight Recorder data


    • Start and manage recordings


    • Save, load, and browse recorded data


    • https://adoptium.net/jmc/

    View Slide

  39. View Slide

  40. View Slide

  41. View Slide

  42. View Slide

  43. View Slide

  44. View Slide

  45. Visual VM

    View Slide

  46. GUI and Graphics

    View Slide

  47. GUI Libraries
    • Swing, built into JDK


    • Clean, cross-platform, easy to build simple UIs


    • Scalable Windowing Toolkit (Eclipse SWT)


    • Native widgets, WebKit browser component, rich ecosystem


    • JavaFX (via JRubyFX, github/jruby/jrubyfx)


    • Scene-based, vector drawing, event-driven modern UI library

    View Slide

  48. Shoes 4

    View Slide

  49. Glimmer
    • Glimmer GUI DSL


    • Multiple backends (SWT, GTK, ...)


    • JRuby + SWT is the most mature


    • JRuby makes cross-platform GUI
    much easier!


    • Works same everywhere


    • GUI libraries shipped with gem

    View Slide

  50. Purugin for Minecraft
    event(:player_egg_throw) do |e|
    e.hatching = true
    e.num_hatches = 120
    e.player.mesg "hatched"
    end

    View Slide

  51. GUIs... like Android?

    View Slide

  52. Ruboto: JRuby on Android
    • ruboto.org


    • Actively used for commercial
    projects today


    • Build interface with GUI builder, wire
    it up with Ruby code


    • Neglected a bit but being updated
    for JRuby 9.4 now!

    View Slide

  53. View Slide

  54. Trying Ruboto IRB
    • Original was pulled from store because it asked for ALL privileges!


    • Duh, it was a tech demo and REPL


    • We will republish soon, but search for two packages to download:


    • Ruboto Core: JRuby and Ruboto framework as a shared package


    • Ruboto IRB: REPL, editor, example scripts

    View Slide

  55. More JVM Features for Ruby

    View Slide

  56. InvokeDynamic

    View Slide

  57. InvokeDynamic
    • User-de
    fi
    ned binding of methods and values


    • Language decides target based on dynamic lookup logic


    • JVM optimizes that target like static code


    • Introduced in Java 1.7 (2011) with input from JRuby


    • Key to JRuby performance!

    View Slide

  58. InvokeDynamic Performance
    Times faster than JRuby Java 8 no indy
    0
    1.25
    2.5
    3.75
    5
    Mandelbrot Red/Black
    4.05x
    3.92x 3.74x
    3.68x 3.72x
    1.97x
    Java 8 indy Java 11 indy Java 17 indy
    3.28x CRuby JIT 1.86x CRuby JIT

    View Slide

  59. Indy + Graal JIT?
    Times faster than JRuby Java 8 no indy
    0
    4
    8
    12
    16
    Mandelbrot Red/Black
    3.13x
    15.7x
    4.05x
    3.92x 3.74x
    3.68x 3.72x
    1.97x
    Java 8 indy Java 11 indy Java 17 indy Graal CE indy
    Escape analysis
    But not always better

    View Slide

  60. Project Loom

    View Slide

  61. Project Loom
    • JRuby's
    fi
    bers are based on threads


    • Too many, JVM blows up!


    • Scheduling, resource-intensive


    • Loom brings
    fi
    bers to JVM


    • Easily handles thousands of
    fi
    bers


    • Faster context-switching

    View Slide

  62. View Slide

  63. View Slide

  64. 5.times do
    t = Time.now
    # create 100k fibers
    ary = 100_000.times.map { Fiber.new { } }
    # resume and complete 100k fibers
    ary.each(&:resume)
    p Time.now - t
    end

    View Slide

  65. $ jruby fiber_test.rb
    [7.603s][warning][os,thread] Attempt to protect stack guard pages failed
    (0x00007fc240a00000-0x00007fc240a04000).
    #
    # A fatal error has been detected by the Java Runtime Environment:
    # Native memory allocation (mprotect) failed to protect 16384 bytes for
    # memory to guard stack pages
    #
    # An error report file with more information is saved as:
    # /home/headius/work/jruby93/hs_err_pid75149.log
    #
    # If you would like to submit a bug report, please visit:
    # https://bugreport.java.com/bugreport/crash.jsp
    #
    Aborted (core dumped)
    😩

    View Slide

  66. --enable-preview

    View Slide

  67. $ jruby -J--enable-preview fiber_test.rb
    2.324123
    0.880373
    0.6916289999999999
    0.73703
    0.655856
    🤩

    View Slide

  68. Project Panama
    • Foreign function interface (FFI)


    • With JVM help to make direct calls


    • Foreign memory API


    • JVM-assisted access, lifecycle


    • API extraction from C/++ headers


    • Save time setting up bindings

    View Slide

  69. jextract code generator
    • Writing bindings using FFI or Fiddle is still challenging


    • Parameter sizes, struct layout, in and out values, pointers


    • Differences across platforms


    • jextract: produce Panama/FFI stub code from a C header
    fi
    le


    • Call that stub code from JRuby... easy FFI for all!

    View Slide

  70. Wrapping Up

    View Slide

  71. JRuby Future
    • JRuby 9.4 continues to stablize


    • Many production users already!


    • Big optimization work coming the rest of this year


    • JRuby 9.5: Java 17 minimum, Ruby 3.2 support, many optimizations


    • Lots of new JVM features to leverage


    • Looking into native compilation, snapshotting for fast startup!

    View Slide

  72. JRuby on Rails Future
    • Rails 7 support is looking great!


    • We are caught up again, will keep tracking updates


    • As your app grows, JRuby can help you scale


    • Reduce resources, save money 🤑


    • Let's talk about running your app on JRuby!

    View Slide

  73. Thank You!
    • Charles Oliver Nutter


    [email protected]


    • @headius


    • https://github.com/jruby/jruby


    • https://www.jruby.org

    View Slide