A few weeks ago I posted about a poor man's chat server I created with Hobo's Ajax features. I now had the request to improve performance, so I tried to fix it.
I pimped my old solution with a Faye messaging server: Ryan Bates created a gem and a Railscast about it.
Lets see how we update data on all connected clients but only when necessary. In conversation.dryml we subscribe to a faye server:
<%= subscribe_to "/conversations/" + this.id.to_s %>
and to a channel for this chat session (called conversation in this app):
<script>
PrivatePub.subscribe("/conversations/<%= this.id.to_s -%>", function(data, channel) {
$("form#" + data.type + "-form").submit();
});
</script>
When the channel fires a callback is triggered, and we immediately select and submit a small form. That is selected by the type that was posted from the create action in the controller:
PrivatePub.publish_to("/conversations/"+ this.conversation.id.to_s, type: "messages")
which itself was called by the form in the view of the person who created the message:
<form with="&@message" update="mymessages">
<div> New Message: <input:content/> </div>
</form>
Back to the update: The form
<form id="messages-form" style="display:none;" action="refresh" update="messages">
<input name="id" value="#{this.id}" type="hidden"/>
</form>
calls a minimalist controller action to get the new data:
def refresh
self.this = Conversation.find(params[:id])
hobo_show
end
and it updates the part:
<collection:messages part="messages"/>
So not the data to be displayed is propagated. Just the info, what part has to be updated. Then an ajax call gets the data of the part to be updated. This part then can contain data of any type, e.g. images. See conversation_2.dryml for in example with clipboard upload. These two roundtrips are very small and super fast (< 1K transfered data, 25ms for the ajax call on the server, < 100ms roundtrip. I don't now how to measure the faye roundtrip time.) It looks like real time.
The Faye server comes as a dependency with private_pub and Ryan Bates explains nicely how to set it up in development. I stumbled setting it up on my Ubuntu server with rvm (and passenger). Passenger is the wrong way to go, it is optimized for short requests, Faye sessions are open for a long time (the full conversation).
The easiest way is to create an upstart script (faye.conf). This way you it starts automatically on boot and you can start it manually using
sudo service faye start
You cannot stop, status or restart it using service. Kill is your friend for stopping if necessary, and
netstat -an | grep 9292
for checking status.
Upstart scripts invoking rackup / rvm apps need a wrapper to use the correct ruby and rvm version. That can be created like this:
rvm alias create faye ruby-2.0.0-p353
rvm wrapper faye --no-links rackup
so that the upstart script can use the correct user:
exec sudo -u put_username_in_here /home/put_username_in_here/.rvm/wrappers/faye/rackup /var/rails/mercator/private_pub.ru -s thin -E production
Phew, I guess that's it.