Oracle WebLogic Session Replication: Cluster-Aware Session Management Without Load Balancer Dependency
The previous two posts in this series covered Nginx Plus sticky sessions and Redis-based session externalisation. Both solve the session affinity problem at different layers. This post covers a third approach — one that lives entirely within the WebLogic cluster itself.
WebLogic’s in-memory session replication means sessions are automatically copied to a secondary Managed Server in the cluster. If the primary server fails, the secondary takes over immediately — with the full session intact. No Redis cluster. No Nginx sticky routing. No user interruption.
Done correctly, it is one of the most elegant session HA solutions available on the Java EE stack.
How WebLogic Session Replication Works
WebLogic uses a primary/secondary replication model. When a session is created on a Managed Server, WebLogic designates:
- A primary server — where the session is actively used and stored in JVM heap
- A secondary server — a different Managed Server in the cluster that holds a replicated backup copy
Every time a session is modified and the request completes, WebLogic replicates the delta to the secondary server. The secondary holds a shadow copy in its own heap — ready to take over instantly if the primary fails.
Client Request
│
▼
[Load Balancer — any routing algorithm]
│
▼
[ManagedServer1 — Primary for this session]
Session: { userId: 42, cart: [...] } ← Active
│
│ Replication (end of request)
▼
[ManagedServer2 — Secondary for this session]
Session: { userId: 42, cart: [...] } ← Shadow copy
When ManagedServer1 goes down, the load balancer routes the next request to any available server. ManagedServer2 recognises it holds the secondary for this session ID, promotes itself to primary, and serves the request without loss. The user never knew anything happened.
No sticky routing required. No external store required.
Session Replication Modes in WebLogic
WebLogic supports several persistence store types. Choose based on your consistency vs performance requirements:
| Mode | Description | Use Case |
|---|---|---|
replicated | Synchronous in-memory replication to secondary | Production clusters, strong consistency |
replicated_if_clustered | Replication when clustered, memory-only when not | Dev/prod parity without config change |
async-replicated | Asynchronous replication — faster writes, slight inconsistency window | High-throughput, tolerable brief inconsistency |
async-replicated-if-clustered | Async version of the above | Same as above with dev/prod parity |
jdbc | Persisted to a database | Cross-datacenter HA, long-lived sessions |
memory | No replication (default) | Single server or stateless apps |
For most production WebLogic clusters, replicated_if_clustered is the right default. It gives you full synchronous replication in production and works without replication in standalone/development mode without a config change.
Requirements: Making Your Application Cluster-Aware
This is where most WebLogic session replication implementations fail. The cluster and WebLogic configuration can be perfect — but if the application isn’t cluster-aware, sessions will not replicate correctly, and you’ll see inconsistent state or silent data loss after failover.
There are four hard requirements.
1. The <distributable/> Tag in web.xml
This is the signal to the container that the application is designed for distributed deployment. Without it, WebLogic will not replicate sessions regardless of what you configure in weblogic.xml.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<!-- This tag is mandatory for session replication -->
<distributable/>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
Missing this tag is the single most common reason WebLogic session replication “doesn’t work.”
2. All Session Attributes Must Implement java.io.Serializable
For WebLogic to replicate a session to the secondary server, it must serialise every object stored in the session and transmit it over the network. Any session attribute that does not implement Serializable will either cause a replication failure silently or throw a runtime exception depending on your WebLogic version.
// WRONG — will break replication
public class UserContext {
private Connection dbConnection; // Not serializable — never store this
private HttpServletRequest request; // Not serializable
}
// CORRECT — all fields serializable
public class UserContext implements Serializable {
private static final long serialVersionUID = 1L;
private Long userId;
private String username;
private List<String> roles;
private Map<String, Object> preferences;
// getters/setters
}
Audit every object you put in HttpSession. If it holds a database connection, a thread, a file handle, or any non-serializable reference — it cannot go in the session when replication is enabled.
3. Always Call setAttribute() Again After Mutating Session Objects
This is the subtle one that catches experienced developers. WebLogic tracks session modifications by watching setAttribute() calls. If you retrieve a mutable object from the session, modify it in place, and never call setAttribute() again, WebLogic does not know the session changed — and the replication to the secondary never happens.
// WRONG — mutation without re-setAttribute
// The secondary server never sees this change
HttpSession session = request.getSession();
List<CartItem> cart = (List<CartItem>) session.getAttribute("cart");
cart.add(newItem); // Direct mutation
// No setAttribute call — replication misses this update
// CORRECT — always re-setAttribute after mutation
HttpSession session = request.getSession();
List<CartItem> cart = (List<CartItem>) session.getAttribute("cart");
cart.add(newItem);
session.setAttribute("cart", cart); // Triggers replication delta
This is particularly important for collections, maps, and custom objects. The rule is simple: if you changed it, set it back.
4. No Server-Affine Resources in Session
Sessions replicated to a secondary server will be picked up by that server on failover. If your session holds any resource that is tied to the original server — an open JMS session, a stateful EJB reference specific to a JVM, a cached local file path — those resources will be unavailable or invalid on the secondary. Design sessions to hold data only, not server-specific resource handles.
The Configuration: XML Files
weblogic.xml — Session Descriptor
This is the primary configuration file for session replication. It lives in WEB-INF/weblogic.xml of your WAR.
WebLogic supports two syntax styles for the session descriptor. The <session-param> style is the older format commonly found in WebLogic 10.x and 11g deployments — it uses explicit <param-name> / <param-value> pairs and is still fully supported in WebLogic 12c and 14c:
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app
xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app
http://xmlns.oracle.com/weblogic/weblogic-web-app/1.9/weblogic-web-app.xsd">
<session-descriptor>
<!-- session-param style: compatible with WebLogic 10.x / 11g / 12c / 14c -->
<session-param>
<param-name>PersistentStoreType</param-name>
<param-value>replicated</param-value>
</session-param>
<session-param>
<param-name>TimeoutSecs</param-name>
<param-value>3600</param-value>
</session-param>
<session-param>
<param-name>InvalidationIntervalSecs</param-name>
<param-value>60</param-value>
</session-param>
<session-param>
<param-name>CookieName</param-name>
<param-value>JSESSIONID</param-value>
</session-param>
<session-param>
<param-name>CookiePath</param-name>
<param-value>/</param-value>
</session-param>
<session-param>
<param-name>CookieSecure</param-name>
<param-value>true</param-value>
</session-param>
<session-param>
<param-name>CookieHttpOnly</param-name>
<param-value>true</param-value>
</session-param>
<session-param>
<param-name>URLRewritingEnabled</param-name>
<param-value>false</param-value>
</session-param>
<session-param>
<param-name>MaxInMemorySessions</param-name>
<param-value>10000</param-value>
</session-param>
</session-descriptor>
</weblogic-web-app>
Note: PersistentStoreType accepts replicated, replicated_if_clustered, async-replicated, async-replicated-if-clustered, jdbc, file, or memory. Using replicated (without the _if_clustered suffix) enforces replication unconditionally — WebLogic will throw a deployment error if the server is not part of a cluster, which makes misconfiguration visible early rather than silently falling back to in-memory behaviour.
WebLogic 14.1.1 compatibility note: The <session-param> style is fully valid in WLS 14.1.1. Oracle has maintained this syntax across all major versions for backward compatibility. The correct schema version for 14.1.1 is 1.9 — which is what the xsi:schemaLocation above references. No changes needed.
WLS 14.1.1 ships with Java EE 8 support using the standard javax namespace. The weblogic.xml namespace and <session-param> / <session-descriptor> structure are completely unaffected — identical across WLS 12c, 12.2.1.x, and 14.1.1. Your web.xml should use the Java EE 8 (javax) declaration:
<!-- web.xml — Java EE 8 / WLS 14.1.1 with javax namespace -->
<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">
<!-- Required for session replication -->
<distributable/>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
The modern element-based syntax (WebLogic 12c+) achieves the same result with cleaner XML:
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app
xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app
http://xmlns.oracle.com/weblogic/weblogic-web-app/1.9/weblogic-web-app.xsd">
<session-descriptor>
<!-- Use in-memory replication when deployed to a cluster -->
<persistent-store-type>replicated_if_clustered</persistent-store-type>
<!-- Session timeout in seconds (60 minutes) -->
<timeout-secs>3600</timeout-secs>
<!-- How often WebLogic checks for and invalidates expired sessions -->
<invalidation-interval-secs>60</invalidation-interval-secs>
<!-- Session cookie configuration -->
<cookie-name>JSESSIONID</cookie-name>
<cookie-path>/</cookie-path>
<cookie-domain>.yourdomain.com</cookie-domain>
<cookie-secure>true</cookie-secure>
<cookie-http-only>true</cookie-http-only>
<!-- Disable URL rewriting — forces cookie-only session tracking -->
<url-rewriting-enabled>false</url-rewriting-enabled>
<!-- Allow session sharing across web apps in the same EAR -->
<sharing-enabled>false</sharing-enabled>
<!-- Maximum sessions in memory per server before oldest are invalidated -->
<max-in-memory-sessions>10000</max-in-memory-sessions>
</session-descriptor>
</weblogic-web-app>
weblogic-application.xml — EAR-Level Session Sharing
If you have multiple web applications in an EAR that need to share the same session (common in large WebLogic deployments), configure session sharing at the EAR level in META-INF/weblogic-application.xml:
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-application
xmlns="http://xmlns.oracle.com/weblogic/weblogic-application"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-application
http://xmlns.oracle.com/weblogic/weblogic-application/1.8/weblogic-application.xsd">
<!-- Enable session sharing across web modules in this EAR -->
<session-descriptor>
<persistent-store-type>replicated_if_clustered</persistent-store-type>
<sharing-enabled>true</sharing-enabled>
<timeout-secs>3600</timeout-secs>
<cookie-name>JSESSIONID</cookie-name>
<cookie-domain>.yourdomain.com</cookie-domain>
<cookie-secure>true</cookie-secure>
<cookie-http-only>true</cookie-http-only>
</session-descriptor>
</weblogic-application>
Domain config.xml — Cluster and Replication Group Configuration
The cluster is configured in the WebLogic domain’s config.xml. The replication group settings control which server becomes the secondary for each primary — typically you want the secondary on a different physical host or rack for genuine HA.
<!-- Cluster definition -->
<cluster>
<name>AppCluster</name>
<!-- Unicast preferred over multicast for modern datacentres -->
<cluster-messaging-mode>unicast</cluster-messaging-mode>
<!-- Address used for cluster communication -->
<cluster-address>ms1.internal,ms2.internal,ms3.internal,ms4.internal</cluster-address>
<!-- Channel used for session replication traffic -->
<cluster-broadcast-channel>ReplicationChannel</cluster-broadcast-channel>
<!-- Replication type: man = in-memory -->
<session-flush-interval>-1</session-flush-interval>
<session-flush-threshold>-1</session-flush-threshold>
</cluster>
<!-- Managed Server 1 — Rack A -->
<server>
<name>ManagedServer1</name>
<listen-address>ms1.internal</listen-address>
<listen-port>7001</listen-port>
<cluster>AppCluster</cluster>
<!-- This server belongs to replication group RackA -->
<replication-group>RackA</replication-group>
<!-- Secondary copies for this server's sessions go to RackB -->
<preferred-secondary-group>RackB</preferred-secondary-group>
</server>
<!-- Managed Server 2 — Rack A -->
<server>
<name>ManagedServer2</name>
<listen-address>ms2.internal</listen-address>
<listen-port>7001</listen-port>
<cluster>AppCluster</cluster>
<replication-group>RackA</replication-group>
<preferred-secondary-group>RackB</preferred-secondary-group>
</server>
<!-- Managed Server 3 — Rack B -->
<server>
<name>ManagedServer3</name>
<listen-address>ms3.internal</listen-address>
<listen-port>7001</listen-port>
<cluster>AppCluster</cluster>
<replication-group>RackB</replication-group>
<preferred-secondary-group>RackA</preferred-secondary-group>
</server>
<!-- Managed Server 4 — Rack B -->
<server>
<name>ManagedServer4</name>
<listen-address>ms4.internal</listen-address>
<listen-port>7001</listen-port>
<cluster>AppCluster</cluster>
<replication-group>RackB</replication-group>
<preferred-secondary-group>RackA</preferred-secondary-group>
</server>
The replication group configuration ensures that if ManagedServer1 (Rack A) holds the primary for a session, the secondary is assigned to Rack B. A rack-level failure only affects the primary — the secondary survives and promotes immediately.
What This Means for the Load Balancer
With WebLogic in-memory replication properly configured, your load balancer can run a pure round-robin or least-connections policy with no session affinity at all. The application tier handles session continuity entirely.
upstream weblogic_cluster {
least_conn;
server ms1.internal:7001;
server ms2.internal:7001;
server ms3.internal:7001;
server ms4.internal:7001;
# No sticky directive needed
}
If you are running Nginx Plus with active health checks, you still want that — proactive upstream health detection is always better than passive. But the session affinity constraint is gone.
The Trade-off: Network Overhead and GC Pressure
In-memory replication is not free. For every request that modifies session state, WebLogic serialises the session delta and sends it to the secondary over the cluster network. The costs:
Serialisation overhead. Large sessions with complex object graphs are expensive to serialise on every request. Keep sessions lean — user identity, roles, lightweight state. Push large data (product catalogues, report results) to a cache or database, not the session.
GC pressure. Sessions live in JVM heap on both primary and secondary. At high concurrency with large sessions, this is a meaningful heap consumer. Monitor old-gen usage closely. With max-in-memory-sessions set in weblogic.xml, WebLogic will invalidate the oldest sessions when the limit is hit — tune this to match your heap allocation.
Network bandwidth. At 50,000 concurrent sessions of 10KB each, a full replication sweep is 500MB in-flight. Use async replication if you can tolerate a small inconsistency window, or invest in your cluster network bandwidth.
Comparison: The Three Session HA Approaches
| WebLogic Replication | Redis Session Store | Nginx Plus Sticky | |
|---|---|---|---|
| Where state lives | JVM heap (primary + secondary) | External Redis cluster | JVM heap (primary only) |
| Failover behaviour | Automatic, full session preserved | Automatic, full session preserved | Session lost if primary dies |
| Load balancer dependency | None | None | Sticky routing required |
| Application changes needed | Serializable, setAttribute discipline | Spring Session dependency | None |
| Operational complexity | WebLogic cluster config | Redis cluster + monitoring | Nginx Plus config |
| Best fit | WebLogic-native, no new infrastructure | Polyglot, stateless backends | Legacy apps, short-term fix |
The Bottom Line
WebLogic session replication is the right answer when you want genuine session HA without introducing external infrastructure. It lives entirely within the application tier, requires no changes to your load balancer, and provides automatic failover with zero session loss — provided your application is correctly cluster-aware.
The requirements aren’t complex: <distributable/> in web.xml, serializable session attributes, disciplined use of setAttribute(), and proper replication group configuration in config.xml. Get those four things right, and the load balancer becomes a pure traffic distributor — routing requests to any available server, without caring which server owns which session.
Prasad Gujar is a Platform Engineer specialising in Middleware, Kubernetes, and enterprise infrastructure. Views are his own.
