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

Aloha Ruby Conference 2012

Aaron Patterson
October 08, 2012
1.6k

Aloha Ruby Conference 2012

Aaron Patterson

October 08, 2012
Tweet

Transcript

  1. AT&T, AT&T logo and all AT&T related marks are trademarks

    of AT&T Intellectual Property and/or AT&T affiliated companies.
  2. def fib(n) if n < 3 1 else fib(n-1) +

    fib(n-2) end end 4.times { fib(34) } Recursive Fib()
  3. def fib(n) if n < 3 1 else fib(n-1) +

    fib(n-2) end end 4.times.map { Thread.new { fib(34) } }.each(&:join) Threaded Fib()
  4. Slow Server server = WEBrick::GenericServer.new(:Port => 28561, :MaxClients => 4)

    server.start do |socket| { } until socket.gets == "\r\n" socket.print "HTTP/1.0 200 OK\r\n" socket.print "\r\n" sleep 0.5 socket.print "Hello World" socket.close end
  5. Slow Server server = WEBrick::GenericServer.new(:Port => 28561, :MaxClients => 4)

    server.start do |socket| { } until socket.gets == "\r\n" socket.print "HTTP/1.0 200 OK\r\n" socket.print "\r\n" sleep 0.5 socket.print "Hello World" socket.close end
  6. $ time ruby client.rb Hello World Hello World Hello World

    Hello World real 0m2.029s user 0m0.017s sys 0m0.012s $
  7. $ time ruby client.rb Hello World Hello World Hello World

    Hello World real 0m2.029s user 0m0.017s sys 0m0.012s $ 4 * 0.5
  8. require 'net/http' uri = URI('http://localhost:28561') 4.times.map { Thread.new { puts

    Net::HTTP.get_response(uri).body } }.each &:join Threaded Client
  9. $ time ruby client.rb Hello World Hello World Hello World

    Hello World real 0m0.525s user 0m0.016s sys 0m0.009s $
  10. $ time ruby client.rb Hello World Hello World Hello World

    Hello World real 0m0.525s user 0m0.016s sys 0m0.009s $ 0.525!
  11. threadsafe! ❤ Preload frameworks (enable) ❤ Cache classes (enable) ❤

    Dependency loading (disable) ❤ Allow concurrency (enable)
  12. Thread 2 Get Lock Read from Socket Process Stuff Write

    to Socket Release Lock Thread 1 Rack::Lock
  13. Thread 2 Get Lock Read from Socket Process Stuff Write

    to Socket Release Lock Rack::Lock
  14. Thread 2 Get Lock Read from Socket Process Stuff Write

    to Socket Release Lock Rack::Lock
  15. Fix #1 (eager init) class Foo class << self def

    hard_calculation @calc end end @calc = fib(34) end p Foo.hard_calculation
  16. Fix #2 (locking) class Foo @lock = Mutex.new class <<

    self def hard_calculation @lock.synchronize do @calc ||= fib(34) end end end end p Foo.hard_calculation
  17. Fix #2 (locking) class Foo @lock = Mutex.new class <<

    self def hard_calculation @lock.synchronize do @calc ||= fib(34) end end end end p Foo.hard_calculation
  18. Fix #2 (locking) class Foo @lock = Mutex.new class <<

    self def hard_calculation @lock.synchronize do @calc ||= fib(34) end end end end p Foo.hard_calculation
  19. Move to object class Foo def initialize @calc = fib(34)

    end def hard_calculation @calc end end Foo.new.hard_calculation
  20. Lazy Object class Foo include Mutex_m def hard_calculation synchronize do

    @calc ||= fib(34) end end end Foo.new.hard_calculation
  21. Maintain API class Foo include Mutex_m def hard_calculation synchronize do

    @calc ||= fib(34) end end Instance = new def self.hard_calculation Instance.hard_calculation end end Foo.hard_calculation
  22. Hash.new { } class Foo def initialize @cache = Hash.new

    { |h,k| h[k] = [] } end def some_value(key) @cache[key] end end
  23. Fix #1 (lock) class Foo include Mutex_m def initialize super

    @cache = Hash.new { |h,k| h[k] = [] } end def some_value(key) synchronize { @cache[key] } end end
  24. Constants SET_TWICE = 10 SET_TWICE = 10 ALSO_GLOBAL = {}

    ALSO_GLOBAL[:foo] ||= “bar” Warning No Warning
  25. Example class BrowserController < ApplicationController include ActionController::Live def index 100.times

    do response.stream.write "hello!\n" end response.stream.close end end
  26. Example class BrowserController < ApplicationController include ActionController::Live def index 100.times

    do response.stream.write "hello!\n" end response.stream.close end end Mix in Stream
  27. Our API def index response.status = 200 response.headers[‘X-Whatever’] = ‘<3’

    response.stream.write ‘hello’ response.stream.write ‘ world’ response.stream.close end
  28. Wrapped Request class Response attr_accessor :status attr_reader :headers, :stream def

    initialize @status = 200 @headers = {} @stream = StringIO.new end end def call(env) res = Response.new controller.response = res controller.index [res.status, res.headers, res.stream] end
  29. Threaded action def call(env) res = Response.new controller.response = res

    Thread.new { controller.index } [res.status, res.headers, res.stream] end
  30. Block until write def call(env) res = Response.new controller.response =

    res Thread.new { controller.index } res.stream.await [res.status, res.headers, res.stream] end
  31. Block until write def call(env) res = Response.new controller.response =

    res Thread.new { controller.index } res.stream.await [res.status, res.headers, res.stream] end Block
  32. Blocking Buffer class Buffer def initialize @latch = Latch.new @buffer

    = Queue.new end def await # wait for write @latch.await end def write(str) @latch.release @buffer << str end end
  33. Blocking Buffer class Buffer def initialize @latch = Latch.new @buffer

    = Queue.new end def await # wait for write @latch.await end def write(str) @latch.release @buffer << str end end `call` blocks here
  34. Blocking Buffer class Buffer def initialize @latch = Latch.new @buffer

    = Queue.new end def await # wait for write @latch.await end def write(str) @latch.release @buffer << str end end `write` unblocks
  35. Control Output class MyERB < ERB def set_eoutvar(compiler, eoutvar =

    '_erbout') compiler.put_cmd = "#{eoutvar}.write" compiler.insert_cmd = "#{eoutvar}.write" compiler.pre_cmd = [] compiler.post_cmd = [] end end doc = MyERB.new '<%= hello %> world', nil, nil, '$stdout' puts doc.src
  36. SSE Response HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block

    X-Content-Type-Options: nosniff Content-Type: text/event-stream Transfer-Encoding: chunked event: ping data: {"ping":"2012-10-06T21:44:41-07:00"} event: reload data: {"changed":["/Users/aaron/git/lolwut/app/views/ users/"]}
  37. SSE Response HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block

    X-Content-Type-Options: nosniff Content-Type: text/event-stream Transfer-Encoding: chunked event: ping data: {"ping":"2012-10-06T21:44:41-07:00"} event: reload data: {"changed":["/Users/aaron/git/lolwut/app/views/ users/"]}
  38. SSE Response HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block

    X-Content-Type-Options: nosniff Content-Type: text/event-stream Transfer-Encoding: chunked event: ping data: {"ping":"2012-10-06T21:44:41-07:00"} event: reload data: {"changed":["/Users/aaron/git/lolwut/app/views/ users/"]}
  39. SSE Response HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block

    X-Content-Type-Options: nosniff Content-Type: text/event-stream Transfer-Encoding: chunked event: ping data: {"ping":"2012-10-06T21:44:41-07:00"} event: reload data: {"changed":["/Users/aaron/git/lolwut/app/views/ users/"]}
  40. Client Side jQuery(document).ready(function() { setTimeout(function() { var source = new

    EventSource('/control'); // if we get a reload command, reload the page source.addEventListener('reload', function(e) { window.location.reload(); }); }, 1); });
  41. Client Side jQuery(document).ready(function() { setTimeout(function() { var source = new

    EventSource('/control'); // if we get a reload command, reload the page source.addEventListener('reload', function(e) { window.location.reload(); }); }, 1); });
  42. Client Side jQuery(document).ready(function() { setTimeout(function() { var source = new

    EventSource('/control'); // if we get a reload command, reload the page source.addEventListener('reload', function(e) { window.location.reload(); }); }, 1); });
  43. Client Side jQuery(document).ready(function() { setTimeout(function() { var source = new

    EventSource('/control'); // if we get a reload command, reload the page source.addEventListener('reload', function(e) { window.location.reload(); }); }, 1); });