JRuby (on Rails) Tacks Knacks

http://kares.org/jrubyconf-2014

Down Key or Swipe Up

by Karol Bucek

What is it, Precious?

... what does 'J' stand for in JRuby ?

  • JVM, Java, JIT Ruby
  • Jep, Jo, Juppie Ruby
  • Japan, Jumbo, J.hovah's Ruby

It's Just Ruby!

EuRuKo 2008 (Prague)

The 'J' Letter

we've come a long way (since)

  • Java was really "terrifying" (just watch the talk)
  • JRuby (massively) helped the Java != JVM transition
  • still a lot of folks ignore the JVM - "its not for them"
  • please do share your J(Ruby) stories ...
  • ... deploying with JRuby is the best thing since Brötchen

Keep it MRI Compatible

it will pay off (and not just due faster tests)

  • promotes abstraction from 3rd party APIs
  • re-implement or stub simple platform specific APIs
  • seems like a waste of time (as with tests)
  • ... you will definitely thank yourself (later)

serialport gem's API - simple to re-implement

Do not start Threads

on your own unless you absolutely must

  • starting an "occasional" (daemon) thread (on boot) is 'fine'
  • request (server) performance will suffer as load increases
  • if you "must" do async tasks from requests use a thread pool
  • if a 3rd party gem does Thread.new you better deal with it
  • ... might want to try out: jruby.thread.pool.enabled=true

a (simplified) piece of ActionController::Live::Response

Thread.new

"hard" to integrate with an async-capable server

hide the async nature of the response "under" a middleware

there's (still) no "official" rack env['async.callback'] support

Rails.logger ain't thread-safe

silencing the logger is still not thread-safe (and maybe never will be)

  • default logger might raise it's level and you won't see log entries
  • esp. true on JRuby but can be reproduced on MRI as well (e.g. using Puma)
  • Rails.logger.silencer is on by default (even on production)

on 4.x this gets included into ActiveSupport::Logger

Rails.logger ain't thread-safe

  • this (is an old) "feature" that dates back to Rails 2.3
  • been reported/discussed/changed several times - same code persists
  • interestingly logger.silence got deprecated on 3.2 (for removal)
  • but got re-introduces as a re-usable mixin on 4.0 for all loggers :)

assumes 4.x ... but similar code works for old Rails as well

  • be aware if gems create and use "custom" loggers
  • ... there's more things that are better NOT silenced
  • just think twice whenever there's a silence_xxx { ... }
  • even if Rails does not silence or silence_stream, gems might!

ActiveRecord Pooling

ActiveRecord::ConnectionAdapter::ConnectionPool

  • "forcibly" (no way to avoid) introduced in Rails 2.2 - 2008 yay :)
  • old ActiveRecord (2.3) had issues with "leaking" a connection
  • in 3.2 pool received a lot of attention - still "too" synchronized
  • 2012 @thedarkone starts with improvements (end up in 4.x)

very appreciated step but took quite some time (4.0) to "smooth" out

SUMMARY: lots of (concurrency) issues ... along the way

ActiveRecord Pooling

pool might often be the performance bottleneck

  • first test your server under load (sometimes leads to surprising results)
  • set pool: size high >= 100 (depending on server's max-requests)
  • consider config.middleware.delete 'ActiveRecord::QueryCache'

this middleware forces each request to check-out a connection

ActiveRecord Pooling

what if the pool stands in your way

  • using external pools (not just Java but C pools)
  • duplicate configuration - 2 pool sizes, checkout timeouts
  • with AR-JDBC most common case: jndi: jdbc/MyDS
  • maximizing pool performance - fine grained parameter tuning
  • scaling in a connection limiting environment (e.g. Heroku)

ActiveRecord-Bogacs

pooling "hacks" ... in a relaxed 'spa' fashion!

http://github.com/kares/activerecord-bogacs

ActiveRecord-Bogacs

NOTE: if unsure GENTLY REFUSE!

this is an (early-stage) experiment for now

ActiveRecord::Bogacs::FalsePool

  • does not pool that much (ignores pool: size and similar)
  • for AR's sanity manages a (non-limited) thread to connection mapping
  • the underlying connection pool is expected to be configured by the user
  • should suit JRuby's ActiveRecord jndi: jdbc/MyDS case perfectly
  • (maybe) start pushing with AR-JDBC (sounds like a bad idea for now)

ActiveRecord::Bogacs::DefaultPool

  • based on recent AR pool but with an API compatible for older versions
  • some useful updates related to configuration might be expected
  • e.g. pool initial: size - very easy to support and quite useful in the cloud
  • fixing Thread.current.object_id issue with JRuby's thread-backed fibers

ActiveRecord::Bogacs::ShareablePool

NOTE: the craziest thing - BE VERY TENDER!

  • allows a database connection to be shared between threads
  • very BAD idea except for read-only SQL over a thread-safe connection
  • a shared block is explicitly marked - using with_shared_connection
  • potential use-case - cold cache warmup for a highly concurrent app

Wrap IT Up

  • abstract away from Ruby platform specifics and keep it MRI compatible
  • do not start threads on your own (during requests) - check your gems if they do
  • double check your logger - make sure you have predictable logging on production
  • understand your concurrency requirements and adjust your pools accordingly

Danke sch♡n!



... and have fun with JRuby (on Rails)



hand-(c)rafted for JRubyConf 2014 by http://kares.org