SPDY/HTTP2 Profile Impact on Variable Use
I recommend against use of
table
simply because it suspends iRule processing (K12962) potentially impacting performance. Especially on an F5 with heavy traffic. There are occasions where table
is needed, but this is not one of those cases.
static::variable (array)
What Kai posted above should accomplish the task just fine. However, it is important to note this method slightly changes behavior causing a potential problem. The scenario is unlikely, but I believe deserves some attention. The use of table may also result in the same problem. I have not tested the historical method of setting the variable in
CLIENT_ACCEPTED
.
To prevent this post from spanning pages of info, I won't go into the gory details. I'll just list steps demonstrating the problem.
Assuming there are 2 pools - pool_1/pool_2 Browser is a client connecting with HTTPv2.
- Configure VS with iRule performing default pool selection via static variable with pool_1.
- Make request from a browser to the VS to populate variable.
- Change the default pool on the VS to pool_2.
- Delete pool_1.
- Refresh the page from the browser. This results in a TCL error and a browser error (Chrome reported ERR_SPDY_PROTOCOL_ERROR).
The error persisted regardless of what I did until one of the following occurred:
- A new connection made on the same TMM instance to update the variable.
- Manually delete the connection on the F5 forcing my browser to open a new one.
- Wait for the connection to time-out.
If the purpose is to determine the default pool, adding a OneConnect profile to the VS can accomplish this. With a OneConnect profile, the load balancing decision occurs on each request. This results in the behavior of
LB::server pool
returning the default pool as long as it is run prior to any selection command such as pool
.
This behavior can be demonstrated in a rule like this (assuming the VS has a oneconnect profile):
when HTTP_REQUEST {
log local0. "should always be the default pool - [LB::server pool]"
switch -glob -- [HTTP::path] {
/images/* { pool image_caching }
/app/* { pool main_app }
*.js { pool javascript_storage }
}
}
This is true with the exception of a couple version where F5 "changed the behavior" : K00725997
Conditionally set variableAnother way to accomplish the task is to conditionally set the variable in
HTTP_REQUEST
. This has the advantage of looking and behaving virtually identical to setting in CLIENT_ACCEPTED
.
when HTTP_REQUEST {
we don't care about non-HTTPv2 requests
if {![HTTP2::active]} { return }
if {![info exists default_pool]} {
set default_pool [LB::server pool]
}
log local0. "This should always match the default pool on the VS - ${default_pool}"
}
How it works (I think)
I haven't found official F5 information detailing variable scope and HTTP2, but I can explain what I've seen.
Each HTTP2 stream being processed concurrently receives its own scope for load balancing and iRule variables. Almost as if each concurrent session is its own little connection. This can be demonstrated/verified with an iRule like this:
when HTTP_REQUEST {
we don't care about non-HTTPv2 requests
if {![HTTP2::active]} { return }
if {![info exists default_pool]} {
set default_pool [LB::server pool]
log local0. "default_pool variable set for conncurent stream - [HTTP2::concurrency], set to - ${default_pool}"
} else {
log local0. "default_pool variable exists on conncurent stream - [HTTP2::concurrency], set to - ${default_pool}"
}
}
As you refresh the page, logging indicates each concurrent session sets the variable only once.
If each conncurrent stream receives its own scope, it's logical to conclude setting a variable in each consumes more memory. However, when using HTTPv2, each client should only use a single TCP connection in lieu of concurrent TCP connections, so the trade off is setting a variable in each HTTP2 concurrent session as apposed to setting the variable once across multiple TCP connections.