Forum Discussion

Tuomas_Jormola_'s avatar
Tuomas_Jormola_
Icon for Nimbostratus rankNimbostratus
Apr 17, 2008

Strange things happening while attempting universal persistence using JSESSIONID

Hi,

I'm setting up a Java web application behind a pair of F5 BIG-IP LTMs

running operating system version 9.3.1. The application is hosted on

a cluster of machines running Resin servlet container.

The backend nodes are not using any kind of shared session store,

so we need to direct each session to the same backend node that

served the initial request. Resin is configured to use JSESSIONID cookie

to store the session identifier. So based on documentation and

examples found here on DevCentral, I created a universal persistence

profile, and assigned that to the virtual server as the only

persistency profile. The iRule powering the profile is included below

(it also supports setting the session id in the request URI,

but cookies are preferred).


when RULE_INIT {
  set ::debug 1
}
when CLIENT_ACCEPTED {
  if { $::debug } {
    set client [IP::client_addr]:[TCP::client_port]
  }
}
when HTTP_RESPONSE {
  if { [HTTP::cookie exists "JSESSIONID"] } {
    set jsessionid [HTTP::cookie "JSESSIONID"]
    persist add uie $jsessionid 3600
    if { $::debug } {
      log local0. "NEW PERSIST ENTRY client>$client< JSESSIONID>$jsessionid<"
    }
  }
}
when HTTP_REQUEST {
  set jsessionid ""
  set cookie_id ""
  set uri [HTTP::uri]
  set uri_id [findstr $uri "jsessionid=" 11 "?"]
  if { [HTTP::cookie exists "JSESSIONID"] } {
    set cookie_id [HTTP::cookie "JSESSIONID"]
  }
  if { $cookie_id ne "" } {
    set jsessionid $cookie_id
  } elseif { $uri_id ne "" } {
    set jsessionid $uri_id
  }
  if { $jsessionid ne "" } {
    persist uie $jsessionid 3600
  }
  if { $::debug } {
    set table ""
    if { $jsessionid ne "" } {
      set table [persist lookup uie $jsessionid]
    }
    if { $table eq "" } {
      log local0. "client>$client< uri>$uri< cookie_id>$cookie_id< uri_id>$uri_id< JSESSIONID>$jsessionid< table>null<"
    } else {
      log local0. "client>$client< uri>$uri< cookie_id>$cookie_id< uri_id>$uri_id< JSESSIONID>$jsessionid< table>[lindex $table 0] [lindex $table 1] [lindex $table 2]<"
    }
  }
}
when LB_SELECTED {
  if { $::debug } {
    set server [LB::server addr]:[LB::server port]
    log local0. "client>$client< server>$server<"
  }
}
when LB_FAILED {
  if { $::debug } {
    if { $jsessionid ne "" } {
      if { $table ne "" } {
        log local0. "client>$client< JSESSIONID>$jsessionid< table>[lindex $table 0] [lindex $table 1] [lindex $table 2]<"
      } else {
        log local0. "client>$client< JSESSIONID>$jsessionid< table>null<"
      }
    } else {
      log local0. "client>$client< JSESSIONID>null< table>null<"
    }
  }
  if { $jsessionid ne "" } { persist delete uie $jsessionid }
  if { $table ne "" } { LB::reselect pool [lindex $table 0] }
}

However, when this setup is in place, we noticed that requests of

a session were not actually always directed to the same node,

even though the logic seemed to work as sane values were printed in

the syslog by iRule debugging statements. I give an example using

the debug statements in the iRule and HTTP data captured using tcpdump

on the BIG-IP LTM when the web application was accessed with a browser.

Setup:

Client IP: 194.137.112.98

BE Nodes: 192.168.135.16:6889 and 192.168.135.17:6889

Here we have the first request from the client

(irrelevant HTTP headers omitted)


GET /littleredcap-demo/app/?lang=fi HTTP/1.1

As you can see, we have no session cookie.

The request was served by 192.168.135.17 with response


HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=abccmG4bh7OVaeO7KjCLr; path=/

We now have the cookie. Corresponding debug log entries from the iRule:


Apr 17 14:30:24 tmm tmm[875]: Rule persist_jsessionid : client>194.137.112.98:8283< uri>/littleredcap-demo/app/?lang=fi< cookie_id>< uri_id>< JSESSIONID>< table>null<
Apr 17 14:30:24 tmm tmm[875]: Rule persist_jsessionid : client>194.137.112.98:8283< server>192.168.135.17:6998<
Apr 17 14:30:25 tmm tmm[875]: Rule persist_jsessionid : NEW PERSIST ENTRY client>194.137.112.98:8283< JSESSIONID>abccmG4bh7OVaeO7KjCLr<

So the iRule logs that new universal persistence entry has been created

for this session id, and we're using 192.168.135.17:6998 as

the backend node for the session.

But then, with next request hitting the virtual server, things are

getting weird. The request that the browser sends in order to get some

CSS resource needed to render the front page looks like this.


GET /littleredcap-demo/css/app-layout.css HTTP/1.1
Cookie: JSESSIONID=abccmG4bh7OVaeO7KjCLr

We have the session id, as iRule log entry confirms


Apr 17 14:30:25 tmm tmm[875]: Rule persist_jsessionid : client>194.137.112.98:8276< uri>/littleredcap-demo/css/app-layout.css< cookie_id>abccmG4bh7OVaeO7KjCLr< uri_id>< JSESSIONID>abccmG4bh7OVaeO7KjCLr< table>test_pool 192.168.135.17 6998<

This log entry finds the original backend node associated for

the session, 192.168.135.17. But the tcpdump capture and access log

on the backend 192.168.135.16 revealed that the actual request was

redirected to the node 192.168.135.16 instead! So persistency for

this esablished session was not honoured, and the default

scheduling algorithm that chooses the node from the pool was

applied instead.

Any idea why this happened? Can someone point out the flaw in

the iRule? Thanks.

10 Replies

  • I may be missing something in your explanation, but is there a specific reason that you cannot use the BigIP builtin cookie persistence mechanism? With this the BigIP sends its own cookie to the client that indicates the LB node.

     

     

    Kevin
  • Just one question: are you using oneconnect ?

     

     

    If yes you should try to configure oneconnect with a mask of 255.255.255.255 or disable it.
  • Posted By nmenant on 04/17/2008 7:10 AM

     

     

    Just one question: are you using oneconnect ?

     

     

    If yes you should try to configure oneconnect with a mask of 255.255.255.255 or disable it.

     

     

     

    oneconnect is not in use.
  • Hi,

     

     

    Posted By kevin.stewart on 04/17/2008 6:44 AM

     

     

    I may be missing something in your explanation, but is there a specific reason that you cannot use the BigIP builtin cookie persistence mechanism? With this the BigIP sends its own cookie to the client that indicates the LB node.

     

     

    Kevin

     

     

     

    We want to minimize the number of cookies sent, and since a session id is already made available by the application, and the BIG-IP is supposed to be able to utilizie it, why not use that.

     

    Also in the future, the requirements might change so that we can't use cookies at all,

     

    in which case the application server is configured to deliver the session id in the request uri.

     

    In order to support this, we would have to write a universal persistence profile anyway.
  • Looks like adding a OneConnect profile with mask 255.255.255.255 to the virtual server solved the problem.
  • Glad it works with oneconnect.

    the reason is simple: Click here

    By default persist command will work for when the TCP connection is established. Then it won't do any more loadbalacing for each HTTP request except if ordered to (pool pool1 member 10.0.0.1)

    When you enable oneconnect each HTTP request is load balanced. Really useful when facing piplening !

    
    "When a OneConnect profile is enabled for a virtual server, and an HTTP client sends multiple requests within a single connection, the BIG-IP LTM is able to process each HTTP request individually. The BIG-IP LTM sends the HTTP requests to different destination servers if necessary.
    Without a OneConnect profile enabled for the virtual server, the BIG-IP system performs load-balancing for each TCP connection."
  • Sounds like you got it working, but I think I saw a bug in your iRule that could cause some problems later on.

    To get the session ID out of the URL you use the following:

    
      set uri_id [findstr $uri "jsessionid=" 11 "?"]

    This will get the string after "jsessionid=' until a "?" is found. However, URL parameters are delimited by a "&", not a "?". So, if you have a url like this:

    http://somedomain.com/some/path?jsessionid=MYSESSIONID

    it'll work fine, but if you have this:

    http://somedomain.com/some/path?jsessionid=MYSESSIONID&someotherparam=FOOBAR

    it won't work, as the jsessionid string you get back will end up being "MYSESSIONID&someotherparam=FOOBAR"

    If your application doesn't use other query params, or always puts jsessionid as the last query param then it will work, but if any other params are put after jsessionid then you'll get the wrong value.
  • aherrman,

     

     

    I think the logic is okay. The jsessionid is typically inserted in response redirects and content links before the query string:

     

     

    http://somedomain.com/some/path;jsessionid=MYSESSIONID?param1=val1&someotherparam=FOOBAR

     

     

    Aaron
  • Posted By hoolio on 04/25/2008 12:58 AM

     

     

    aherrman,

     

     

    I think the logic is okay. The jsessionid is typically inserted in response redirects and content links before the query string:

     

     

    http://somedomain.com/some/path;jsessionid=MYSESSIONID?param1=val1&someotherparam=FOOBAR

     

     

    Aaron

     

     

     

    Yep, at least the environment we use (Resin 3.0 serving web applications created with Wicket framework) generates URLs containing the sessionid in this format, and the iRule copes with that. I did not have to change the iRule at all to get this working, all that was needed was the OneConnect profile.
  • Hi,

     

     

    We have exactly same problem, can you post here you oneconnect profile settings?

     

     

    tia,