<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="cache">
  <title>TorqueBox Caching</title>

  <section>
    <title>Overview</title>
    <para>
      As a part of the JBoss AS, TorqueBox utilizes the <ulink
    url="http://infinispan.org">Infinispan</ulink> 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.
      <itemizedlist>
        <listitem><code>TorqueBox::Infinispan::Cache</code> Direct Ruby access to the Infinispan cache</listitem>
        <listitem><code>ActiveSupport::Cache::TorqueBoxStore</code> Sinatra and Rails session, fragment and other framework caching</listitem>
        <!--        <listitem><code>dm-infinispan-adapter</code> Infinispan-backed <ulink url="http://datamapper.org">DataMapper</ulink> adapter</listitem> -->
      </itemizedlist>

      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.

    </para>
  </section>


  <section id="caching-clustering-modes">
    <title>Clustering Modes</title>

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

    <formalpara>
      <title>Local</title>

      <para>This is the default mode when TorqueBox runs non-clustered,
      roughly equivalent to the Rails <code>MemoryStore</code>
      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.</para>
    </formalpara>

    <formalpara>
      <title>Invalidation</title>

      <para>
      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 best when
      the infinispan data store is backed by a single, canonical data source
      such as a relational database. It also works very well for Rails' 
      fragment and action caching, and is the default mode for the cache
      underlying <code>ActiveSupport::Cache::TorqueBoxStore</code>.
    </para>
    </formalpara>

    <formalpara>
      <title>Replicated</title>

      <para>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.</para>
    </formalpara>

    <formalpara>
      <title>Distributed</title>

      <para>This is the default mode for a la carte caches when TorqueBox runs
      clustered.  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.</para>
    </formalpara>
  </section>

  <section id="caching-options-and-usage">
    <title>TorqueBox::Infinispan::Cache Options and Usage</title>

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

    <table>
      <title>Cache options</title>

      <tgroup cols="3">
        <colspec align="left" />

        <thead>
          <row>
            <entry>Option</entry>

            <entry>Default</entry>

            <entry>Description</entry>
          </row>
        </thead>

        <tbody>
          <row>
            <entry><parameter>:mode</parameter></entry>

            <entry><parameter>:distributed</parameter></entry>

            <entry>Any of the following will result in
            <emphasis>replicated</emphasis> mode: <itemizedlist>
                <listitem>
                  <para><parameter>:r</parameter></para>
                </listitem>

                <listitem>
                  <para><parameter>:repl</parameter></para>
                </listitem>

                <listitem>
                  <para><parameter>:replicated</parameter></para>
                </listitem>

                <listitem>
                  <para><parameter>:replication</parameter></para>
                </listitem>
              </itemizedlist> Any of the following will result in
            <emphasis>distributed</emphasis> mode: <itemizedlist>
                <listitem>
                  <para><parameter>:d</parameter></para>
                </listitem>

                <listitem>
                  <para><parameter>:dist</parameter></para>
                </listitem>

                <listitem>
                  <para><parameter>:distributed</parameter></para>
                </listitem>

                <listitem>
                  <para><parameter>:distribution</parameter></para>
                </listitem>
              </itemizedlist> Any of the following will result in
              <emphasis>invalidation</emphasis> mode: <itemizedlist>
                <listitem>
                  <para><parameter>:i</parameter></para>
                </listitem>
                <listitem>
                  <para><parameter>:inv</parameter></para>
                </listitem>
                <listitem>
                  <para><parameter>:invalidated</parameter></para>
                </listitem>
                <listitem>
                  <para><parameter>:invalidation</parameter></para>
                </listitem>
              </itemizedlist>
              Any other value for
            <parameter>:mode</parameter> will result in
            <emphasis>distributed</emphasis> when clustered and
            <emphasis>local</emphasis> otherwise.</entry>
          </row>

          <row>
            <entry><parameter>:sync</parameter></entry>

            <entry>true</entry>

            <entry>The coordination between nodes in a cluster can happen
            either synchronously (slower writes) or asynchronously (faster
            writes).</entry>
          </row>

          <row>
            <entry><parameter>:name</parameter></entry>

            <entry>{the application's name}</entry>

            <entry>The <parameter>:name</parameter> option enables you to
            create multiple cache stores in your app, each with different
            options. It's also a way you can configure multiple apps to
            share the same cache store.</entry>
          </row>
          <row>
            <entry><parameter>:persist</parameter></entry>

            <entry>false</entry>

            <entry>The <parameter>:persist</parameter> option enables
              file-based persistence of the cache entries.  Any value for
              <parameter>:persist</parameter> which is a path to a writable
              directory will be used for cache storage.</entry>
          </row>
          <row>
            <entry><parameter>:transaction_mode</parameter></entry>
            <entry><parameter>:transactional</parameter></entry>
            <entry>By default, all local caches are transactional. If you
              don't need transactions, set this to <parameter>:non_transactional</parameter>.
            </entry>
          </row>
          <row>
            <entry><parameter>:locking_mode</parameter></entry>

            <entry><parameter>:optimistic</parameter></entry>

            <entry>Starting with Infinispan 5.1 the supported transaction models are 
              <parameter>:optimistic</parameter> and <parameter>:pessimistic.</parameter>
              The <parameter>:optimistic</parameter> option defers lock
              acquisition to transaction prepare time, reducing lock
              acquisition duration and increasing throughput. With the
              <parameter>pessimistic</parameter> model, cluster wide-locks are
              acquired on each write and released after the transaction
              completes.  
            </entry>
          </row>
        </tbody>
      </tgroup>
    </table>

    <formalpara>
      <title>TorqueBox::Infinispan::Cache Usage</title>
      <para>
        The <code>Cache</code> 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 
        <code>Cache</code> just make a new one providing it with initialization
        options.
      </para>
    </formalpara>
    <para>
      <programlisting>
        require 'torquebox-cache'
        cache = TorqueBox::Infinispan::Cache.new( :name => 'treasure', :persist=>'/data/treasure' )</programlisting>
    </para>
    <para>
      Adding, removing and updating items in the cache is as you might expect.
      <programlisting>
# 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' )
      </programlisting>

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

      <programlisting>
# 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 )
      </programlisting>

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

      <programlisting>
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
      </programlisting>
      <para>To read more about transactions see <xref linkend='transactions'/>.</para>
    </para>

  </section>

  <section id='torque-box-store-usage'>
    <title>ActiveSupport::Cache::TorqueBoxStore Options and Usage</title>

    <para>
      As noted in <xref linkend='web'/> the TorqueBox store can be used for 
      all of the implicit caching within Rails and session storage within
      Sinatra. <emphasis>NB:</emphasis>The <code>TorqueBoxStore</code> uses 
      <code>:invalidation</code> as it's default mode. If you will be using
      the Rails cache to explicitly store and retreive values across a cluster
      you should change the default clustering mode to <code>:replicated</code>
      or <code>:distributed</code> mode.
      <programlisting>config.cache_store = :torque_box_store, {:mode => :distributed}</programlisting>
    </para>

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

    <para>
      Rails and Sinatra configuration details can be found in <xref linkend='web'/>.
      Usage is essentially transparent to the application beyond this configuration.
    </para>
  </section>

</chapter>


