Multi-threaded servers compete for the global interpreter lock (GIL) and incur the cost of continuous context switching, potential deadlocks, or plain wasted cycles. Asynchronous servers, on the other hand, create a mess of callbacks and errbacks, complicating the code. But, what if, you could get all the benefits of asynchronous programming, while preserving the synchronous look and feel of the code – no threads, no callbacks?
11. Mongo Couch MySQL PSQL … Drivers Threads Ruby VM GIL Fibers … Network Mongrel Unicorn Passenger …
12. 2 Mongo Couch MySQL PSQL … Drivers Threads Ruby VM 1 4 GIL Fibers … Network Mongrel Unicorn 3 We’re as fast as the slowest component Passenger …
13. Global Interpreter Lock is a mutual exclusion lock held by a programming language interpreter thread to avoid sharing code that is not thread-safe with other threads. There is always one GIL for one interpreter process. Concurrency is a myth in Ruby (with a few caveats, of course) http://bit.ly/ruby-gil
14. N-M thread pool in Ruby 1.9… Better but still the same problem! Concurrency is a myth in Ruby still no concurrency in Ruby 1.9 http://bit.ly/ruby-gil
15. Nick – tomorrow @ 11:45am Concurrency is a myth in Ruby still no concurrency in Ruby 1.9 http://bit.ly/ruby-gil
16. Blocks entire Ruby VM Not as bad, but avoid it still.. Avoid locking interpreter threads at all costs let’s say you’re writing an extension…
19. Blocking calls to mysql_real_query mysql_real_query requires an OS thread Blocking on mysql_real_query blocks the Ruby VM Aka, “select sleep(1)” blocks the entire Ruby runtime for 1s (ouch) gem install mysqlwhat you didn’t know…
20. static VALUE async_query(intargc, VALUE* argv, VALUE obj) { ... send_query( obj, sql ); ... schedule_query( obj, timeout); ... returnget_result(obj); } staticvoidschedule_query(VALUEobj, VALUE timeout) { ... structtimevaltv = { tv_sec: timeout, tv_usec: 0 }; for(;;){ FD_ZERO(&read); FD_SET(m->net.fd, &read); ret = rb_thread_select(m->net.fd + 1, &read, NULL, NULL, &tv); ... if (m->status == MYSQL_STATUS_READY) break; } } send query and block Ruby: select() = C: rb_thread_select() mysqlplus.gem under the hood
25. *nix IPC is fast! Woo! … Full Ruby VM An exclusive Ruby VM for EACH request am I the only one who thinks this is terrible?
26. “Does not care if your application is thread-safe or not, workers all run within their own isolated address space and only serve one client at a time for maximum robustness.” Robustness? That sounds like a bug. An exclusive Ruby VM for EACH request am I the only one who thinks this is terrible?
27.
28. Step 2: consider entire stack The driver, the web-server, and the network must all work together.
29. Node imposes the full-stack requirements Node imposes async drivers Node imposes async frameworks Surprise: Node is “fast”
30. We can ignore the performance issues at our own peril or, we can just fix the problem
31. > I’ll take Ruby over JS gem install eventmachine
32. p "Starting"EM.rundop"Running in EM reactor"endp”won’t get here" whiletruedo timersnetwork_ioother_io end EventMachine Reactor concurrency without thread EventMachine: The Speed DemonWednesday @ 11:45am – Aman Gupta
33. Non-blocking IO requires non-blocking drivers: AMQP http://github.com/tmm1/amqp MySQLPlushttp://github.com/igrigorik/em-mysqlplus Memcachedhttp://github.com/astro/remcached DNS http://github.com/astro/em-dns Redishttp://github.com/madsimian/em-redis MongoDBhttp://github.com/tmm1/rmongo HTTPRequesthttp://github.com/igrigorik/em-http-request WebSockethttp://github.com/igrigorik/em-websocket Amazon S3 http://github.com/peritor/happening And many others: http://wiki.github.com/eventmachine/eventmachine/protocol-implementations
42. We can do better than node.js all the benefits of evented code without the drawbacks
43. Ruby 1.9 Fibers are a means of creating code blocks which can be paused and resumed by our application (think lightweight threads, minus the thread scheduler and less overhead). f=Fiber.new { whiletruedo Fiber.yield"Hi” end } pf.resume# => Hi pf.resume# => Hi pf.resume# => Hi Manual / cooperative scheduling! Ruby 1.9 Fibers and cooperative scheduling http://bit.ly/d2hYw0
44. Fibers vs Threads: creation time much lower Fibers vs Threads: memory usage is much lower Ruby 1.9 Fibers and cooperative scheduling http://bit.ly/aesXy5
45. defquery(sql) f = Fiber.current conn=EventMachine::MySQL.new(:host => 'localhost') q = conn.query(sql) c.callback { f.resume(conn) } c.errback { f.resume(conn) } return Fiber.yield end EventMachine.rundo Fiber.new { res =query('select sleep(1)') puts "Results: #{res.fetch_row.first}" }.resume end Exception, async! Untangling Evented Code with Fibers http://bit.ly/d2hYw0
46. defquery(sql) f = Fiber.current conn=EventMachine::MySQL.new(:host => 'localhost') q = conn.query(sql) c.callback { f.resume(conn) } c.errback { f.resume(conn) } return Fiber.yield end EventMachine.rundo Fiber.new{ res =query('select sleep(1)') puts "Results: #{res.fetch_row.first}" }.resume end 1. Wrap into a continuation Untangling Evented Code with Fibers http://bit.ly/d2hYw0
47. defquery(sql) f=Fiber.current conn=EventMachine::MySQL.new(:host => 'localhost') q = conn.query(sql) c.callback { f.resume(conn) } c.errback { f.resume(conn) } returnFiber.yield end EventMachine.rundo Fiber.new{ res =query('select sleep(1)') puts "Results: #{res.fetch_row.first}" }.resume end 2. Pause the continuation Untangling Evented Code with Fibers http://bit.ly/d2hYw0
48. defquery(sql) f=Fiber.current conn=EventMachine::MySQL.new(:host => 'localhost') q = conn.query(sql) c.callback { f.resume(conn) } c.errback { f.resume(conn) } returnFiber.yield end EventMachine.rundo Fiber.new{ res =query('select sleep(1)') puts "Results: #{res.fetch_row.first}" }.resume end 3. Resume the continuation Untangling Evented Code with Fibers http://bit.ly/d2hYw0
49.
50. Multi request interface which accepts any callback enabled client
51. Fibered iterator to allow concurrency control & mixing of sync / async
54. remcached: .get, etc, and .multi_* methods are synchronousem-synchrony: simple evented programming best of both worlds…
55. require"em-synchrony/em-mysqlplus" EventMachine.synchronydo db =EventMachine::MySQL.new(host:"localhost") res =db.query("select sleep(1)") puts res EventMachine.stop end Async under the hood Untangling Evented Code with Fibers http://bit.ly/d2hYw0
57. EM-HTTP, EM-MySQL, EM-Jack, etc. Drivers Async-rack Ruby VM Fibers Network Goliath Thin One VM, full concurrency, network-bound Ruby 1.9, Fibers, Thin: in production!
58. git clone git://github.com/igrigorik/em-mysqlplus.git git checkout activerecord rake install database.yml development: adapter:em_mysqlplus database:widgets pool: 5 timeout: 5000 environment.rb require 'em-activerecord’ require 'rack/fiber_pool' # Run each request in a Fiber config.middleware.useRack::FiberPool config.threadsafe! Async Rails with EventMachine & MySQL
59. classWidgetsController< ApplicationController defindex Widget.find_by_sql("select sleep(1)") render:text => "Oh hai” end end ab –c 5 –n 10 http://127.0.0.1:3000/widgets Server Software: thin Server Hostname: 127.0.0.1 Server Port: 3000 Document Path: /widgets/ Document Length: 6 bytes Concurrency Level: 5 Time taken for tests: 2.210 seconds Complete requests: 10 Failed requests: 0 Requests per second: 4.53 [#/sec] (mean) woot! Fiber DB pool at work. Async Rails with EventMachine & MySQL
61. Ruby 1.9 + Rails 3 + new stack = Order of magnitude better performance (aka, enough of a reason to actually switch)
62. The state of art is not good enough, in fact, it’s terrible! Let’s fix it. Fibers & Cooperative Scheduling in Ruby: http://www.igvita.com/2009/05/13/fibers-cooperative-scheduling-in-ruby/ Untangling Evented Code with Ruby Fibers: http://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/ EM-Synchrony: http://github.com/igrigorik/em-synchrony What do you think?