Weblogic JSessionID Persistence for Session Replication

Problem this snippet solves:

Persists HTTP requests on the primary and secondary server values found in the JSESSIONID cookie when the WebLogic servers implement session replication across two servers. The actual JSESSIONID session ID is not used for peristence. The server-specific token is used to create one persistence record per pool member. This should use less LTM memory than the standard JSESSIONID perstistence iRule which creates one persistence record per client session. See this article for details on the WebLogic behavior when replicating application session replication:

Load Balancers and the WebLogic Session Cookie

When an HTTP request is received, the iRule looks for a JESSSIONID cookie. If found, the primary and secondary server ID hashes are parsed from the JSESSIONID cookie. If a persistence record is found for the primary server ID, it is used to persist the client request. If there isn't a primary server ID persistence record, the secondary server ID is checked for a persistence record. If neither is found or the JSESSIONID cookie is null, the request is load balanced according to the load balancing method applied to the virtual server.

In order to ensure the second and subsequent requests follow the first, LTM must create a persistence record indicating the pool member to which the first request was load balanced. If the server is setting the jsessionid in a cookie, the persistence key value may be extracted from the server response to create the persistence record. If the server is setting the jsessionid in the URLs, source address persistence with a short timeout is recommended to track the original destination until the jsessionid is sent.

Please post any questions or issues to the iRules forum. This version of the iRule has not been tested in production, so I'm keen to find out about any issues you may encounter. Thanks, Aaron

How to use this snippet:

To ensure a new persistence record is followed when a request is re-load balanced in a client-side Keep-Alive connection, apply a OneConnect profile to the virtual server.

The iRule assumes the JSESSIONID cookie is in upper case when used as a cookie name. If this isn't the case, please update the example.

  • To persist on jsessionid, create the iRule below and create a custom Universal persistence profile.
  • Then use this custom Universal persistence profile as the Default Persistence profile on your Virtual Server.
  • Applying a Fallback Persistence profile of type Source Address Affinity with a host mask and a short timeout (the default source_addr persistence profile will do the trick) to your virtual server is also recommended.
  • Once done testing, comment out the LBSELECTED, LBFAILED and SERVER_CONNECTED events as they're there just for debug logging.

This iRule requires LTM v10. or higher.

Code :

# jsessionid persistence with primary and secondary server fallback
#
# Uses Universal Inspection Engine (UIE) persistence to select the primary server.
# If the primary is down, then the secondary server is selected.
#
# Cookie format from http://download.oracle.com/docs/cd/E11035_01/wls100/cluster/load_balancing.html#wp1028843 
# Assumes a cookie format of:
# sessionid!primary_server_id!secondary_server_id 
#
# Use UIE persistence based on the first server ID in the application's jsessionid cookie.
# If that fails, then try the second server ID.
# Check server responses to determine the secondary server ID's hash token and save the mappings in the session table
#
when RULE_INIT {

# Log debug to /var/log/ltm? 1=yes, 0=no
set static::j_debug 1

# LTM persistence timeout
set static::persist_timeout 3600

# Java sessiond ID cookie name
set static::j_cookie "JSESSIONID"

}
when HTTP_REQUEST {

if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: [HTTP::method] to [HTTP::host][HTTP::uri]\
cookie: [HTTP::cookie $static::j_cookie]"}

# Check if cookie has a value
if {[HTTP::cookie $static::j_cookie] ne ""}{

if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Scanning cookie: [HTTP::cookie $static::j_cookie]"}

# Parse the three ! delineated fields from the cookie
if {[scan [HTTP::cookie $static::j_cookie] {%[^!]!%[^!]!%[^!]} session pri_server_hash sec_server_hash] == 3}{

if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Cookie: [HTTP::cookie $static::j_cookie] pri: $pri_server_hash; sec: $sec_server_hash;\
pri UIE: [persist lookup uie $pri_server_hash]; sec UIE: [persist lookup uie $sec_server_hash]"}

# Persist off of the primary server token if it is present, not a null value and has a current persistence record
if {$pri_server_hash ne "" and $pri_server_hash ne "NONE" and [persist lookup uie $pri_server_hash] ne ""}{

if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Using existing primary UIE persistence"}
persist uie $pri_server_hash $static::persist_timeout

} elseif {$sec_server_hash ne "" and $sec_server_hash ne "NONE" and [persist lookup uie $sec_server_hash] ne ""}{
# Persist off of the secondary server token if it is present, not a null value and has a current persistence record
if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Using existing secondary UIE persistence"}
persist uie $sec_server_hash $static::persist_timeout
} else {
if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No existing persistence record"}
}
}
}
}

# Debug logging only. Remove this event after testing.
when LB_SELECTED {
if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Selected server [LB::server]"}
}
# Debug logging only. Remove this event after testing.
when LB_FAILED {
if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: LB failure for selected server [LB::server]"}
}
# Debug logging only. Remove this event after testing.
when SERVER_CONNECTED {
if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Connected server [IP::server_addr]:[TCP::server_port]"}
}

when HTTP_RESPONSE {

# Check if cookie has a value
if {[HTTP::cookie $static::j_cookie] ne ""}{

if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Cookie: [HTTP::cookie $static::j_cookie]"}

# Parse the second field of the cookie value.
#  The current server always sets its hash as the primary server hash in the second field of the cookie.
set pri_server_hash [getfield [HTTP::cookie $static::j_cookie] "!" 2]

if {$pri_server_hash ne ""}{

if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Adding: persist add uie $pri_server_hash $static::persist_timeout"}
# Add a persistence record for the primary server
persist add uie $pri_server_hash $static::persist_timeout

if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Cookie: [HTTP::cookie $static::j_cookie] set by [IP::server_addr]:[TCP::server_port],\
UIE: [persist lookup uie $pri_server_hash]"}
}
}
}
Published Mar 18, 2015
Version 1.0

Was this article helpful?

2 Comments

  • Hi there Hoolio

     

    If we do persistency with JSESSIONID on BIG-IP, what happens to the weblogic clustering and it`s session replication mechanism? I mean when a pool member is down I want the other pool member continue to respond to the client and it should be transparent to the client that the mem is down and the client should not be logged out...

     

    Thanks Shahrzad