Big Ruby Conf Bonus tracks

Big Ruby Conf Bonus tracks

A whirlwind tour of Tony Arcieri's Celluloid and Eric Lindvall's Metriks


Adam Keys

March 01, 2013


  1. You should know Celluloid and Metriks Adam Keys @therealadam

    March 2013
  2. Concurrent objects in Ruby

  3. What’s an actor model? Actor "daryl" Object ralph ... ...

    frob frob grok Thread 1 Actor "larry" Object B ... frob frob grok grok Thread 2
  4. Celluloid actors require 'redis' require 'celluloid' class RedisAdapter include Celluloid

    def initialize(redis) @redis = redis end def record(path) redis.incr("stats:#{path}") end def fetch(path) redis.get("stats:#{path}") end protected attr_reader :redis end
  5. Celluloid actors require 'celluloid' require 'json' class Stats include Celluloid

    def handle(msg) log msg case msg when %r{^VISIT (.*)$} path = $1 # regex globals :( redis.async.record(path) "OK\n" when %r{^STATS (.*)$} path = $1 # regex globals :( stats = redis.fetch(path) JSON.dump(stats: {path => stats}) else "ERR\n" end end def log(msg) end def redis Celluloid::Actor[:redis] end end
  6. Reliability and Supervision Groups class Collector < Celluloid::SupervisionGroup supervise Stats,

    as: :stats supervise RedisAdapter, as: :redis, args: [] supervise Listener, as: :listener, args: ["", 4000] end if __FILE__ == $0 collector =! trap("INT") { collector.terminate; exit } sleep end
  7. I/O actors require 'celluloid/io' class Listener include Celluloid::IO def initialize(host,

    port) log "Starting on #{host}:#{port}" @server =, port) end def finalize return unless @server @server.close end def run loop { async.handle_connection(@server.accept) } end def handle_connection(socket) _, host, port = socket.peeraddr log "#{host}: #{port} connected" msg = socket.readpartial(4096) response = stats.handle(msg) socket.write(response) socket.close rescue EOFError log "#{host}: #{port} disconnected" socket.close end def log(msg) puts msg end def stats Celluloid::Actor[:stats] end end
  8. More reading •Erlang - •D-Cell - •On Ruby

    concurrency: • started-with-ruby-concurrency-using-two- simple-classes/ • that-state-locks-monitors-and-atomics/ • for-concurrency/ •Java Concurrency in Practice
  9. Knowing your production systems

  10. All arguments are shallow given numbers

  11. Counters, stats, et. al. require 'metrics' $api_hits = Metriks.counter('api_hits') $api_hits.increment

    require 'metriks/reporter/logger' reporter = Metriks::Reporter::Logger(logger:'log/metrics.log')) reporter.start $api_stats = Metriks.timer('api_stats') $api_stats.time do # … work … end
  12. 1. Logfiles I, [2013-02-18T07:00:00.006093 #50406] INFO -- : metriks: time=1361192400

    name=stats type=timer count=92440 one_minute_rate=434.4299373735234 five_minute_rate= 220.682764284654 fifteen_minute_rate=90.15828364936169 mean_rate=423.37823163275544 min=0.000362023 max=0.070315041 mean=0.0007417273702509757 stddev=2.4841137753254027e-06 median=0.000614221 95th_percentile=0.0011458962499999996 I, [2013-02-18T07:00:00.009590 #50406] INFO -- : metriks: time=1361192400 name=api_stats type=timer count=92440 one_minute_rate=434.4299373735234 five_minute_rate=220.682764284654 fifteen_minute_rate=90.15828364936169 mean_rate=423.3715240226875 min=0.000300078 max=0.070015888 mean=0.0006167874175681445 stddev=2.4013694294628166e-06 median=0.000505336 95th_percentile=0.0009915237499999996
  13. 2. Convert to CSV #!/usr/bin/env ruby require 'csv' columns =

    %w{name time mean_rate 95th_percentile} out = CSV.generate do |csv| csv << columns.to_a ARGF.each_line do |l| fields = l.split next if fields[0] != 'I,' metrics = fields.slice(7..-1).inject({}) do |hsh, str| name, value = str.split('=') hsh.update(name => value) end csv << columns.inject([]) { |row, name| row << metrics[name] } end end puts out
  14. 3. Spreadsheets!

  15. Dashboard services

  16. More reading •Metrics, the original library - http:// •Metrics

    everywhere, the slides - http:// metrics-everywhere.pdf •Metrics everywhere, the talk - http://
  17. Thanks!