Sharing Sessions Between Rails and Node.js Using Redis

I’ve been spending my free hours lately working on a realtime multiplayer game. In building the game, I decided to build the front-end using Ruby on Rails with the actual game implemented using Node.js, specifically using Socket.IO. One of the first major challenges I encountered with this approach is the need to share session data between the two platforms. Searching for help on the topic only led to others seeking help on the topic with the responses all stating the sharing sessions is the way to go. Unfortunately, there was no actual information on how to go about doing that, which I intend to cover here.

Before starting on my project, I had already made a few decisions about the technologies being used, one of which is my intention to use Redis for session management. Fortunately there is already a well-maintained Ruby gem named redis-store available, which ties into Rails nicely. Additionally, there is a package named connect-redis that is available on the Node.js for the same reason. Independently, these work great.

With a little bit of work, I was able to find the Rails-based sessions within my Node.js code. Daniel Baulig has a good step-by-step guide that explains how to go about reading session data from within Socket.io. A good guide that got me started However, my app server would crash when trying to parse the session data. As it turns out, connect-redis expects the data to be in JSON format, and it tries to parse the data once it is found. This is problem because the redis-store gem is serializing data via Marshal.dump, making the two incompatible.

To go about resolving this issue, I thought that there must be a way to allow redis-store to serialize the session data to JSON. This turned out to be fine for serializing, but I quickly realized this was not suitable for restoring the session state in Rails. Rails is expecting some objects (such as flash messages) to come back from the session store as the appropriate class, which means a pure JSON solution wouldn’t work.

After digging further, I came across two other pull request attempts for redis-store (here and here), which attempted to implement a pure JSON solution as well. Since neither of them allowed Rails to function properly given the above, both of these pull requests were declined.

I finally came up a with a simple solution that addresses both issues: serialize the data to JSON, but force Ruby objects that don’t have an inherent JSON representation (i.e.: anything that does not directly map to a JSON string, object, array, number, boolean, or null) to be marshaled via Marshal.dump. This will convert them to a string that is illegible from the perspective of Node.js, but theoretically, Node.js should not need access to pure Ruby objects that are in the session anyway.

After a bit of work, I submitted a pull request to redis-store that introduces the idea of serialization strategy. By default, a “marshal” strategy is used, which defaults to using Marshal.dump and Marshal.load as the gem has always done. This means that anyone who has been using the gem to date will not have any backward compatibility concerns by upgrading the gem. It also includes a “json” and “yaml” strategy, which are exactly what they sound like. Strategies can be defined from the Rails side simply by including a :strategy option when configuring the session store’s servers. (Typically, this is done in config/initializers/session_store.rb).

MyApp::Application.config.session_store :redis_store,
  key: 'session_id',
  domain: '.dev.local',
  servers: {
      host: 'localhost',
      port: 6379,
      db: 0,
      namespace: 'myapp:sessions',
      strategy: :json }

If my pull request is ultimately accepted into the redis-store code base, then tying Rails and Node.js sessions will be no harder than specifying the serialization strategy used by Rails. With that said, please comment on my pull request if you are interested in seeing this code merged in so that it can be used by others in the future.

Leave a Reply

Your email address will not be published. Required fields are marked *