rubyjrubycelluloidsecure-random

require 'celluloid' takes a long time to load


I have a JRuby application deployed on Google compute engine. I noticed that it takes a while to load the celluloid gem. After looking into more details, I found that it is taking more time to load celluloid/internals/stack.

Usually, it takes more than 2 mins. Once it even took around 41 mins to load that. The server has a good configuration with 15Gb RAM and 4 cores.

JAVA version

java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

JRUBY version

jruby 9.0.5.0 (2.2.3) 2016-01-26 7bee00d Java HotSpot(TM) 64-Bit Server VM 25.74-b02 on 1.8.0_74-b02 +jit [linux-amd64]

enter image description here


When I tried same with ruby it loads celluloid quickly.


Solution

  • Install haveged to replenish entropy faster on virtual machines.

    You probably don't have enough entropy to generate UUID's pulled from SecureRandom which under jRuby runs out of randomness easily. Especially on virtual machines. So your virtual machine is literally waiting for more randomness to be possible. haveged helps replenish that randomness lightening fast by collecting new kinds of entropy.

    Installing...

    Download: http://www.issihosts.com/haveged/downloads.html

    Under Debian-like Linux flavors like Mint and Ubuntu, this gets that done:

    sudo apt-get install haveged
    

    This is a known issue with SecureRandom under jRuby ...

    In the past I had this issue also, and had to use strace to locate the issue. Notice this file, which is the one loaded just before the line you mentioned in your question ... internals/uuid.rb:

    require "securerandom"
    
    module Celluloid
      module Internals
        # Clearly Ruby doesn't have enough UUID libraries
        # This one aims to be fast and simple with good support for multiple threads
        # If there's a better UUID library I can use with similar multithreaded
        # performance, I certainly wouldn't mind using a gem for this!
        module UUID
          values = SecureRandom.hex(9).match(/(.{8})(.{4})(.{3})(.{3})/)
          PREFIX = "#{values[1]}-#{values[2]}-4#{values[3]}-8#{values[4]}".freeze
    
          #de ...
        end
      end
    end
    

    That was the offending code, because it generates a 9 hex-digit string which it can use as a prefix for UUID codes... using SecureRandom.

    Then later, there are uses of that via the Celluloid::Internals::UUID.generate method. But at load-time, the Celluloid::Internals::UUID module performs operations which require SecureRandom ... which jRuby has had trouble with: