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

JRuby: Zero to Scale

headius
November 20, 2019

JRuby: Zero to Scale

JRuby is deployed by hundreds of companies, running Rails and other services at higher speeds and with better scalability than any other runtime. With JRuby you get better utilization of system resources, the performance and tooling of the JVM, and a massive collection of libraries to add to your toolbox.

In this talk, we'll cover:

* Getting started on JRuby
* Comparison to CRuby
* Building, migrating, and deploying apps
* Tuning, profiling, and monitoring
* Scaling considerations

headius

November 20, 2019
Tweet

More Decks by headius

Other Decks in Programming

Transcript

  1. JRuby: Zero to Scale
    Thomas E. Enebo (@tom_enebo)
    Charles O. Nutter (@headius)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  4. JRuby 9.2.9.0
    • Lots of memory and startup time 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

    View full-size slide

  5. Getting JRuby in 3 Steps

    View full-size slide

  6. Step 1: You Need a JVM
    • Java (version 8 or 11 recommended)
    • Is it installed? java -version
    • If !installed
    • package install: dnf/apt install java
    • explicit install: http://adoptopenjdk.net

    View full-size slide

  7. Step 2: Install JRuby

    View full-size slide

  8. Step 2: Install JRuby on Windows
    www.jruby.org

    View full-size slide

  9. Step 3: Profit!

    View full-size slide

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

    View full-size slide

  11. Native Threads
    vs
    Global Interpreter Lock

    View full-size slide

  12. 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 full-size slide

  13. Ruby 2.5.7 unthreaded
    JRuby unthreaded
    Ruby 2.5.7 threaded
    JRuby threaded

    View full-size slide

  14. 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 full-size slide

  15. Virtual Machine
    vs
    Going It Alone

    View full-size slide

  16. 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 full-size slide

  17. 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 full-size slide

  18. 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 full-size slide

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

    View full-size slide

  20. 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 full-size slide

  21. 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 full-size slide

  22. 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 full-size slide

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

    View full-size slide

  24. Java bytecode
    ==
    portability

    View full-size slide

  25. 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 full-size slide

  26. Ruby C Extensions
    vs
    Java Native Extensions

    View full-size slide

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

    View full-size slide

  28. C Extensions
    • API is massive
    • API is specific to C Ruby’s implementation
    • It is the implementation
    • At odds with concurrent Ruby execution
    • Huge support cost

    View full-size slide

  29. 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 full-size slide

  30. 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 full-size slide

  31. oj for JRuby!
    • 9200 lines of Java (vs 20k lines of C)
    • Almost ready: 448 runs, 765 assertions, 43 failures, 12 errors
    • 35 F/E from minor features, date/time diffs

    View full-size slide

  32. 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 full-size slide

  33. 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 full-size slide

  34. Scripting Java

    View full-size slide

  35. Scripting Java
    • Access Java Classes using a Ruby Syntax
    • Java Libraries
    • Give alternatives to existing Ruby Libraries
    • Give access to APIs which do not exist in Ruby

    View full-size slide

  36. 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 full-size slide

  37. JRuby Performance

    View full-size slide

  38. 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 full-size slide

  39. 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 full-size slide

  40. 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.15s
    2.3s
    3.45s
    4.6s
    gem list (~350 gems)
    3.0s
    4.6s
    JRuby JRuby --dev

    View full-size slide

  41. 0
    1
    2
    3
    4
    gem list
    3.05
    0.939
    CRuby JRuby --dev

    View full-size slide

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

    View full-size slide

  43. OpenJDK Class Data Sharing
    • Pre-validate and cache class data
    • Save frequently-accessed data over time
    • Combined with --dev gives best current JRuby startup

    View full-size slide

  44. Java 11
    total execution time (lower is better)
    0
    1.75
    3.5
    5.25
    7
    gem list (~350 gems)
    JRuby --dev --dev + CDS

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  48. 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 full-size slide

  49. Warmup Time
    • Large applications take longer to warm up
    • Working on new JIT metrics, profiling to reduce this curve

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  52. 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 full-size slide

  53. 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 full-size slide

  54. Migrating An App

    View full-size slide

  55. Migration Process
    • Existing application Activities:
    • Configuration Changes (e.g. config/database.yml)
    • C extensions replacement
    • Thread-safety audit

    View full-size slide

  56. Use-Case: Discourse
    • “A platform for community discussion”
    • Very large, well-known Rails application
    • >500 gems
    • 250,000 lines of Ruby
    • JRuby is not currently supported
    • But it is almost working!

    View full-size slide

  57. Step 1: jruby-lint

    View full-size slide

  58. jruby-lint gem
    See how ready your Ruby code is to run on JRuby
    $ gem install jruby-lint

    $ cd my-app

    $ jrlint

    View full-size slide

  59. [] ~/projects/discourse $ jrlint
    JRuby-Lint version 0.9.0
    ./Gemfile:: [gems, info] For more on gem compatibility see http://wiki.jruby.org/C-Extension-Alternatives
    ./Gemfile:80: [gems, warning] Found gem 'oj' which is reported to have some issues:
    Try `gson`, `json` or `json_pure` instead.|
    gem 'oj'
    ./Gemfile:81: [gems, warning] Found gem 'pg' which is reported to have some issues:
    Use activerecord-jdbcpostgresql-adapter instead or pg_jruby (drop-in replacement).|
    gem 'pg'
    ./Gemfile:187: [gems, warning] Found gem 'mysql2' which is reported to have some issues:
    Use activerecord-jdbcmysql-adapter.|
    gem 'mysql2'
    ./Gemfile:188: [gems, warning] Found gem 'redcarpet' which is reported to have some issues:
    Same as with **RDiscount** use alternatives such as kramdown, Maruku or markdown_j|
    gem 'redcarpet'
    ./Gemfile:189: [gems, warning] Found gem 'sqlite3' which is reported to have some issues:
    Use activerecord-jdbcsqlite3-adapter.|
    gem 'sqlite3', '~> 1.3.13'
    ./app/mailers/user_notifications.rb:152: [nonatomic, warning] Non-local operator assignment (@popular_topics) is not guaranteed to be atomic.
    @popular_topics = topics_for_digest[0, SiteSetting.digest_topics]
    ./app/mailers/user_notifications.rb:630: [nonatomic, warning] Non-local operator assignment (@site_name) is not guaranteed to be atomic.
    @site_name = SiteSetting.email_prefix.presence || SiteSetting.title # used by I18n
    ./app/models/report.rb:21: [nonatomic, warning] Non-local operator assignment (@start_date) is not guaranteed to be atomic.
    @start_date ||= Report.default_days.days.ago.utc.beginning_of_day
    ./app/models/report.rb:22: [nonatomic, warning] Non-local operator assignment (@end_date) is not guaranteed to be atomic.
    @end_date ||= Time.now.utc.end_of_day
    ./app/models/admin_dashboard_next_data.rb:17: [nonatomic, warning] Non-local operator assignment (@json) is not guaranteed to be atomic.
    @json ||= get_json
    ./app/models/topic_posters_summary.rb:31: [nonatomic, warning] Non-local operator assignment (@descriptions_by_id) is not guaranteed to be atomic.
    @descriptions_by_id ||= begin
    ./app/models/topic_posters_summary.rb:33: [nonatomic, warning] Non-local operator assignment (descriptions[id]) is not guaranteed to be atomic.
    descriptions[id] ||= []
    ./app/models/topic_posters_summary.rb:79: [nonatomic, warning] Non-local operator assignment (@avatar_lookup) is not guaranteed to be atomic.
    @avatar_lookup ||= options[:avatar_lookup] || AvatarLookup.new(user_ids)
    ./app/models/topic_posters_summary.rb:83: [nonatomic, warning] Non-local operator assignment (@primary_group_lookup) is not guaranteed to be atomic.
    @primary_group_lookup ||= options[:primary_group_lookup] || PrimaryGroupLookup.new(user_ids)
    ./app/models/directory_item.rb:8: [nonatomic, warning] Non-local operator assignment (@headings) is not guaranteed to be atomic.
    @headings ||= [:likes_received,
    ./app/models/directory_item.rb:18: [nonatomic, warning] Non-local operator assignment (@types) is not guaranteed to be atomic.
    @types ||= Enum.new(all: 1,
    ./app/models/group_history.rb:11: [nonatomic, warning] Non-local operator assignment (@actions) is not guaranteed to be atomic.
    @actions ||= Enum.new(
    ./app/models/locale_site_setting.rb:10: [nonatomic, warning] Non-local operator assignment (@values) is not guaranteed to be atomic.
    @values ||= supported_locales.map do |locale|
    ./app/models/locale_site_setting.rb:25: [nonatomic, warning] Non-local operator assignment (@language_names) is not guaranteed to be atomic.
    @language_names ||= begin
    ./app/models/locale_site_setting.rb:41: [nonatomic, warning] Non-local operator assignment (@supported_locales) is not guaranteed to be atomic.
    @supported_locales ||= begin
    ./app/models/category.rb:98: [nonatomic, warning] Non-local operator assignment (TOPIC_CREATION_PERMISSIONS) is not guaranteed to be atomic.
    TOPIC_CREATION_PERMISSIONS ||= [:full]
    ./app/models/category.rb:99: [nonatomic, warning] Non-local operator assignment (POST_CREATION_PERMISSIONS) is not guaranteed to be atomic.
    POST_CREATION_PERMISSIONS ||= [:create_post, :full]
    ./app/models/category.rb:109: [nonatomic, warning] Non-local operator assignment (@topic_id_cache) is not guaranteed to be atomic.
    @topic_id_cache = DistributedCache.new('category_topic_ids')
    ./app/models/category.rb:228: [nonatomic, warning] Non-local operator assignment (@@cache) is not guaranteed to be atomic.
    @@cache ||= LruRedux::ThreadSafeCache.new(1000)
    ./app/models/category.rb:520: [nonatomic, warning] Non-local operator assignment (@has_children) is not guaranteed to be atomic.
    @has_children ||= (id && Category.where(parent_category_id: id).exists?) ? :true : :false

    View full-size slide

  60. Unsupported Extensions
    ./Gemfile:: [gems, info] For more on gem compatibility see http://wiki.jruby.org/C-Extension-Alternatives
    ./Gemfile:80: [gems, warning] Found gem 'oj' which is reported to have some issues:
    Try `gson`, `json` or `json_pure` instead.|
    gem 'oj'
    ./Gemfile:81: [gems, warning] Found gem 'pg' which is reported to have some issues:
    Use activerecord-jdbcpostgresql-adapter instead or pg_jruby (drop-in replacement).|
    gem 'pg'
    ./Gemfile:187: [gems, warning] Found gem 'mysql2' which is reported to have some issues:
    Use activerecord-jdbcmysql-adapter.|
    gem 'mysql2'
    ./Gemfile:188: [gems, warning] Found gem 'redcarpet' which is reported to have some issues:
    Same as with **RDiscount** use alternatives such as kramdown, Maruku or markdown_j|
    gem 'redcarpet'
    ./Gemfile:189: [gems, warning] Found gem 'sqlite3' which is reported to have some issues:
    Use activerecord-jdbcsqlite3-adapter.|
    gem 'sqlite3', '~> 1.3.13'

    View full-size slide

  61. Threading Concerns
    ./app/models/report.rb:21: [nonatomic, warning] Non-local operator assignment (@start_date) is
    not guaranteed to be atomic.
    @start_date ||= Report.default_days.days.ago.utc.beginning_of_day
    ./app/models/report.rb:22: [nonatomic, warning] Non-local operator assignment (@end_date) is not
    guaranteed to be atomic.
    @end_date ||= Time.now.utc.end_of_day
    ./app/models/admin_dashboard_next_data.rb:17: [nonatomic, warning] Non-local operator assignment
    (@json) is not guaranteed to be atomic.
    @json ||= get_json
    ./app/models/topic_posters_summary.rb:31: [nonatomic, warning] Non-local operator assignment
    (@descriptions_by_id) is not guaranteed to be atomic.
    @descriptions_by_id ||= begin
    ./app/models/topic_posters_summary.rb:33: [nonatomic, warning] Non-local operator assignment
    (descriptions[id]) is not guaranteed to be atomic.
    descriptions[id] ||= []
    ./app/models/topic_posters_summary.rb:79: [nonatomic, warning] Non-local operator assignment
    (@avatar_lookup) is not guaranteed to be atomic.
    @avatar_lookup ||= options[:avatar_lookup] || AvatarLookup.new(user_ids)
    ./app/models/topic_posters_summary.rb:83: [nonatomic, warning] Non-local operator assignment
    (@primary_group_lookup) is not guaranteed to be atomic.
    @primary_group_lookup ||= options[:primary_group_lookup] || PrimaryGroupLookup.new(user_ids)
    @language_names) is not guaranteed to be atomic.
    @language_names ||= begin

    View full-size slide

  62. Unsupported Features
    ./script/measure.rb:48: [objectspace, warning]
    Use of ObjectSpace is expensive and disabled
    by default. Use -X+O to enable.
    ObjectSpace.each_object do |o|
    ./script/check_forking.rb:14: [fork, error]
    Kernel#fork is not implemented on JRuby.
    child = fork do
    ./script/check_forking.rb:17: [fork, error]
    Kernel#fork is not implemented on JRuby.
    grand_child = fork do

    View full-size slide

  63. Step 2: Replace C Extensions

    View full-size slide

  64. % bundle install
    Fetching pg 1.1.4
    Installing pg 1.1.4 with native extensions
    Gem::Ext::BuildError: ERROR: Failed to build gem native extension
    gem 'pg'
    Gemfile Gemfile
    gem 'pg', platform: :mri
    gem 'activerecord-jdbcpostgresql-adapter', platform: :jruby

    View full-size slide

  65. Missing C Extension
    1.Remove it if not needed (development dep like byebug)
    2.Use a pure-Ruby version if performance is good enough
    3.Call into a native library using FFI (Foreign Function Interface)
    4.“Script” a Java library
    5.Write a JRuby extension

    View full-size slide

  66. Step 3: Run It
    • Once your application bundles, it should run!
    • If not talk to us or file an issue
    puma -t 20:20 -e production

    View full-size slide

  67. Monitoring and Profiling

    View full-size slide

  68. 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 full-size slide

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

    View full-size slide

  70. Visual VM and Visual GC

    View full-size slide

  71. 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 full-size slide

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

    View full-size slide

  73. 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 full-size slide

  74. 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 full-size slide

  75. 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 full-size slide

  76. Java Modules
    • Java 9+ introduced Java Modules
    • You'll see warnings about JRuby or Ruby accesses
    • New config file for JVM flags: .jruby.java_opts
    • Gather JVM options into a single file
    • Current dir, home dir, JRuby bin/ dir

    View full-size slide

  77. 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 full-size slide

  78. Let us know if you have any
    questions!

    View full-size slide

  79. 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 full-size slide