JBoss.orgCommunity Documentation

Chapter 7. TorqueBox Caching

7.1. Overview
7.2. Clustering Modes
7.3. TorqueBox::Infinispan::Cache Options and Usage
7.4. ActiveSupport::Cache::Store Options and Usage

As a part of the JBoss AS, TorqueBox utilizes the Infinispan data grid. Infinispan offers a noSQL key/value store that can be replicated or distributed across a cluster, or run on a single machine locally. The cache is exposed as Ruby modules and classes through TorqueBox. There are two ways that applications can take advantage of this data store.

  • TorqueBox::Infinispan::Cache Direct Ruby access to the Infinispan cache
  • ActiveSupport::Cache::TorqueBoxStore Sinatra and Rails session, fragment and other framework caching

Each of these components allows applications to configure the clustering mode and other options of the underlying Infinispan cache, and fully supports JTA and XA distributed transactions in the container.

Infinispan offers a number of clustering modes that determine what happens when an entry is written to the cache.

Local

This is the default mode when TorqueBox runs non-clustered, roughly equivalent to the Rails MemoryStore implementation, though it has some advantages over a simple memory store, e.g. write-through/write-behind persistence, JTA/XA support, MVCC-based concurency, and JMX manageability.

Invalidation

This is the default mode when TorqueBox runs clustered. No data is actually shared among the nodes in this mode. Instead, notifications are sent to all nodes when data changes, causing them to evict their stale copies of the updated entry. This mode works very well for Rails' fragment and action caching.

Replicated

In this mode, entries added to any cache instance will be copied to all other cache instances in the cluster, and can then be retrieved locally from any instance. This mode is probably impractical for clusters of any significant size. Infinispan recommends 10 as a reasonable upper bound on the number of replicated nodes.

Distributed

This mode enables Infinispan clusters to achieve "linear scalability". Cache entries are copied to a fixed number of cluster nodes (2, by default) regardless of the cluster size. Distribution uses a consistent hashing algorithm to determine which nodes will store a given entry.

The TorqueBox::Infinispan::Cache supports a number of options. All components that use the cache as their underlying storage, e.g. ActiveSupport::Cache::TorqueBoxStore as well as the Cache class itself accept a hash of options. The common options for all cache components are:


TorqueBox::Infinispan::Cache Usage

The Cache object may be used to store and retrieve values from Infinispan. You can store just about anything: arbitrary Ruby data types, Java class instances, strings, numbers. The gamut. To use the Cache just make a new one providing it with initialization options.

        require 'torquebox-cache'
        cache = TorqueBox::Infinispan::Cache.new( :name => 'treasure', :persist=>'/data/treasure' )

Adding, removing and updating items in the cache is as you might expect.

# Put some stuff in the cache
cache.put( 'akey', "a string value" )
cache.put( "time", Time.now )
cache.put( user.id, user )

# Get it back again
time = cache.get( "time" )
user = cache.get( params[:id] )

# Remove something
cache.remove( 'akey' )
      

You also have typical hash-like methods which allow you to manipulate and query the cache for key information.

# Get all of the keys
keyset = cache.keys

# See if the cache contains a key
cache.contains_key? user.id

# Get everything! Caution, this could be very expensive
thewholeshebang = cache.all

# Clear it out. This happens asynchronously, so returns quickly
cache.clear

# Only put this in the cache if there isn't already something there with the same key
cache.put_if_absent( key, session[:user] )

# And you can replace a value in the cache conditionally 
# - if it hasn't changed since the last time you accessed it
t1 = Time.now
t2 = Time.now + 10
cache.put( "time", t1 )
# replaces t1 with t2
cache.replace( "time", t1, t2 )
# does NOT replace since the value is now t2
cache.replace( "time", t1, Time.now + 20 )
      

Increment, Decrement and Transactions

TorqueBox::Infinispan::Cache also provides some convenience methods for atomically incrementing or decrementing a value in the cache. Additionally, the Cache provides transactional blocks with Cache#transaction do ..., and all Cache operations automatically participate in XA transactions if they are called from within a TorqueBox.transaction block.

cache.increment('mykey') # 1
cache.increment('mykey') # 2
cache.decrement('mykey') # 1

# Automatically participates in XA transactions
Torquebox.transaction do
  cache.increment('mykey')
  # ...
end

# And can scope transactions itself
cache.transaction do
  cache.decrement('mykey')
  end
      

To read more about transactions see Chapter 15, TorqueBox Distributed Transactions.

As noted in Chapter 6, TorqueBox Web Applications the TorqueBox store can be used for all of the implicit caching within Rails and session storage within Sinatra.

In addition to the common options for TorqueBox::Infinispan::Cache as noted above, ActiveSupport::Cache::TorqueBoxStore supports all the options of the existing Rails implementations, including the advanced features of MemCacheStore, along with a few more to control how data replication occurs amongst the nodes in a cluster.

Rails and Sinatra configuration details can be found in Chapter 6, TorqueBox Web Applications. Usage is essentially transparent to the application beyond this configuration.