Rails 5 ActionCable & Underlying architecture part (1/2)

Tanuj Soni
deqode
Published in
5 min readApr 10, 2017

--

There have been a lot of blogs floating around and teaching how to write a simple chat app using the brand new Rails 5 ActionCable feature. Being also developing things in NodeJS with Socket.io, I find it hard to believe that ActionCable could also compete with node’s non-blocking event loop based I/O.

My disbelief was also supported when I looked at ActionCable when it was in very early stage and an official screencast stated that:-

“You should not do any processing in actioncable channels because they are not concurrent , if you do they will block everything.”

Now the Rails 5 is officially released, I thought of taking a look at it again as I read somewhere that ActionCable channels are now concurrent. Now it became interesting to know how this is made possible and efficient enough on a language that is not concurrent by itself. So now I will be walking you through the introduction and building blocks of ActionCable.

Caution: The post targets Rails 5.0.0.1 August 11, 2016, release. Things might have changed and I will be making amendments accordingly. Also understanding the complete architecture requires basic knowledge of building blocks which I will be discussing in the current post.

Starting from the ground, what the heck are Websockets & ActionCable:-

The web works by request/response system of HTTP. A client(browser) loads up a web page and then nothing happens until the user navigates to another page. Although, AJAX made things more dynamic and parts of web pages can be updated without reloading the whole page. Updating things by AJAX required some periodic polling or some other user initiated interactions on the page. Pooling for updated data is good solution but it’s not that fast and takes a lot of bandwidth as initial handshaking is required for every request. Also, pooling is not really real time as you checks for updated data in some time intervals.

One of the hack done with request/response system called long polling, in this client opens an HTTP connection to the server which keeps it open until server sends response and server holds the response till it actually has new data. This creates an illusion that, server is able to send new data to client as soon as possible.

Still, all communication originally is initiated by the client and these solutions are not suitable for latency sensitive application like:- Stock trading terminal, online multiplayer gaming.

Now comes the Websockets

It’s an API that establishes a persistent connection between client and server. Handshaking is required only once when the connection is initiated. Now with this connection either client or server can send data without repeated handshake with very low latency.

ActionCable

Writing websockets in Rails was a tedious task as we have to depend on gems like faye. Also existing solutions were not easy to code and scale. ActionCable provides an easy interface for both writing server-side Rails code as well as client-side Javascript code. With it websockets features can be written in Ruby style and form as the rest of your Rails application, while still being performant and scalable.

ActionCable Implementation:-

ActionCable is based around the Publish-Subscribe messaging queue, often referred to as Pub/Sub. It is used to publish some data(message) to subscribers. This queue is also enables Rails and ActionCable to share states. As the queue is accessible to all worker instances, any background worker or any instance of Rails server can publish can push the data/messages to pub/sub queue. Clients can subscribe/unsubscribe to channels according to their requirements. Mainly Redis is used for pub/sub but other tools asynchronous, inline, PostgreSQL, evented Redis, and non-evented Redis are also supported.

ActionCable server can be mounted on the Rails server by the use of Rack socket hijacking API to take over control of connections from the application server. While this seems a good approach while development but in production we can also run Rails & ActionCable separately to tweak the both of them according to our scaling requirements. One thing we need to keep in mind while running Rails & ActionCable separately is that we should put both the server in common domain namespace so that cookies can be shared by both of them.

The ActionCable server does not need to be a multi-threaded application server. This is because ActionCable uses the Rack socket hijacking API to take over control of connections from the application server. ActionCable then manages connections internally, in a multithreaded manner, regardless of whether the application server is multi-threaded or not. So ActionCable works with all the popular application servers — Unicorn, Puma and Passenger.

See this:- http://old.blog.phusion.nl/2013/01/23/the-new-rack-socket-hijacking-api/

ActionCable smartly used EventMachine to make it able to handle a large number of websocket connections. ActionCable runs a 3 second heartbeat on all connections to check if any connection is stale and this process needs to be done continuously. All these type of operation are put in an EventLoop provided by EventMachin and this EventLoop is started by “EM.run”.

The method “run” starts EventMachine’s event loop. Don’t worry if you have no idea of what an event loop is: it is somewhat similar to the one which is offered by NodeJS. Once started it traps the thread it runs in, so the code continues running until it’s stopped. For now you can think of it being a while(true) loop.

Understand what an EventLoop is:- https://www.youtube.com/watch?v=8aGhZQkoFbQ

Main Dependencies of ActionCable

The ActionCable component is mainly dependent on the following:-

Websocket-driver

It is complete implementation of the WebSocket protocols for ruby, it also uses EventMachine for event-compatible iterators and timers.

websocket-driver provides a lot of functionalities out of the box like:-

- Select the correct server-side driver to talk to the client

- Generate and send both server- and client-side handshakes

- Recognize when the handshake phase completes and the WS protocol begins

- Negotiate subprotocol selection based on Sec-WebSocket-Protocol

- Negotiate and use extensions via the websocket-extensions module

- Buffer sent messages until the handshake process is finished

- Deal with proxies that defer delivery of the draft-76 handshake body

- Notify you when the socket is open and closed and when messages arrive

- Recombine fragmented messages

- Dispatch text, binary, ping, pong and close frames

- Manage the socket-closing handshake process

- Automatically reply to ping frames with a matching pong

- Apply masking to messages sent by the client

See more at:- https://github.com/faye/websocket-driver-ruby

Concurrent-ruby

It’s a concurrency library for Ruby. Inspired by Erlang, Clojure, Scala, Go, Java, JavaScript, and classic concurrency patterns. It is mainly used in ActionCable for Thread Pool. A thread pool is a group of pre-instantiated, idle threads which stand ready to be given work. These are preferred over instantiating new threads for each task when there is a large number of short tasks to be done rather than a small number of long ones.

See More:- https://github.com/ruby-concurrency/concurrent-ruby

nio4r

It’s primarily served core async I/O functionality. It was a part of popular concurrency library (Celluloid) but later the author of Celluloid felt that nio4r is something that can be extracted out and other Ruby concurrency libraries can be built on top of it.

See more at:- https://github.com/celluloid/nio4r

I am finalizing the part 1 of post here. In the second part of this I will be focusing on the EventMachine and above described dependencies along with the description of how all the pieces fit together and how the ActionCable is made scalable.

--

--