<title>TCP-Ring Distributed Sessions</title>

<summarylist/>

<p/>Resin's TCP-ring protocol for distributed sessions can improve 
reliability and scalability over JDBC-based distributed sessions.
Because sessions are always duplicated on separate servers, TCP-ring
sessions do not have a single point of failure. JDBC-based sessions
are vulnerable to the session database going down.  As the number of
servers increases, JDBC-based sessions can start overloading the
backing database.  With TCP-ring sessions, each additional server
shares the backup load, so the main scalability issue reduces to network
bandwidth.  In combination with sticky-session load balancing,
TCP-ring sessions can approach zero overhead when the majority of
requests read session state without modification.

<section title="Configuration">

TCP-Ring configuration must tell each host the servers in the TCP-ring
and it must enable the <var/tcp-store/> in the session configuration.
Because session configuration is specific to a virtual host and a
web-application, each web-app needs <var/tcp-store/> enabled
individually.  It's not possible to enable <var/tcp-store/> for an
entire site.

<p/>Most sites using Resin's load balancing will already have the
<var/&lt;srun>/> configured.  Each <var/&lt;srun>/> block corresponds to a
host, including the current host.  Since TCP-ring sessions uses
Resin's srun protocol, each host must listen for srun requests.

<example title='resin.conf fragment'>
...
&lt;http-server>
  &lt;srun id='a' host='192.168.0.1' srun-index='1'/>
  &lt;srun id='b' host='192.168.0.2' srun-index='2'/>
  &lt;srun id='c' host='192.168.0.3' srun-index='3'/>
  &lt;srun id='d' host='192.168.0.4' srun-index='4'/>
  ...
  &lt;web-app id='myapp'>
    ...
    &lt;session-config>
      &lt;tcp-store/>
    &lt;/session-config>
  &lt;/web-app>
&lt;/http-server>
...
</example>

<p/>Usually, hosts will share the same resin.conf.  Each host will be
started with a different <var/-server xx/> to select the correct
block.  On Unix, startup will look like:

<example title="Starting Host&nbsp;C on Unix">
resin-2.0.x> bin/httpd.sh -conf conf/resin.conf -server c start
</example>

<p/>On NT, Resin will generally be configured as a service:

<example title="Starting Host&nbsp;C on NT">
resin-2.0.x> bin/httpd -conf conf/resin.conf -server c -install-as ResinC
</example>

<subsection title="always-save-session">

<p/>Resin's distributed sessions needs to know when a session has
changed in order to save the new session value.  Although Resin can
detect when an application calls <var/HttpSession.setAttribute/>, it
can't tell if an internal session value has changed.  The following
Counter class shows the issue:

<example title="Counter.java">
package test;

public class Counter implements java.io.Serializable {
  private int count;

  public int nextCount() { return count++; }
}
</example>

<p/>Assuming a copy of the Counter is saved as a session attribute,
Resin doesn't know if the application has called <var/nextCount/>.  If it
can't detect a change, Resin will not backup the new session, unless
<var/always-save-session/> is set.  When <var/always-save-session/> is
true, Resin will back up the session on every request.

<example>
...
&lt;web-app id='/foo'>
...
&lt;session-config>
  &lt;tcp-store/>
  &lt;always-save-session/>
&lt;/session-config>
...
&lt;/web-app>
</example>

<p/>In contrast to JDBC-based sessions, Resin will ignore the
<var/always-load-session/> flag for TCP-based sessions.  Because of
the more sophisticate TCP-ring protocol, <var/always-load-session/> is
not needed.

</subsection>

<subsection title="Serialization">

Resin's distributed sessions relies on Java serialization to save and
restore sessions.  Application object must <var/implement
java.io.Serializable/> for distributed sessions to work.

</subsection>

</section>

<section title="Protocol Examples">

<subsection title="Session Request">

To see how TCP-ring sessions work, consider a case where
the load balancer sends the request to a random host.  Host&nbsp;C owns the
session but the load balancer gives the request to Host&nbsp;A.  In the
following figure, the request modifies the session so it must be saved
as well as loaded.

<center>
<ct:img src='srunc.gif'/>
</center>

<p/>The session id encodes the owning host.  The example session
id, <var/caaMbyA/>, decodes to an srun-index of 3, mapping
to Host&nbsp;C.  Host&nbsp;A must know the owning host
for every cookie so it can communicate with the owning srun.
The example configuration defines all the sruns Host&nbsp;A needs to
know about.  If Host&nbsp;C is unavailable, Host&nbsp;A can use its
configuration knowledge to use Host&nbsp;D as a backup
for <var/caaMbyA/> instead..

<p/>When the request first accesses the session, Host&nbsp;A asks
Host&nbsp;C for the serialized session data (<var/2:load/>).
Since Host&nbsp;A doesn't cache the session data, it must
ask Host&nbsp;C for an update on each request.  For requests that
only read the session, this TCP load is the only extra overhead,
i.e. they can skip <var/3-5/>.  The <var/always-save-session/>
flag, in contrast, will always force a write.

<p/>At the end of the request, Host&nbsp;A writes any session
updates to Host&nbsp;C (<var/3:store/>). If always-save-session
is false and the session doesn't change, this step can be skipped.
Host&nbsp;A sends
the new serialized session contents to Host&nbsp;C.  Host&nbsp;C saves
the session on its local disk (<var/4:save/>) and saves a backup
to Host&nbsp;D (<var/5:backup/>).

</subsection>

<subsection title="Sticky Session Request">

Smart load balancers that implement sticky sessions can improve
TCP-ring performance.  In the previous request, Resin's TCP-ring
sessions maintain consistency for dumb load balancers or twisted
clients like the AOL browsers.  The cost is the additional network
traffic for <var/2:load/> and <var/3:store/>.  Smart load-balancers
can avoid the network traffic of <var/2/> and <var/3/>.

<center>
<ct:img src='same_srun.gif'/>
</center>

<p/>Host&nbsp;C decodes the session id, <var/caaMbyA/>.  Since it owns
the session, Host&nbsp;C gives the session to the servlet with no work
and no network traffic.  For a read-only request, there's zero
overhead for TCP-ring sessions.  So even a semi-intelligent load
balancer will gain a performance advantage.  Normal browsers will have
zero overhead, and bogus AOL browsers will have the non-sticky
session overhead.

<p/>A session write saves the new serialized session to disk
(<var/2:save/>) and to Host&nbsp;D (<var/3:backup/>).
<var/always-save-session/> will determine if Resin can take advantage
of read-only sessions or must save the session on each request.

</subsection>

<subsection title="Failover">

Since the session always has a current copy on two servers, the load
balancer can direct requests to the next server in the ring.  The
backup server is always ready to take control.  The failover will
succeed even for dumb load balancers, as in the non-sticky-session
case, because the srun hosts will use the backup as the new owning
server.

<p/>In the example, either Host&nbsp;C or Host&nbsp;D can stop and
the sessions will use the backup.  Of course, the failover will work
for scheduled downtime as well as server crashes.  A site could
upgrade one server at a time with no observable downtime.

</subsection>

<subsection title="Recovery">

When Host&nbsp;C restarts, possibly with an upgraded version of Resin,
it needs to use the most up-to-date version of the session; its
file-saved session will probably be obsolete.  When a "new" session
arrives, Host&nbsp;C loads the saved session from both the file and
from Host&nbsp;D.  It will use the newest session as the current
value.  Once it's loaded the "new" session, it will remain consistent
as if the server had never stopped.

</subsection>

<subsection title="No Distributed Locking">

Resin's TCP-ring sessions does not lock sessions.  For browser-based
sessions, only one request will execute at a time.  Since browser
sessions have no concurrently, there's no need for distributed
locking.  However, it's a good idea to be aware of the lack of
distributed locking.

</subsection>

</section>

<section title="Conclusion">

Although reliability generally will end up costing some performance,
the trick for a good implementation is to increase reliability with a
minimal cost.  In some environments, using JDBC distributed sessions
or simple file-based persistent sessions will improve a site's
robustness with low-enough cost.  Because of its scalability, it's
more likely that Resin's TCP-ring distributed sessions will be a
better choice for most deployment configurations.

</section>
