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

Aloha Ruby Conference 2012

Avatar for Aaron Patterson Aaron Patterson
October 08, 2012
1.7k

Aloha Ruby Conference 2012

Avatar for Aaron Patterson

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); });