<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kickass Labs &#187; ruby on rails</title>
	<atom:link href="http://www.kickasslabs.com/tag/ruby-on-rails/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kickasslabs.com</link>
	<description>We &#9829; code.</description>
	<lastBuildDate>Wed, 28 Dec 2011 16:57:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Quick and Dirty Messaging</title>
		<link>http://www.kickasslabs.com/2008/11/22/93/</link>
		<comments>http://www.kickasslabs.com/2008/11/22/93/#comments</comments>
		<pubDate>Sat, 22 Nov 2008 20:05:55 +0000</pubDate>
		<dc:creator>Brad</dc:creator>
				<category><![CDATA[Great Minds]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[gm]]></category>
		<category><![CDATA[message queue]]></category>
		<category><![CDATA[messaging]]></category>
		<category><![CDATA[rails rumble]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[rumble]]></category>

		<guid isPermaLink="false">http://www.kickasslabs.com/?p=93</guid>
		<description><![CDATA[Our Rails Rumble 2008 entry, Great Minds (you can have a look at the latest version or the original Rumble version), required a messaging system. Such systems are easy to do wrong, and we knew we&#8217;d need something that would &#8230; <a href="http://www.kickasslabs.com/2008/11/22/93/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Our <a href="http://railsrumble.com/" title="Rails Rumble 2008" target="rr2008">Rails Rumble 2008</a> entry, <i>Great Minds</i> (you can have a look at the <a href="http://greatminds.kickasslabs.com/" title="Great Minds" target="gmkal">latest version</a> or the <a href="http://greatminds.r08.railsrumble.com/" title="Great Minds - Rumble Version" target="gmrr">original Rumble version</a>), required a messaging system.  Such systems are easy to do wrong, and we knew we&#8217;d need something that would stay solid under unknown load during Rumble judging.</p>
<p>The solution was quick &amp; dirty (as most solutions are during Rails Rumble), but the results worked, and allowed us to qualify for judging and reach a respectable 28th place finish in the &#8220;Completeness&#8221; category.</p>
<p>Check out the details after the fold.</p>
<p><span id="more-93"></span></p>
<h3>Design Constraints</h3>
<p><i>Great Minds</i> is a word game played by 2 or more players.  The game is played in a virtual chat <b>Room</b>, containing one or more <b>Players.</b>  Players can take various actions, such as submitting a word to the game, chatting, or changing their handle/username.  The results of these actions must be transmitted to other players in the room.</p>
<p>Other constraints on the design included:</p>
<ul>
<li><b>No New Technologies:</b>  There are tools like <a href="http://juggernaut.rubyforge.org/" title="Juggernaut" target="juggernaut">Juggernaut</a> that make messaging pretty easy, but we had no hands-on experience with them, and learning a new tool on a 48-hour schedule was deemed a risk to completing the project &#038; qualifying for Rumble judging.</li>
<li><b>Broadcast:</b>  We needed something more than a simple message queue like <a href="http://xph.us/software/beanstalkd/" title="Beanstalk" target="beanstalk">Beanstalk</a>; the consumer of a message couldn&#8217;t remove it from the queue, as there might be other consumers needing the same message.</li>
<li><b>Room State Reconstruction:</b>  Someone coming to the room for the first time (or leaving and returning) should be able to see the current room state, including all participants, recently played words, and recent chat.</li>
<li><b>Replay:</b>  Though we didn&#8217;t get around to implementing it, we wanted to offer the ability to review a particularly funny game.</li>
</ul>
<p>The first two constraints affected our design and technology choices; the second two demanded that the messages be persisted.  (Alternatively, we could have saved the room state in some other way, but as long as we had the messages anyway, it seemed simpler to just keep them and reconstruct the state.)</p>
<h3>Client Implementation</h3>
<p>In the absence of a viable push solution like Juggernaut, we opted for a polling solution:  Clients would periodically send a signal that:</p>
<ul>
<li>let the server know they&#8217;re still alive and connected,</li>
<li>sent any new messages to the server, and</li>
<li>requested any pending messages from the server.</li>
</ul>
<p>On the client side, this meant that every message got packaged as JSON, stuck in a queue, and periodically collected and sent to the server as the optional payload of a keep-alive signal.  With some help from Prototype, this was as simple as:</p>
<pre>
  function SyncMessages() {
          var data = Object.toJSON(MESSAGES_QUEUE);
          var url = '/room/sync_messages';
          var params = {
                  authenticity_token: AUTH_TOKEN,
                  messages: '[]',
                  //messages: Object.toJSON(MESSAGES_QUEUE),
                  last_message_id: LAST_MESSAGE_ID };

          new Ajax.Request(url, {
            method: 'post',
                  parameters: params,
            onSuccess: HandleMessages });
  }

  SyncMessages(); //start first on load
  var message_syncer = new PeriodicalExecuter(SyncMessages, 3);
</pre>
<p><code>HandleMessages()</code> updates the DOM based on messages coming back.</p>
<p>The other feature to note is the <code>LAST_MESSAGE_ID</code> token above.  Each client is responsible for keeping track of the last message ID it got.  On the server side, message IDs are assigned sequentially, so all new messages are guaranteed to have a higher ID.</p>
<p>We could have kept track of each client&#8217;s last message on the server side, but this would have been much more complex &#8211; we would have needed to store data either in the session (which already held a fair amount of stuff) or in another table in order to keep track of who had received what.</p>
<h3>Server Implementation</h3>
<p>On the server side, messages were persisted in a table.  This table would have relatively frequent inserts, somewhat more frequent reads, and no updates or deletions.  For this reason, we went with the default MyISAM table architecture.  (We had considered a design that would have updated the table with delivery status information and would have required InnoDB&#8217;s row-level locking, but in the end we opted for the simpler design.)  The table looks like:</p>
<pre>
  class CreateMessages < ActiveRecord::Migration
    def self.up
      create_table :messages do |t|
        t.integer :message_type_id, :room_id, :game_id, :user_id
        t.string  :data, :user_handle
        t.timestamps
      end
    end

    def self.down
      drop_table :messages
    end
  end
</pre>
<p>It also has an index:</p>
<pre>
  class AddRoomIndexToMessages < ActiveRecord::Migration
    def self.up
      add_index :messages, [:room_id, :id]
    end

    def self.down
      remove_index :messages, [:room_id, :id]
    end
  end
</pre>
<p>Recall that the client keeps track of the last message it got, and periodically requests all messages since.  This index speeds that request - which is just <code>msgs = Message.find :all, :conditions => ['room_id = ? AND id > ?', session['room_id'], last_message_id], <img src='http://www.kickasslabs.com/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> rder => 'id'</code>.  (Yes, I know that should be a <a href="http://ryandaigle.com/articles/2008/8/20/named-scope-it-s-not-just-for-conditions-ya-know" title="Named Scopes" title="namedscopes">named scope</a>.  I hadn't gotten around to learning those yet at the time of the Rumble.)</p>
<p>When the a keep-alive signal hits the server:</p>
<ul>
<li>any new messages attached to the signal are added to the <code>messages</code> table,</li>
<li>any game mechanics related to those messages are executed (which may result in more messages being added to the table), and</li>
<li>all messages for the caller's room with an ID greater than the caller's LAST_MESSAGE_ID are packaged as JSON and sent back down in the HTTP response.</li>
</ul>
<p>A note:  If you're in a room and you enter (for example) a chat message, there's actually a server round-trip involved (pushing the message to the server and getting the same message back as a response) before that chat message is shown in your browser.  We worried about this, but not for long - the interactivity delay turned out to be minimal, and it was much simpler to keep it this way (every message is treated the same for all clients) than to create an exception (you update your DOM with your chat messages immediately, but your own chat messages are somehow filtered from your message stream).</p>
<h3>What's Missing?</h3>
<p>Obviously, this was a quick &amp; dirty solution, tailored to the immediate needs of a Rails Rumble project.  Here's what I'd do differently (and indeed, will do differently, as we have plans for the future of Great Minds - stay tuned):</p>
<ul>
<li><b>Security:</b>  We do use the Rails session <code>authenticity_token</code>, but there's still no protection from a server flood from someone who has a token, either by cranking down the polling interval or by fiddling with the LAST_MESSAGE_ID.</li>
<li><b>Server Push:</b>  Polling is wasteful - requests are made whether or not there are any messages to send or receive.  We plan to move to Juggernaut soon.</li>
<li><b>Decoupling from Game Mechanics:</b>  In the first iteration, message receipt and delivery were too intimately tied to game mechanics, with <code>Message</code> objects being explictly constructed and followed by <code>Message.save!</code> calls.  I've already started the process of teasing them apart in preparation for the move to Juggernaut, factoring this into a <code>deliver_message()</code> method.</li>
</ul>
<p>Questions?  Thoughts?  Add a comment!  I promise to get back to you.</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.kickasslabs.com%2F2008%2F11%2F22%2F93%2F&amp;title=Quick%20and%20Dirty%20Messaging" id="wpa2a_2"><img src="http://www.kickasslabs.com/wp-content/plugins/add-to-any/share_save_120_16.png" width="120" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://www.kickasslabs.com/2008/11/22/93/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

