When we build applications on a singleton server things are very simple. But then we need to start scaling out (usually better approach than scaling up) and we need to worry about session state management. Here is a great article by Justin Weiss and video of his talk on Rails sessions.

Sticky sessions

The simplest thing on AWS Elastic Load Balancer is to enable sticky sessions. ELB will create a cookie and the next time the request comes back it will be sent to the same EC2 instance as before.

The downside is that if we need to take server out of the load balancer to do maintenance / deploy code there could still be users on it. To help with that we need to use dedicated state server or DB to store session info.

Redis

In Ruby on Rails applications we can enable Redis session storage using redis-rails.

# config/environments/development.rb
config.redis_host = 'localhost'
# config/initializers/session_store.rb
AppName::Application.config.session_store :redis_store, {
  servers: [
    { host: Rails.application.config.redis_host,
    port: 6379, db: 0, namespace: "session" },
  ],
  expire_after: 1.day
}
# data in Redis
{"db":0,"key":"session:63f3a232ca05b895b0d9adb1b292903e","ttl":7192,
  "type":"string","value":"...","size":138}

Redis will purge the data after one day with TTL.

Mongo

We have been using this approach for a couple of years with mongo_session_store-rails4 gem.

# config/initializers/session_store.rb
Rails.application.config.session_store :mongoid_store
MongoSessionStore.collection_name = "sessions"

Documents in Mongo will have ID (zzv-ATGWb5lG-w7AwwI438pXHtk) and DATA (#<BSON::Binary:0x00000008468ce8>). We can also modify the default model class to add TTL indexes which will purge old records.

class Sessions
  include Mongoid::Document
  field :data
  field :created_at, 	type: DateTime
  field :updated_at, 	type: DateTime
  # create index to clean out the collection
  index({updated_at: 1}, {expire_after_seconds: 1.day})  
end

ActiveRecord / SQL

We need to follow instructions on activerecord-session_store to install the gem and created SQL table where data will be stored.

Rails.application.config.session_store :active_record_store,
  :key => '_my_app_session'

Sessions table will have ID (primary key), session_id (ea2c0d7d8b5799c0f48966c9312f95e8), data, created_at and updated_at. Since MySQL / Postgres do not have TTL process we will need to create a background job to clean out these records.

class SessionCleanJob < ApplicationJob
  queue_as :low
  def perform(*args)
    # delete all session records older than X time
  end
end

Which approach should we use depends on our needs. If we are already using Redis for other tasks such as caching or background jobs then it may make sense to store our session data. On the other hand if we do not yet have Redis and our primary DB is not under strain then it’s probably simpler to store sessions there.