WildFly Configuring HTTP Session in a cluster

This tutorial discusses in detail how to configure HTTP Session Management in clustered Web applications, based on the new changes introduced in WildFly 17.

Web applications which are running in a cluster-aware configuration (“ha” or “full-ha” profile) can survive the lifespan of a single server by adding the element into the web.xml file. Example:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <distributable/>
</web-app>

This is sufficient to achieve that your HTTP Session survives a server crash/restart, provided that at least one server is still available. You can further specialize the way HTTP Session is managed in the cluster using different strategies, depending on your WildFly version.

HTTP Session Management in WildFly 17 or newer

WildFly 17 introduced the distributable-web subsystem, which manages a set of session management profiles that encapsulate the configuration of a distributable session manager:

   <subsystem xmlns="urn:jboss:domain:distributable-web:2.0" default-session-management="default" default-single-sign-on-management="default">

You can check the default-session-management attribute from the CLI as follows:

 /subsystem=distributable-web:read-attribute(name=default-session-management)
{
    "outcome" => "success",
    "result" => "default"
}

The HTTP Session Management is handled, under the hoods,by Infinispan, therefore if you want to check its settings, you have to reach the infinispan-session-management attribute under the distributable-web subsystem. Example, for the “default” infinispan-session-management:

 /subsystem=distributable-web/infinispan-session-management=default:read-resource
{
    "outcome" => "success",
    "result" => {
        "cache" => undefined,
        "cache-container" => "web",
        "granularity" => "SESSION",
        "affinity" => {"primary-owner" => undefined}
    }
}

As you can see, there are several configurable attribute for the infinispan-session-management:

  • cache-container: This references the Infinispan cache-container into which session data will be stored.
  • cache: This references a cache for the related cache-container. If undefined, the default cache of the associated cache container will be used.
  • granularity: This defines the session manager mapping for the individual cache entries:
  • affinity: This attribute defines the affinity that a web request should have for a given server.

Configuring HTTP Session Granularity

The granularity attribute can have the following values:

  • SESSION: Stores all session attributes within a single cache entry. This is generally more expensive than ATTRIBUTE granularity, but preserves any cross-attribute object references.
  • ATTRIBUTE: Stores each session attribute within a separate cache entry. This is generally more efficient than SESSION granularity, but does not preserve any cross-attribute object references.

If your application does not share any object references between attributes, users are strongly advised to use ATTRIBUTE granularity. Using ATTRIBUTE granularity, each session attribute is stored in a separate cache entry. This means that a given request is only required to replicate/persist those attributes that were added/modified/removed/mutated in a given request. For read-heavy applications, this can dramatically reduce the replication/persistence payload per request.

Here is how you can set the granularity to “ATTRIBUTE” for the default session manager:

/subsystem=distributable-web/infinispan-session-management=default/:write-attribute(name=granularity,value=ATTRIBUTE)

Configuring HTTP Session Affinity

The affinity attribute defines the affinity that an HTTP request has for a given WildFly server. The affinity of the associated web session determines the algorithm for generating the route to be appended onto the session ID (within the JSESSIONID cookie, or when encoding URLs). Possible values are:

  • affinity=none: HTTP requests won’t have affinity to any particular node.
  • affinity=local: HTTP requests will have an affinity to the server that last handled a request for a given session. This is the standard sticky session behavior.
  • affinity=primary-owner: HTTP requests will have an affinity to the primary owner of a given session. This is the default setting. Behaves the same as affinity=local if the backing cache is not distributed nor replicated.

Adding a new Session Management Profile

You can define a new Session Management Profile as follows:

/subsystem=distributable-web/infinispan-session-management=custom-profile/:add(cache-container=web,granularity=ATTRIBUTE)

Now, in order to link the “custom-manager” Session Management Profile to your application, several options are available:

Include a WEB-INF/distributable.xml file in your application, which links your customer profile:

<?xml version="1.0" encoding="UTF-8"?>
<distributable-web xmlns="urn:jboss:distributable-web:1.0">
    <session-management name="custom-profile"/>
</distributable-web>

You can also link the Session Management Profile through the existing jboss-all.xml:

<?xml version="1.0" encoding="UTF-8"?>
<jboss xmlns="urn:jboss:1.0">
    <distributable-web xmlns="urn:jboss:distributable-web:1.0">
        <session-management name="custom-profile"/>
    </distributable-web>
</jboss>

Defining Session Management Profile at application level

It is also possible to use deployment-specific settings for your session management profile. This can be done by adding an infinispan-session-management element into the /WEB-INF/distributable-web.xml. Example:

<?xml version="1.0" encoding="UTF-8"?>
<distributable-web xmlns="urn:jboss:distributable-web:1.0">
  <infinispan-session-management cache-container="web" cache="demo-cache" granularity="ATTRIBUTE">
            <local-affinity/>
  </infinispan-session-management>
</distributable-web>

As an alternative, also the file META-INF/jboss-all.xml can contain deployment specific settings for your session profile:

<?xml version="1.0" encoding="UTF-8"?>
<jboss xmlns="urn:jboss:1.0">
    <distributable-web xmlns="urn:jboss:distributable-web:2.0">
      <infinispan-session-management cache-container="web" granularity="SESSION">
        <primary-owner-affinity/>
      </infinispan-session-management>
    </distributable-web>
</jboss>

Using jboss-web.xml to manage max-active-sessions

Prior to WildFly 17, the file jboss-web.xml contained the core settings for configuring HTTP Session replication. Here is the basic structure of it:

<jboss-web>
  ...
  <max-active-sessions>...</max-active-sessions>
  ...
  <replication-config>
    <replication-granularity>...</replication-granularity>
    <cache-name>...</cache-name>
  </replication-config>
  ...
</jboss-web>

Since WildFly 17, the replication-config section has been deprecated. You can still use max-active-sessions to impose a limit to the number of currently active sessions. Please notice that this limit works differently, depending if you are running an application in a cluster or not (that is if you are using in web.xml).

  • Clustered Web applications: if you reach the max-active-sessions and a new request arrives, the request is accepted and an old session is passivated to disk using a Least Recently Used (LRU) algorithm
  • Not Clustered Web applications: when exceeding the max-active-sessions limit, a new request will fail with an IllegalStateException.

Configuring HTTP Session replication before WildFly 17

Prior to WildFly 17, you can rely on jboss.web.xml to configure the replication configuration for HTTP Sessions. For example, the following jboss-web.xml snippet shows how to set the replication-granularity to SESSION for your Web application:

<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_10_0.xsd">
   <replication-config>
      <replication-granularity>SESSION</replication-granularity>
    </replication-config>
</jboss-web>

Older WildFly versions could also configure the replication-trigger of your HTTP Session as in the following example:

<jboss-web>
  <replication-config>
    <replication-trigger>SET_AND_NON_PRIMITIVE_GET</replication-trigger>
    <replication-granularity>SESSION</replication-granularity>
  </replication-config>
</jboss-web>

There are 4 possible levels of replication of your session data: each one triggers the session replication between nodes in a different way: 

Level Description Speed
SET This is the best option for performance. The session is replicated only if an attributed is explicity modified with setAttribute method. good
SET_AND_GET With this option any attributed that is get/set is marked as dirty even if it’s not written back to the session. This leads to a significant performance degradation. slow
SET_AND_NON_
PRIMITIVE_GET
This is the default option. It works the same as SET_AND_GET except for primitive system types (String, Integer, Long). Since they are immutable objects they are not replicated when a get is issued. average
ACCESS This is the most conservative option. It causes the session to be marked as dirty whenever it is accessed. Since a the session is accessed during each HTTP request, it will be replicated with each request. Note that use of this option can have a significant performance impact, so use it with caution v.slow

In the following table you can see a set of commands and whether they make the session dirty.

Command SET SET_AND_NP_GET SET_AND_GET ACCESS
HttpSession session; NO NO NO YES
String s = (String)session.getAttribute(“x”); NO NO YES YES
Person p = (String)session.getAttribute(“p”); NO YES YES YES
Person p = (String)session.getAttribute(“p”);
session.setAttribute(p);
YES YES YES YES