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

Scaling Ruby Applications with JRuby

headius
February 07, 2020

Scaling Ruby Applications with JRuby

JRuby is the fastest way to run Ruby apps, from microservices to large monolithic Rails apps, and it’s easier than ever to get started! We’ll cover JRuby installation, migrating apps, and deploying and scaling applications using JRuby and standard hosting services. Save money! Use JRuby!

Delivered Feb 7, 2020 at RubyFuza in Cape Town, South Africa

headius

February 07, 2020
Tweet

More Decks by headius

Other Decks in Programming

Transcript

  1. Scaling Ruby with JRuby
    Thomas E. Enebo (@tom_enebo)
    Charles O. Nutter (@headius)

    View Slide

  2. • JRuby co-leads
    • Red Hat
    Charles Thomas Ruby
    Java
    Beer

    View Slide

  3. What is JRuby?
    Ruby Implementation (2.5.7 compatible)
    …which happens to run on the
    Java Virtual Machine (JVM)

    View Slide

  4. JRuby 9.2.9.0
    • Lots of memory improvements
    • Better support for Java module system
    • .jruby.java_opts file to manage JVM options
    • Improved startup time for most commands
    • Dozens of issues fixed
    • 9.2.10.0 coming out next week? 9.3.0.0 next (2.6.x)

    View Slide

  5. Why JRuby?
    What are the differences?
    - JRuby - C Ruby

    View Slide

  6. Native Threads
    vs
    Global Interpreter Lock

    View Slide

  7. require 'benchmark'
    ary = (1..1000000).to_a
    loop {
    puts Benchmark.measure {
    10.times {
    ary.each {|i|}
    }
    }
    }
    Unthreaded
    require 'benchmark'
    ary = (1..1000000).to_a
    loop {
    puts Benchmark.measure {
    (1..10).map {
    Thread.new {
    ary.each {|i|}
    }
    }.map(&:join)
    }
    }
    Multi-threaded

    View Slide

  8. Ruby 2.5.7 unthreaded
    JRuby unthreaded
    Ruby 2.5.7 threaded
    JRuby threaded

    View Slide

  9. Time (in seconds)
    0
    0.09
    0.18
    0.27
    0.36
    Unthreaded Threaded
    0.15
    0.23
    0.35
    0.34
    CRuby JRuby
    (lower is better)

    View Slide

  10. Virtual Machine
    vs
    Going It Alone

    View Slide

  11. Ruby Teams
    Hiro
    Marcin
    Nahi
    Subbu Douglas
    Christian Karol
    Tom
    Charlie Hiro
    charli
    Nahi
    zzak nurse
    hsbt matz
    ko1
    nobu
    OS (libc, ...)
    JRuby MRI

    View Slide

  12. Ruby Teams
    Hiro
    Marcin
    Nahi
    Subbu Douglas
    Christian Karol
    Tom
    Charlie Hiro
    charli
    Nahi
    zzak nurse
    hsbt matz
    ko1
    nobu
    OS (libc, ...)
    JRuby MRI
    JVM
    POSIX

    View Slide

  13. JVM
    Ruby Teams
    Hiro
    Marcin
    Nahi
    Subbu Douglas
    Christian Karol
    Tom
    Charlie Hiro
    charli
    Nahi
    zzak nurse
    hsbt matz
    ko1
    nobu
    OS (libc, ...)
    JRuby MRI
    Better ???
    POSIX

    View Slide

  14. !Perspective!
    ???
    Total control can be great!

    View Slide

  15. Shoulders of Giants
    JVM
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    Hiro
    Marcin
    Nahi
    Subbu Douglas
    Christian Dmitry
    Tom
    Charlie
    JRuby

    View Slide

  16. All the stuff!
    JVM
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    J. Rose
    Garbage
    Collection
    Native JIT
    Profiled
    Optimizations
    Native
    Threading
    Tooling Cross Platform

    View Slide

  17. Alternate JVMs
    • Hotspot: standard OpenJDK Java VM
    • OpenJ9: open source version of IBM's J9 JVM
    • New GC and JIT tuning options
    • Startup time and JIT code-caching features
    • GraalVM: replaces Hotspot JIT with Graal JIT
    • New JIT optimizations, ahead-of-time native compilation

    View Slide

  18. JVM is everywhere++!
    • Unix++, Windows
    • Exotic platforms: zLinux, OpenVMS, AS/400
    • Mobile: Android’s Dalvik, Embedded JVMs

    View Slide

  19. Java bytecode
    ==
    portability

    View Slide

  20. actionmailer-javamail, active_documentum, activerecord-jdbc-adapter, activerecord-jdbcdbf-adapter, activerecord-jdbcderby-adapter, activerecord-jdbch2-adapter,
    activerecord-jdbchsqldb-adapter, activerecord-jdbcmssql-adapter, activerecord-jdbcmysql-adapter, activerecord-jdbcpostgresql-adapter, activerecord-jdbcsqlite3-
    adapter, activerecord-netezza-adapter, activerecord-vertica-adapter, akephalos, akephalos-nerian, akephalos2, akka-actor-jars, akka-remote-jars,
    akubra_llstore_migrate, Antwrap, async-http-client-jars, atomic, atoulme-Antwrap, autotest-java, bbrowning-deltacloud-client, bbrowning-deltacloud-core, bcrypt-
    ruby, bee_java, berkeley-db-java-jars, bert, bio-maf, blockenspiel, boc, bond, bosdk, bouncy-castle-java, boxed-geminabox, brute-fuzzy, bryanl-gherkin, bson, buby,
    buildr, buildr-resolver, buildrizpack, butternut, capistrano-java, capybara-java_script_lint, carrierwave-neo4j, carrierwave_imagevoodoo, cassandra-jars, chrest,
    cloby, commons-io-jars, concurrently, contextual, coupler, cucumber-java, cucumber-jvm, cuke4duke, cuuid, dm-ldap-adapter, dm-lucene-adapter, do-jdbc_sqlserver,
    do_derby, do_h2, do_hsqldb, do_jdbc, do_mysql, do_openedge, do_oracle, do_postgres, do_sqlite3, do_sqlserver, doubleshot, dripdrop, dubious, duby,
    engineyard-visualvm, epall-limelight, errbit_zmq_handler, eurydice, euston, euston-daemons, euston-eventstore, euston-projections, euston-rabbitmq, euston-
    websites, eventmachine, excemel, faye-websocket, ffi, fig, file-find, fishwife, foreman, forkit, forkjoin, gamelan, gemshit, geoip-jars, get_back, gherkin, glassfish,
    gravitext-util, gravitext-xmlprod, grizzly, guava-jars, hadoop-find, hashdot-test-daemon, hiredis, hitimes, hope, hostor, hot_bunnies, hourglass, hpricot,
    http_parser.rb, inline_javascript, iudex, iudex-async-httpclient, iudex-barc, iudex-brutefuzzy-protobuf, iudex-brutefuzzy-service, iudex-char-detector, iudex-core,
    iudex-da, iudex-filter, iudex-html, iudex-http, iudex-http-test, iudex-httpclient-3, iudex-jetty-httpclient, iudex-rome, iudex-simhash, iudex-worker, ivy-jars, iyyov,
    jactive_support, java-autotest, java-inline, java2ruby-xmldsig, java_bin, java_inline, java_override, java_properties, java_streamify, java_testing_guff, javabean_xml,
    javaclass, javaeye4r, javagems, javajake, javaobj, javaobjs, javaobs, javaparse, javasand, javascript-securehash-rails, javascript-state-machine-rails,
    javascript_auto_include, javascript_eraser, javascript_features, javascript_i18n, javascript_localize, javascript_safe_logger, javascript_util_asset_pack, javascripto,
    javascripto-rails, jdbc-derby, jdbc-hsqldb, jdbc-jtds, jdbc-openedge, jdbc-openedge-internal, jdbc-postgres, jdbc-sqlite3, jedis-jars, jena-jruby, jessica, jettr, jetty, jetty-
    jsp, jgeoip, jms4r, jpdfer, jrack_handlers, jrtm, jruby-activemq, jruby-akka_jars, jruby-elasticsearch_jars, jruby-httpclient, jruby-launcher, jruby-management, jruby-
    metrics, jruby-pageant, jruby-vijava, jruby_gc_stats, jruby_sandbox, jruby_threach, jrubyconf-button, jsmetric4java, json, json-jruby, jsound, kb-activerecord-jdbc-
    adapter, kirk, kyotocabinet-java, ladle, latex-decode, launchy, libnotify, limelight, linecache, logback, logback-jars, looksee, lumix, mack-javascript, markdownj,
    maven_irb_plugin, metrics-core-jars, metrics-java, mguymon-buildr, mikka, mini_aether, mirah, mirah_model, miso-java, mixology, mm_mq, mongrel, msgpack-idl-
    java, msgpack-jruby, multimeter, naether, nanoc-javascript-concatenator, neo4j, neo4j-admin, neo4j-advanced, neo4j-community, neo4j-core, neo4j-enterprise,
    neo4j-spatial, neo4j-will_paginate, neo4j-wrapper, netty-jars, ning-compress-jars, nio4r, nokogiri, nokogiri-fitzsimmons, nokogiri-maven, nosqoop4u, ontomde-
    demo-java5, ontomde-java, ontomde-java-frontend, ontomde-uml2-java, ontomde-uml2-kbjava, open_nlp, pacer, pacer-dex, pacer-neo4j, pacer-orient, pelops-jars,
    persvr, pg_array_parser, protobuf-jars, pry, puma, qtjruby-core, qwirk_active_mq_adapter, qwirk_jms_adapter, rabbitmqadmin-cli, ragweed,
    rails_javascript_helpers, rakejava, rave, rcov, realityforge-jekyll, realityforge-jekylltask, redcar-bundles, redcar-clojure, redcar-filter-through-command, redcar-
    groovy, redcar-icons, redcar-javamateview, redcar-javascript, redcar-mirah, redcar-svnkit, redcar-xulrunner-win, RedCloth, refinerycms-javascripts, reigns, revo-
    nokogiri, rika, rjack-async-httpclient, rjack-commons-codec, rjack-commons-dbcp, rjack-commons-dbutils, rjack-commons-pool, rjack-httpclient-3, rjack-
    httpclient-4, rjack-icu, rjack-jackson, rjack-jdom, rjack-jets3t, rjack-jetty, rjack-jetty-jsp, rjack-jms, rjack-jms-spec, rjack-logback, rjack-lucene, rjack-maven, rjack-
    mina, rjack-nekohtml, rjack-protobuf, rjack-qpid-client, rjack-rome, rjack-slf4j, rjack-solr, rjack-tarpit, rjack-xerces, rmagick4j, rmodbus, rtm-javatmapi, rtm-
    majortom, rtm-ontopia, rtm-tinytim, rtm-tmql, rubeus, rubinius-core-api, ruby-blockcache, ruby-debug-base, ruby-maven, ruby2java, rubydoop, rubyjedi-
    nokogiri_java, scala-inline, scala-library-jars, scriptty, slf4j, slf4j-jars, slyphon-zookeeper, slyphon-zookeeper_jar, smackr, smartimage, SNMP4JR, solr_sail, spiegela-
    jruby-httpclient, sproutcore, spymemcached, sqldroid, steamcannon-deltacloud-client, steamcannon-deltacloud-core, stilts-stomp-client, supermarket, svm_toolkit,
    swt, theduke, thick, to-javascript, torquebox-base, torquebox-cache, torquebox-configure, torquebox-container-foundation, torquebox-core, torquebox-
    messaging, torquebox-messaging-container, torquebox-naming, torquebox-naming-container, torquebox-security, torquebox-server, torquebox-vfs, torquebox-
    web, twitter4j4r, UDJrb, unageanu-javaclass, unimidi, universe-javascript, url_escape, weakling, webbit-jars, wildnet-jackson, wildnet-netty, wildnet-server,
    wildsonet-hazelcast, wildsonet-netty, wildsonet-server, wildsonet-streamer, wrest, wrong, xdojava, xqruby, zookeeper
    No build tools!

    View Slide

  21. Ruby C Extensions
    vs
    Java Native Extensions

    View Slide

  22. Why Extensions?
    • Performance
    • Access Existing Library (e.g. openssl)

    View Slide

  23. C Extensions
    • API is massive
    • API is specific to C Ruby’s implementation
    • It is the implementation
    • Huge support cost (if non-CRuby wanted to support it)
    • At odds with concurrent Ruby execution

    View Slide

  24. Java Native Extensions
    • API is massive
    • API is specific to JRuby’s implementation
    • It is the implementation*
    • Allows Concurrent Ruby Execution
    • Support cost minimal
    • Common Gems Supported * We have plans for a formal API

    View Slide

  25. oj: Optimized json
    • Fast json parsing and dumping with many options
    • Common transitive dependency with its own API
    • Needed for many popular apps/libraries
    https://github.com/ohler55/oj

    View Slide

  26. oj for JRuby!
    • 10700 lines of Java (vs 20k lines of C)
    • Almost ready: 469 runs, 4883 assertions, 12 failures, 1 errors
    • 5 date/time differences
    • Streaming IO missing
    • No optimization work yet

    View Slide

  27. Load Performance
    0M
    0.3M
    0.6M
    0.9M
    1.2M
    small medium large
    0.13
    0.29
    1.1
    0.05
    0.11
    0.36
    0.06
    0.11
    0.72
    MRI (oj) JRuby (json) JRuby (oj)
    Millions of loads per second (higher is better)
    https://techblog.thescore.com/2014/05/23/benchmarking-json-generation-in-ruby/

    View Slide

  28. Dump Performance
    0
    0.75
    1.5
    2.25
    3
    small medium large
    0.44
    0.86
    2.3
    0.22
    0.44
    1.1
    0.33
    0.73
    2.1
    MRI (oj) JRuby (json) JRuby (oj)
    Million of dumps per second (higher is better)
    https://techblog.thescore.com/2014/05/23/benchmarking-json-generation-in-ruby/

    View Slide

  29. Scripting Java

    View Slide

  30. Scripting Java
    • Access Java libraries using a Ruby syntax
    • Or Scala, or Clojure, or Kotlin...
    • Alternatives to existing Ruby libraries
    • APIs which may not exist in Ruby
    • More options!

    View Slide

  31. JRuby IRB

    View Slide

  32. Brakeman Pro
    https://brakemanpro.com

    View Slide

  33. Fun Stuff
    event(:player_egg_throw) do |e|
    e.hatching = true
    e.num_hatches = 120
    e.player.mesg "hatched"
    end
    Purugin
    https://github.com/enebo/Purugin

    View Slide

  34. JRuby Performance

    View Slide

  35. Startup Time
    • Runtime optimizations give us excellent performance
    • ...eventually!
    • Startup time, warmup time are impacted
    • We continue working to reduce this impact
    • We compare some common commands

    View Slide

  36. JRuby Architecture
    Ruby (.rb)
    JIT
    Java Instructions
    (java bytecode)
    Ruby Instructions
    (IR)
    parse
    interpret
    interpreter
    interpret
    C1 compile
    native code better
    native code
    java
    bytecode
    interpreter
    execute
    C2 compile
    Java Virtual Machine
    JRuby Internals
    deoptimize
    Performance improves the longer your app runs

    View Slide

  37. 0s
    1.5s
    3s
    4.5s
    6s
    gem list
    5.025s
    0.678s
    CRuby JRuby --dev

    View Slide

  38. JRuby Flag: --dev
    • export JRUBY_OPTS="--dev"
    • Disables JRuby's JIT
    • Reduces JVM JIT
    • Don't use when benchmarking!
    • 30-40% reduction
    • More improvements coming
    total execution time (lower is better)
    0s
    1.5s
    3s
    4.5s
    6s
    gem list (200 gems)
    3.0s
    5.0s
    JRuby JRuby --dev

    View Slide

  39. 0s
    1.75s
    3.5s
    5.25s
    7s
    rails console
    6.7s
    4.96s
    CRuby JRuby --dev

    View Slide

  40. OpenJDK Class Data Sharing
    • Pre-validate and cache class data
    • Combined with --dev gives best current JRuby startup
    • http://blog.headius.com/2019/09/jruby-startup-time-exploration.html

    View Slide

  41. Java 13
    total execution time (lower is better)
    0s
    1.5s
    3s
    4.5s
    6s
    gem list (200 gems)
    2.84
    3.358
    5.589s
    2.962s
    5.025s
    JRuby JRuby --dev JRuby --dev --dev + CDS

    View Slide

  42. App Performance
    • Sinatra and Roda
    • https://github.com/CaDs/ruby_benchmarks
    • Comparing CRuby, JRuby, and TruffleRuby
    • RedMine bug tracker and wiki
    • https://www.redmine.org/
    • Real-world Rails application on JRuby, CRuby

    View Slide

  43. Peak Performance
    • After initial startup, warmup, data caching
    • JRuby generally gives better peak performance
    • Be aware of warmup time

    View Slide

  44. Sinatra and Roda requests/second (higher is better)
    0
    12500
    25000
    37500
    50000
    Sinatra Roda
    5,846
    4,257
    44,703
    42,468
    14,489
    12,284
    CRuby JRuby TruffleRuby

    View Slide

  45. Redmine Issue View, rendered and json (higher is better)
    0 req/s
    35 req/s
    70 req/s
    105 req/s
    140 req/s
    issues/13 issues/13.json
    137 req/s
    50 req/s
    91 req/s
    41 req/s
    CRuby 2.6.2 JRuby 9.2.9

    View Slide

  46. Warmup Time
    • Large applications take longer to warm up
    • Working on new JIT metrics, profiling to reduce this curve
    • Ahead-of-time compile to JVM bytecode should help

    View Slide

  47. Sinatra performance over time, requests/second
    0rps
    12500rps
    25000rps
    37500rps
    50000rps
    5s 10s 15s 20s 25s
    CRuby JRuby

    View Slide

  48. Roda performance over time, requests/second
    0rps
    12500rps
    25000rps
    37500rps
    50000rps
    5s 10s 15s 20s 25s
    CRuby JRuby

    View Slide

  49. Redmine issues/13.json warmup, one-minute cycles
    0
    35
    70
    105
    140
    1st 2nd 3rd 4th 5th 6th
    CRuby 2.6.2 JRuby 9.2.9

    View Slide

  50. Redmine memory usage (lower is better)
    0MB
    375MB
    750MB
    1125MB
    1500MB
    Memory usage for 8 workers vs 8 threads
    800MB
    1,430MB
    1,100MB
    CRuby JRuby (default) JRuby (300MB heap)

    View Slide

  51. Monitoring and Profiling

    View Slide

  52. JVM Tooling
    • Visual VM: monitor and profile threads, GC, managed heap
    • Mission Control: visualize Flight Recorder output
    • Flight Recorder: low-overhead system profiling in OpenJDK
    • async-profiler: command line profiles, flame graphs of CPU, alloc
    • All JVM tools work to monitor and profile JRuby apps

    View Slide

  53. VisualVM
    • Standalone graphical console for monitoring, profiling
    • Open sourced as part of OpenJDK project
    • https://visualvm.github.io/

    View Slide

  54. Visual VM and Visual GC

    View Slide

  55. JDK Flight Recorder
    • JVM flag: -XX:+FlightRecorder
    • JRuby flag: -J-XX:+FlightRecorder or put in JAVA_OPTS
    • Add --flight-recorder to JRuby launcher?
    • No overhead or profiling until you start recording
    • And usually less than 1% overhead for that

    View Slide

  56. JDK Mission Control
    • Control center and visualizer for Flight Recorder data
    • Start and manage recordings
    • Browse recorded data
    • https://adoptopenjdk.net/jmc.html

    View Slide

  57. View Slide

  58. View Slide

  59. async-profiler
    • JVM extension for lightweight sampled profiles
    • Install jruby-async-profiler gem
    • jruby -J-agentpath:/lib/libasyncProfiler.so
    • Simplified command lines coming soon
    • See https://github.com/jvm-profiling-tools/async-profiler

    View Slide

  60. View Slide

  61. Final Words

    View Slide

  62. During Development
    • JRUBY_OPTS=--dev
    • BE WARY of .ruby-version
    • Don’t share gem paths between Ruby implementations
    • Java and C extensions clobber each other

    View Slide

  63. Java 9+ Module System
    • New restrictions on access to core Java classes
    • You'll see warnings about JRuby or Ruby accesses
    • Config file for JVM flags: .jruby.java_opts
    • All your JVM options into a single file
    • Current dir, home dir, JRuby bin/ dir

    View Slide

  64. Java Modules and .jruby.java_opts
    WARNING: An illegal reflective access operation has occurred
    WARNING: Illegal reflective access by org.bouncycastle.jcajce.provider.drbg.DRBG (file:/Users/headius/.m2/repository/org/
    bouncycastle/bcprov-jdk15on/1.61/bcprov-jdk15on-1.61.jar) to constructor sun.security.provider.Sun()
    WARNING: Please consider reporting this to the maintainers of org.bouncycastle.jcajce.provider.drbg.DRBG
    WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
    WARNING: All illegal access operations will be denied in a future release
    --add-opens java.base/java.io=org.jruby.dist
    --add-opens java.base/java.nio.channels=org.jruby.dist
    --add-opens java.base/sun.nio.ch=org.jruby.dist
    --add-opens java.management/sun.management=org.jruby.dist

    View Slide

  65. Getting Help
    • JRuby on GitHub: https://github.com/jruby/jruby
    • Issues, Wiki
    • Chat with JRuby devs, users
    • jruby on Matrix
    • #jruby on Freenode IRC
    • Mailing list: https://lists.ruby-lang.org

    View Slide

  66. JRuby Workshop
    • Free-form "getting started" plus hackfest
    • Get JRuby installed and try it out
    • Test out your libraries
    • Try to get your apps running

    View Slide

  67. Thank You!
    • @headius, @tom_enebo
    • https://www.jruby.org
    • https://github.com/jruby/jruby
    • Chat with JRuby devs, users
    • #jruby on Freenode IRC
    • jruby on Matrix
    • Mailing list: https://lists.ruby-lang.org

    View Slide