Overview
• Evented Programming
• Evented Web Backends
• Evented Ruby vs Evented Javascript
• Improving Rails Concurrency
Node.js
• Node.js is an event driven, non-blocking I/O model that makes it lightweight efficient, perfect for data intensive real time that run across distributed devices.
Blocking I/O
If RAM was an F-18 Hornet with a max speed of 1,190 mph, disk access is a banana slug with a
top speed of 0.007 mph
Blocking I/O
F = Fast F18 Hornet
S = Slow Banana Slug
data = File.read(‘file.txt’)
FSSSSSSSSSSSSSSSSSF
CPU is idle
Web Apps
• Blocking I/O decreases concurrency
• Database, file system – disk
• S3, external APIs – network
• ImageMagick – shelling out
Rails Concurrency
tweet = Tweet.new(params[‘tweet’]) # 1.
tweet.shorten_links! # 2. network
tweet.save # 3. disk
Node Concurrency
tweet = new Tweet(); // 1.
tweet.shorten_links(function(tweet) { // 2. callback
tweet.save(function(){ // 3. callback
})
})
Code Smell
tweet = new Tweet(); // 1.
tweet.shorten_links(function(tweet) { // 2. callback
tweet.save(function(){ // 3. callback
})
})
• App code aware of blocking I/O
• Ugly syntax, nested contexts
Node use cases
• Chat Server
• Fast File Upload Client
• API’s
• Proxy server(In SOA)
• Data Streaming
• Any Real Time Data Apps
Think ruby way
• Instead of waiting for something to happen before executing code,
• Put that code in proc,
• Invoke the proc when something happens
Writing asyc code
• Synchronous ruby code uses return values
– ret = operation()
– do_something_with(ret)
• Evented async code uses blocks instead
– operation{ |ret| do_something_with(ret) }
• Different from how you usually use ruby blocks. The is stored and invoked later(it’s asynchronous)
Evented Ruby
• Ruby is capable of evented programming
• Multi-paradigm: procedural, evented, parallel
• Mix and match paradigms
Add Events
http = EM::HttpRequest.new(‘http://vinsol-meetup.com/’).get
http.callback {
# request finished
}
Deferring work
• The reactor itself is single threaded and the EM methods which work with the reactor are not thread-safe.
• This has two outcomes. – code that takes a long time to run and moved to a
bg thread(db queries, http requests etc.)
– Once moved to bg thread, we have ability to tell reactor to do work for us
• This is where EM#defer comes into play.
EM#defer(op, cb)
• We can schedule the execution of a block to one of the threads in EventMachines thread pool.
• EM#thread_pool_size = 20(default)
• EM#defer takes a second parameter, the callback. This callback will be executed on the main reactor thread and will be provided with the return value of our deferred operation.
Code Smell
http = EM::HttpRequest.new(‘http://vinsol-com.com/’).get
http.callback {
# request finished
}
• App code aware of blocking I/O
• Code doesn’t look like ruby
Procedural Interface, Evented Execution
Faraday.default_adapter = :em_synchrony
response = Faraday.get ‘http://vinsol-taf.com/’
• Hides system event callbacks in libraries
• Keeps app code clean
One Requests per Fiber
• Wrap each req in its own fiber
• Web reqs are independent of each other
• Switch between requests instead of processes
System Calls
• EM.popen – non-blocking version
• EM.defer – run blocking call outside of reactor thread
Why Ruby
• Reuse existing code
• Performance won’t be worse
• Keep procedural syntax
• Multi-paradigm
Wrapping up
• Evented programming is hard in any language
• Evented programming for evented problems
• Evented programming doesn’t fix latency
• Avoid evented I/O interface in app code