Evented Ruby VS Node.js

54
Evented programming in node.js and ruby

Transcript of Evented Ruby VS Node.js

Evented programming in node.jsand ruby

Overview

• Evented Programming

• Evented Web Backends

• Evented Ruby vs Evented Javascript

• Improving Rails Concurrency

Evented Programming

$(‘body’).click(function(){

$(this).css(‘color’, ‘red’);

});

Event Callback

Reactor Pattern

Reactor Pattern

• Events: keyboard, mouse, touch

• Reusable reactors: browser

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

Blocking I/O

• Switches b/w busy processes

• OS and h/w caches hides complexity

Blocking I/O

Node.js switches b/w I/O within the same process

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

Rails Concurrency

Rails Concurrency

• Process Concurrency• Lots of memory

Node Concurrency

tweet = new Tweet(); // 1.

tweet.shorten_links(function(tweet) { // 2. callback

tweet.save(function(){ // 3. callback

})

})

Node Concurrency

Node Concurrency

• Reactor switches b/w requests

• Fewer processes needed => Less memory

Latency

• Blocking I/O does not speed up

• Optimize response latency first

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

Who are using node

How event driven programming can be done in ruby

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

Ruby Reactors

• Reactor is just a gem

• Eventmachine, cool.io, others

Basic concepts

• Threads

• Fibers

Threads

• Shared state and memory space

• Light weight

• Preemptive scheduling

Downsides

• Race conditions

• Deadlocks

• Hard to debug

• GIL

Fibers

• Cooperative scheduling

• Relatively lightweight

• Maintains state

Upsides

• No races

• No need of locks

• Parallelism

• Explicit yielding and resuming

Add Events

http = EM::HttpRequest.new(‘http://vinsol-meetup.com/’).get

http.callback {

# request finished

}

OK, so how do I use EM?

• gem install ‘eventmachine’

• require ‘eventmachine’

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

Procedural Interface, Evented Execution

Procedural Interface, Evented Execution

Code Smell?

One Requests per Fiber

• Wrap each req in its own fiber

• Web reqs are independent of each other

• Switch between requests instead of processes

Rack::FiberPool

Recap

• App server is reactor aware

• One fiber per request

• App code is unchanged

Mixing Paradigms

• Libraries may block the reactor

But that’s ok…

Starting Points

• Data stores

• http

• Kernel.system calls

Data Stores

• Redis

• Mysql

• Postgresql

• mongo

HTTP

• Faraday: use an event aware http adapter

System Calls

• EM.popen – non-blocking version

• EM.defer – run blocking call outside of reactor thread

Final Result

Why Ruby

• Reuse existing code

• Performance won’t be worse

• Keep procedural syntax

• Multi-paradigm

Why Node

• Single paradigm consistency

• Community

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

Thanks!