HA Web Sessions via Database Persistence



    Occasionally we receive input from the JBoss AS user community that they'd like to see an option to use database persistence as a mechanism to make web sessions highly available. The main (and still recommended) mechanism for making web sessions HA is to use our standard replication-based approach, which uses the JBoss Cache distributed caching library to replicate sessions to other nodes in the cluster. However, I (Brian Stansberry) have decided to put some effort into production a quality session management implementation that uses database persistence instead of replication. This wiki page is meant to serve as documentation of that solution.


    Why use Database Persistence for web sessions?


    The basic use case we hear for this is environments where sessions need to be available to AS instances located across a WAN. JBoss Cache/JGroups clusters can span a WAN but often users find it impractical to configure their cluster(s) in that way. However, their IT infrastructure already supports making RDBMS data accessible across the WAN. So, persisting sessions to the DB makes them available across the WAN.

    Why not use the Tomcat PersistentManager?


    Tomcat provides a session manager called the PersistentManager that can accomplish a similar thing.  Tomcat's documentation treats it as experimental/untested, although I know people are using it. I originally intended to go enhance that class as a way of reaching my objective, but I eventually found that it would be a lot of work to add to PersistentManager some of the important functionality that JBoss AS' existing distributed session management technology has. Doing that would also lead to a lot of duplicated code. It was much cleaner to use the existing AS distributed session manager, and swap in a database-backed store for the standard JBoss Cache one. This was made much easier by the fact that the interaction with JBC was abstracted out to an SPI; an implementation of that SPI that talks to a RDBMS was fairly straightforward.


    Some key advantages of the AS's distributed session management technology that this approach allows:


    1. Detection of whether a session is "dirty" with persistence to the DB only occurring if it is (For those familiar with JBoss AS web session clustering, this is the SET, SET_AND_NON_PRIMITIVE_GET, SET_AND_GET "replication-trigger" concept).
    2. Handling of cross-context requests, with all sessions affected during the overall request being persisted.
    3. Better handling of servlet spec notifications as sessions are persisted or loaded from the database.


    Allowing Use of Database Persisted Sessions


    JBoss AS by default will detect any case where a webapp's web.xml includes a <distributable/> element, and will automatically switch the webapp's session manager to JBossCacheManager, the standard JBC-based distributed session manager. To use DB-persisted sessions, this preference for JBossCacheManager needs to be disabled. This can be done by editing the $JBOSS_HOME/server/.../deployers/jbossweb.deployer/META-INF/war-deployers-jboss-beans.xml file and adding a property to the WarDeployer bean:


    <bean name="WarDeployer" class="org.jboss.web.tomcat.service.deployers.TomcatDeployer">
        . . .
        <!-- "False" disables overriding the session manager for distributable webapps -->
        <property name="overrideDistributableManager">false</property>


    Once this property is set to false, the AS will respect whatever session manager your webapp is configured to use. The next step is to tell the AS your webapp wants to use DB-persistence. JBoss AS / JBossWeb / Tomcat all allow specification of the sesssion manager class and configuration of the manager via a context.xml file located in the webapp.[1][2] So, to use database-backed session persistence, add a context.xml with content like the following to your webapp's WEB-INF directory:


    <Context cookies="true" crossContext="true">
       <Manager className="org.jboss.web.tomcat.service.session.persistent.DataSourcePersistentManager"


    See [1] for discussion of the attributes on the Context element, the example above is just illustrative and the values of those attributes don't affect session management. What's relevant is the Manager element. That's what configures session management.  To use database persistence for your sessions, you need to configure the following attributes:



    classNameFully-qualified class name of the org.apache.catalina.Manager implementation, which in this case is org.jboss.web.tomcat.service.session.persistent.DataSourcePersistentManager
    dataSourceJndiNameThis manager uses a DataSource to communicate with the database. This required attribute specifies the JNDI name under which the datasource is bound. See below for details on the datasource.


    There are a number of other optional configuration attributes that are discussed below.

    Why not configure all this via jboss-web.xml?


    The jboss-web.xml file is the standard JBoss-specific webapp deployment descriptor, why isn't that used to configure this stuff?


    Simple answer is changing the parsing and handling of jboss-web.xml is overly disruptive for a feature that is being added to the 5..1.x AS codebase. The goal was to add this capability while making very minimal changes to the existing AS 5.x code. The existing AS code can handle processing a context.xml file, and the processing allows configuration of arbitary properties on the session manager, so using the context.xml approach lets us plug in and configure a new session manager without affecting other code. Perhaps in AS 6 some of the configuration options for this session manager will be configurable via jboss-web.xml, although it is quite possible configuration will remain as is. Community input on this is of course appreciated.


    Database Configuration


    Use of DataSourcePersistentManager requires that your database includes a properly configured table to store the web sessions. The appropriate DDL to create this table varies between database vendors, but the following DDL shows the necessary structure:


    CREATE TABLE httpsessions (app VARCHAR(255) NOT NULL, id VARCHAR(255) NOT NULL, 
         fullId VARCHAR(255) NOT NULL, creationtime BIGINT NOT NULL, 
         maxinactive BIGINT NOT NULL, version INT NOT NULL, lastaccess BIGINT NOT NULL, 
         isnew CHAR(1) NOT NULL, valid CHAR(1) NOT NULL, metadata VARBINARY NULL, 
         attributes LONGVARBINARY NOT NULL, 
         CONSTRAINT app_id PRIMARY KEY (app, id))


    The name of the table and of the columns can be changed if the DataSourcePersistentManager is configured to understand the non-standard names.  The creationtime and lastaccess columns need to be able to store a java long; the maxinactive and version columns need to store a java int. The metadata and attributes columns store serialized java objects. The metadata column is currently unused; attributes stores the session attributes map and should be sized large enough to store your largest sessions.


    You can add a synthetic primary key field if you like; just make sure there is a UNIQUE INDEX on app + id.


    Adding an index on the app column may help performance.


    DataSource Configuration


    As noted above, DataSourcePersistentManager uses a datasource to communicate with the database, so you'll need to ensure an appropriate datasource is deployed, with its JNDI name configured provided to DataSourcePersistentManager via the dataSourceJndiName property in context.xml.


    Use of a local-tx-datasource is required. DataSourcePersistentManager's persistence layer uses JDBC transactions.


    Optional Configuration Attributes


    Besides the required configuration attributes described above, DataSourcePersistentManager exposes a number of other properties that can be configured by adding attributes to the Manager element in the webapp's context.xml.


    The following set of attributes configure how the manager interacts with the database.


    connectionNameValue of the username param to pass to DataSource.getConnection. May be null in which case the no-arg variant of getConnection will be called.
    connectionPasswordValue of the password param to pass to DataSource.getConnection. May be null.
    sessionTableName of the database table in which sessions are stored. Default is httpsessions.
    sessionAppColName of the column in which the name of the webapp with which a session is associated is store. Default is app.
    sessionIdColName of the column in which the core, immutable part of a session id is stored. Default is id. This column plus the sessionAppCol form the unique index for the table.
    sessionFullIdColName of the column which stores the full session id, including any mutable element (e.g. a jvmRoute) that is added to the core session id. Default is fullid.
    sessionCreationTimeColName of the column in which the long representing the time when the session was created is stored. Default is creationtime.
    sessionMaxInactiveColName of the column which stores the max number of milliseconds the session can remain unaccessed before it can be expired by the container. Default is maxinactive.
    sessionVersionColName of the column which stores the session's "version". The version is incremented each time the session is persisted. Default is version.
    sessionLastAccessedColName of the column which stores the session's last accessed timestamp as a long. Default is lastaccess.
    sessionNewColName of the column which stores a flag indicating whether the session is new (i.e. not yet joined by the client.) Default is isnew.
    sessionValidColName of the column which stores a flag indicating whether the session is valid. Default is isvalid.
    sessionMetadataColName of the column which may, in the future, store serialized metadata about the session. Currently nothing is stored. Default is metadata.
    sessionAttributeColName of the column in which the serialized session attribute map is stored. Default is attributes.


    The following set of attributes configure the basic operation of the manager:


    cleanupIntervalMinimum interval, in seconds, between executions of a special process that cleans old "abandoned" sessions from the database. A session would be abandoned if the only server that was handling requests for it shutdown before it expired, and no further requests for it came in that caused it to fail over to another server. Such sessions won't be expired via the normal session expiration checks, so a special process periodically runs to clean them from the database. Default is 14400 seconds, i.e. 4 hours.
    replicationTriggerStringConfigures what activity during a request causes the session manager to decide to persist the session state at request end. See discussion of replication-trigger at http://www.jboss.org/community/wiki/ConfigurationChangesforClusteredWebApplicationsinAS5.
    maxUnreplicatedIntervalDetermines the maximum interval between requests, in seconds, after which a request will trigger persistence of the session's timestamp regardless of whether the request has otherwise made the session dirty. See discussion of max-unreplicated-interval at http://www.jboss.org/communitywikiConfigurationChangesforClusteredWebApplicationsinAS5.
    useJKSee the discussion of use-jk at  http://www.jboss.org/communitywikiConfigurationChangesforClusteredWebApplicationsinAS5.
    maxActiveAllowedDetermines the maximum number of active sessions allowed. See discussion of max-active-sessions at http://www.jboss.org/community/wiki/DistributableHttpSessionPassivation.
    useSessionPassivationEnable/disable session passivation, which in the case of this manager means simply removing the session from memory (since sessions are always stored in the persistent store). See discussion of use-session-passivation at http://www.jboss.org/community/wiki/DistributableHttpSessionPassivation.
    passivationMinIdleTimeSee discussion of passivation-min-idle-time at http://www.jboss.org/community/wiki/DistributableHttpSessionPassivation.
    passivationMaxIdleTimeSee discussion of passivation-max-idle-time at http://www.jboss.org/community/wiki/DistributableHttpSessionPassivation.
    processExpiresFrequencyA background process thread periodically (by default every 10 seconds) calls into the session manager giving it a chance to do background processes (e.g. expire or passivate sessions.) This configuration controls value N whereby in 1/N calls to run background processes the manager will actually try to do cleanup work. Default is 1, i.e. every time the manager gets the chance, i.e. every 10 seconds by default, it will do cleanup work. A higher value, say 6, will result in the manager only doing cleanup work once a minute.
    sessionNotificationPolicySee the discussion of session-notification-policy at  http://www.jboss.org/communitywikiConfigurationChangesforClusteredWebApplicationsinAS5.




    Trying It Out


    Attached to this page are two jars that contains the necessary code for this feature. This is an initial version built from the AS 5.1.0.GA codebase. If you want to play with database persistence the attached jboss-web-service.jar can be dropped in the $JBOSS_HOME/server/.../deploy/jbossweb.sar folder replacing the jar of the same name already there. The attached jboss-web-deployer.jar can be dropped in the $JBOSS_HOME/server/.../deployers/jbossweb.deployer folder replacing the jar of the same name already there


    Once this jar is available in a more formal release I'll remove the attached one from this page.


    2009/07/08 11:40 CDT : updated jar with version that includes ability to configure passivation via context.xml.

    2009/07//13 12:57 CDT: added missing jboss-web-deployer.jar and updated jboss-web-service.jar with some bug fixes

    2009/07/16 09:15 CDT: updated with fix for sessions being removed from db on undeploy when useSessionPassivation=false

    2009/11/04 02:45 CDT: user reported problems using previous version with AS 5.1.0.GA; hopefully these work better. This will be the last update of this jar.



    [1] http://tomcat.apache.org/tomcat-6.0-doc/config/context.html

    [2] http://tomcat.apache.org/tomcat-6.0-doc/config/manager.html