Ruby on Rails
HowtoChangeSessionStore (Version #81)

Rails can be configured to use any of the following to store session data.

  • PStore (default)
  • ActiveRecordStore
  • DRbStore
  • FileStore
  • MemoryStore

Read more about these in lib/action_controller/session.

The default is using the file system(PStore), but you can also specify one of the other included stores or use your own class.

Scott Barron has prepared an excellent (if somewhat dated) comparison of session stores on his site.

Generally

Selecting which store to use is a one line affair (in one of your EnvironmentFiles):


config.action_controller.session_store = one_of_the_session_store_engines
)

Where one_of_the_session_store_engines is one of :active_record_store, :p_store, :drb_store, :mem_cache_store, or :memory_store

Each storage engine has its own particular settings and configuration issues that will require further attention.

Owing to the fact that the PStore, FileStore and MemoryStore session stores in Rails derive from Ruby’s CGI module, the CGI module Session documentation is a good source of information about how those session stores work.

  • What are database engine options? (Please put the code inline with the code above)

ActiveRecord Store

To use ActiveRecord as a session storage mechanism, you will need a table in your database named sessions:


 CREATE TABLE sessions (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  sessid  CHAR(32),
  data    TEXT,
  PRIMARY KEY(id),
  INDEX(sessid)
);

In Rails 1.0, you can simply run rake db:sessions:create to create the sessions table.

In case it helps anyone, I believe the new format for the sessions table (as of Rails 1.2.3 anyway) is something more like:


CREATE TABLE sessions (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  session_id varchar(255) default NULL,
  data TEXT,
  updated_at datetime default NULL,
  PRIMARY KEY  (id),
  KEY index_sessions_on_session_id (session_id),
  KEY index_sessions_on_updated_at (updated_at)
);

Note that the Agile Web Development with Rails book suggests adding an updated_at column to your session table. This, in theory, will be updated automatically when your session is accessed. However, this is not working for me (Rails 1.0) so YMMV.

You will also need to update your config/environment.rb file. The code below is already in your /config/environment.rb file. Simply uncomment the line, add the table above and your app is ready to use your database for session storage.

Tell Rails to Use ActiveRecordStore


config.action_controller.session_store = :active_record_store

Bath and Shower
Fragrance
Gift Sets
Hair Care
Makeup
Men’s Grooming
Shaving and Hair Removal
Skin Care
Tools and Accessories

Computers – Computer Add-Ons
Computers – Desktops
Computers – Handhelds & PDAs
Computers – Notebooks

Baby Diapering
Baby Feeding
For Moms
Baby Furniture
Baby Gear
Baby Gifts
Baby Health & Baby Care
Nursery Décor
Potty Training
Baby Safety
Baby Strollers

Changing the table name

To use a table name other than sessions, for instance my_sessions, add the following to your environment configuration:

CGI::Session::ActiveRecordStore::Session.set_table_name "my_sessions"

Changing the primary key name (conflicts with :table_name_with_underscore)

If you are using


ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore

in your environment.config, you must specify

CGI::Session::ActiveRecordStore::Session.set_primary_key 'id'

to tell ActiveRecord to look for the “id” field and not the “session_id” field. Otherwise, you will get an application error due to a null value in the id field.

-Ira Burton?

As an added note to this, you might want to index the sessid field. In my session container performance tests there is quite a difference after a period of time between the two.

- This probably needs moving somewhere else, but a useful tip for ActiveRecordStore:

If you want to add something to the Sessions model (in my situation I wanted to add a user_id field to keep track of the user which this session belonged to so that I could monitor users who have an open session), you might spend hours pulling your hair out if you try something along the lines of


Session.find(:first, :conditions=> ["session_id = ?", @session.session_id]).updateAttribute("user_id",   @session[:user].id)  

A much better and easier way to do this, is to use the ‘model’ attribute. When using ActiveRecordStore as storage for sessions, a method ‘model’ is provided which gives you access to the DB implementation.

So, instead, do this:


@session.model.user_id = @session[:user].id

This will of course require creating a model ‘session.rb’ which extends ActiveRecord.

Performance considerations

In order to improve performance, you might also want to make the sessid and data fields just as big as the values you will store in the session. For example, if you know that the length of the session id values is 32 you can use a statement like this one:

 CREATE TABLE sessions {
  id INTEGER PRIMARY KEY,
  sessid CHAR(32),
  data TEXT
}

The length of the data field must be long enough to accomodate the flash and therefore cannot be easily calculated in advance (I think).

Note (for pgsql users): Due to the way text fields are handled, defining a text field with a maximum width doesn’t provide any performance benefits in PostgreSQL. TEXT fields will do just fine, in fact they are a bit faster than CHAR fields since there is no padding overhead.
- Stefan Kaes

Example MySQL table

A good definition for a MySQL table is as follows. The AUTO_INCREMENT keeps you from getting duplicate id errors.


 CREATE TABLE sessions (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  sessid  CHAR(32),
  data    TEXT,
  PRIMARY KEY(id),
  INDEX(sessid)
) 

-Ira Burton?

Extending ActiveRecordStore

You can customize the behaviour of the DB session store.

The following example is from Jeremy Kempers mailing list post: Avoiding session race condition in concurrent requests.

The ActiveRecordStore backend for CGI::Session allows you to plug in custom session classes. See session/active_record_store.rb in Action Pack. The default session class is a normal Active Record class named Session. An alternative, SqlBypass, is provided which duck-types with the AR class but uses straight SQL over the db connection You can use it as a starting point for pessimistic locking


class MyPessimisticSession < CGI::Session::ActiveRecordStore::SqlBypass
  # Pick a database connection for straight SQL access.
  self.connection = ActiveRecord::Base.connection

  # Override the finder class method to do a SELECT ... FOR UPDATE.
  def self.find_by_session_id(session_id)
    @@connection.select_one "..."
  end
end

# Use our custom session class.
CGI::Session::ActiveRecordStore.session_class = MyPessimisticSession

# Use the ActiveRecordStore for CGI sessions.
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.update(
  :database_manager => CGI::Session::ActiveRecordStore
)

MemCached Store

Install MemCached

Install MemCached (http://www.danga.com/memcached/)

  • If your distribution is debian(sid or sarge).
    
    apt-get install memcached
    

Install Ruby-MemCache


# gem install Ruby-MemCache

Or better with new and faster Mecache-Client

  1. gem install memcache-client

Tell Rails to Use MemCached

Change environment.rb to use MemCacheStore


config.action_controller.session_store = :mem_cache_store

To setup memcache-client we must to change enviroment.rb (ruby-memcache and memcache-client?)


require 'memcache'
memcache_options = {
   :compression => false,
   :debug => false,
   :namespace => "app-#{RAILS_ENV}",
   :readonly => false,
   :urlencode => false
}
memcache_servers = [ '192.168.1.150:2222', '192.168.1.150:2223' ]

Rails::Initializer.run do |config|
….
config.action_controller.session_store = :mem_cache_store

config.action_controller.fragment_cache_store = :mem_cache_store, memcache_servers, memcache_options

end


cache_params = *([memcache_servers, memcache_options].flatten)
CACHE = MemCache.new *cache_params
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.merge!({ ‘cache’ => CACHE })


juampe, thanks to Tom Fakes from craz8
memcache-client will automatic load so no need
<pre> request 'memcache'
-gorou

Category: Howto

Rails can be configured to use any of the following to store session data.

  • PStore (default)
  • ActiveRecordStore
  • DRbStore
  • FileStore
  • MemoryStore

Read more about these in lib/action_controller/session.

The default is using the file system(PStore), but you can also specify one of the other included stores or use your own class.

Scott Barron has prepared an excellent (if somewhat dated) comparison of session stores on his site.

Generally

Selecting which store to use is a one line affair (in one of your EnvironmentFiles):


config.action_controller.session_store = one_of_the_session_store_engines
)

Where one_of_the_session_store_engines is one of :active_record_store, :p_store, :drb_store, :mem_cache_store, or :memory_store

Each storage engine has its own particular settings and configuration issues that will require further attention.

Owing to the fact that the PStore, FileStore and MemoryStore session stores in Rails derive from Ruby’s CGI module, the CGI module Session documentation is a good source of information about how those session stores work.

  • What are database engine options? (Please put the code inline with the code above)

ActiveRecord Store

To use ActiveRecord as a session storage mechanism, you will need a table in your database named sessions:


 CREATE TABLE sessions (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  sessid  CHAR(32),
  data    TEXT,
  PRIMARY KEY(id),
  INDEX(sessid)
);

In Rails 1.0, you can simply run rake db:sessions:create to create the sessions table.

In case it helps anyone, I believe the new format for the sessions table (as of Rails 1.2.3 anyway) is something more like:


CREATE TABLE sessions (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  session_id varchar(255) default NULL,
  data TEXT,
  updated_at datetime default NULL,
  PRIMARY KEY  (id),
  KEY index_sessions_on_session_id (session_id),
  KEY index_sessions_on_updated_at (updated_at)
);

Note that the Agile Web Development with Rails book suggests adding an updated_at column to your session table. This, in theory, will be updated automatically when your session is accessed. However, this is not working for me (Rails 1.0) so YMMV.

You will also need to update your config/environment.rb file. The code below is already in your /config/environment.rb file. Simply uncomment the line, add the table above and your app is ready to use your database for session storage.

Tell Rails to Use ActiveRecordStore


config.action_controller.session_store = :active_record_store

Bath and Shower
Fragrance
Gift Sets
Hair Care
Makeup
Men’s Grooming
Shaving and Hair Removal
Skin Care
Tools and Accessories

Computers – Computer Add-Ons
Computers – Desktops
Computers – Handhelds & PDAs
Computers – Notebooks

Baby Diapering
Baby Feeding
For Moms
Baby Furniture
Baby Gear
Baby Gifts
Baby Health & Baby Care
Nursery Décor
Potty Training
Baby Safety
Baby Strollers

Changing the table name

To use a table name other than sessions, for instance my_sessions, add the following to your environment configuration:

CGI::Session::ActiveRecordStore::Session.set_table_name "my_sessions"

Changing the primary key name (conflicts with :table_name_with_underscore)

If you are using


ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore

in your environment.config, you must specify

CGI::Session::ActiveRecordStore::Session.set_primary_key 'id'

to tell ActiveRecord to look for the “id” field and not the “session_id” field. Otherwise, you will get an application error due to a null value in the id field.

-Ira Burton?

As an added note to this, you might want to index the sessid field. In my session container performance tests there is quite a difference after a period of time between the two.

- This probably needs moving somewhere else, but a useful tip for ActiveRecordStore:

If you want to add something to the Sessions model (in my situation I wanted to add a user_id field to keep track of the user which this session belonged to so that I could monitor users who have an open session), you might spend hours pulling your hair out if you try something along the lines of


Session.find(:first, :conditions=> ["session_id = ?", @session.session_id]).updateAttribute("user_id",   @session[:user].id)  

A much better and easier way to do this, is to use the ‘model’ attribute. When using ActiveRecordStore as storage for sessions, a method ‘model’ is provided which gives you access to the DB implementation.

So, instead, do this:


@session.model.user_id = @session[:user].id

This will of course require creating a model ‘session.rb’ which extends ActiveRecord.

Performance considerations

In order to improve performance, you might also want to make the sessid and data fields just as big as the values you will store in the session. For example, if you know that the length of the session id values is 32 you can use a statement like this one:

 CREATE TABLE sessions {
  id INTEGER PRIMARY KEY,
  sessid CHAR(32),
  data TEXT
}

The length of the data field must be long enough to accomodate the flash and therefore cannot be easily calculated in advance (I think).

Note (for pgsql users): Due to the way text fields are handled, defining a text field with a maximum width doesn’t provide any performance benefits in PostgreSQL. TEXT fields will do just fine, in fact they are a bit faster than CHAR fields since there is no padding overhead.
- Stefan Kaes

Example MySQL table

A good definition for a MySQL table is as follows. The AUTO_INCREMENT keeps you from getting duplicate id errors.


 CREATE TABLE sessions (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  sessid  CHAR(32),
  data    TEXT,
  PRIMARY KEY(id),
  INDEX(sessid)
) 

-Ira Burton?

Extending ActiveRecordStore

You can customize the behaviour of the DB session store.

The following example is from Jeremy Kempers mailing list post: Avoiding session race condition in concurrent requests.

The ActiveRecordStore backend for CGI::Session allows you to plug in custom session classes. See session/active_record_store.rb in Action Pack. The default session class is a normal Active Record class named Session. An alternative, SqlBypass, is provided which duck-types with the AR class but uses straight SQL over the db connection You can use it as a starting point for pessimistic locking


class MyPessimisticSession < CGI::Session::ActiveRecordStore::SqlBypass
  # Pick a database connection for straight SQL access.
  self.connection = ActiveRecord::Base.connection

  # Override the finder class method to do a SELECT ... FOR UPDATE.
  def self.find_by_session_id(session_id)
    @@connection.select_one "..."
  end
end

# Use our custom session class.
CGI::Session::ActiveRecordStore.session_class = MyPessimisticSession

# Use the ActiveRecordStore for CGI sessions.
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.update(
  :database_manager => CGI::Session::ActiveRecordStore
)

MemCached Store

Install MemCached

Install MemCached (http://www.danga.com/memcached/)

  • If your distribution is debian(sid or sarge).
    
    apt-get install memcached
    

Install Ruby-MemCache


# gem install Ruby-MemCache

Or better with new and faster Mecache-Client

  1. gem install memcache-client

Tell Rails to Use MemCached

Change environment.rb to use MemCacheStore


config.action_controller.session_store = :mem_cache_store

To setup memcache-client we must to change enviroment.rb (ruby-memcache and memcache-client?)


require 'memcache'
memcache_options = {
   :compression => false,
   :debug => false,
   :namespace => "app-#{RAILS_ENV}",
   :readonly => false,
   :urlencode => false
}
memcache_servers = [ '192.168.1.150:2222', '192.168.1.150:2223' ]

Rails::Initializer.run do |config|
….
config.action_controller.session_store = :mem_cache_store

config.action_controller.fragment_cache_store = :mem_cache_store, memcache_servers, memcache_options

end


cache_params = *([memcache_servers, memcache_options].flatten)
CACHE = MemCache.new *cache_params
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.merge!({ ‘cache’ => CACHE })


juampe, thanks to Tom Fakes from craz8
memcache-client will automatic load so no need
<pre> request 'memcache'
-gorou

Category: Howto