Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scaling Ruby Applications with JRuby

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


February 07, 2020

More Decks by headius

Other Decks in Programming


  1. JRuby • 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 • coming out next week? next (2.6.x)
  2. 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
  3. 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)
  4. Ruby Teams Hiro Marcin Nahi Subbu Douglas Christian Karol Tom

    Charlie Hiro charli Nahi zzak nurse hsbt matz ko1 nobu OS (libc, ...) JRuby MRI
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. JVM is everywhere++! • Unix++, Windows • Exotic platforms: zLinux,

    OpenVMS, AS/400 • Mobile: Android’s Dalvik, Embedded JVMs
  11. 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!
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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/
  17. 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/
  18. 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!
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. Peak Performance • After initial startup, warmup, data caching •

    JRuby generally gives better peak performance • Be aware of warmup time
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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)
  32. 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
  33. VisualVM • Standalone graphical console for monitoring, profiling • Open

    sourced as part of OpenJDK project • https://visualvm.github.io/
  34. 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
  35. JDK Mission Control • Control center and visualizer for Flight

    Recorder data • Start and manage recordings • Browse recorded data • https://adoptopenjdk.net/jmc.html
  36. async-profiler • JVM extension for lightweight sampled profiles • Install

    jruby-async-profiler gem • jruby -J-agentpath:<jruby home>/lib/libasyncProfiler.so • Simplified command lines coming soon • See https://github.com/jvm-profiling-tools/async-profiler
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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