Forum Discussion
Alex_B_
Jan 20, 2017Nimbostratus
Respond with cached content after timeout
As commented in the explanation for the "after" command (https://devcentral.f5.com/wiki/iRules.after.ashx) we can make some actions with the http request. It's possible to respond with cached content...
IheartF5_45022
Jan 21, 2017Nacreous
Hi yes you can do this but it's quite complicated unfortunately. You'll need to setup 2 virtual servers, vs_webapp, and vs_webapp_backend (the 2nd one needs to have the same name as the first one, but with '_backend' appended). The config of the 2 can be identical with the exception of the iRles (below) attached. In addition only vs_webapp needs to be enabled on the ingress VLAN.
vs_webapp has the following iRule attached;-
when RULE_INIT {
The wait interval in ms
set static::wait_ms 200
The low watermark for wait iterations - used for conditional GETs where stale content exists in WAM - when set low will result in more requests being served stale content
Use 0 here to achieve "serve stale on expiration" behaviour
set static::low_loop_cnt 20
The high watermark for wait iterations - used for non-conditional GETs where no stale content exists in WAM - this must be hi gher than $low_loop_cnt
when set too low will result in many concurrent requests being sent to OWS and if set too high may result in too many requests queued concurrently
would suggest 60<=high_loop_cnt<=300 but depends on the application and baseline testing of that.
set static::high_loop_cnt 240
}
when HTTP_REQUEST {
set debug 0
log prefix for connection/request tracking.
All debug logs will start with [xxxx.yyyy] where xxxx is the connection and yyyy is the request id
if {$debug} {
per request identifier
set prefix "\[[expr {int (rand() * 10000)}]\] "
this notifies backend VIP we are in debug mode and passes the connection prefix.
HTTP::header replace "X-tls-debug" $prefix
}
set table "[getfield [HTTP::host] : 1][string tolower [URI::decode [HTTP::path]]]"
if {$debug} {log local0. "${prefix}$table [clock clicks -milliseconds]"}
Check if there is already an in-progress request for this host/page combo
set i 1
while {[table lookup $table] ne "" } {
if {$i == 1} {
Mark (to the downstream virtual) this as a queued request by inserting header
HTTP::header insert X-mcms-queued "yes"
if {[table lookup $table] eq "COND"} {
Use the low loop watermark as this object is in cache so stale content could be served
set loop_cnt $static::low_loop_cnt
} else {
Use the high loop watermark as this object is not in cache so stale content cannot served
set loop_cnt $static::high_loop_cnt
}
}
Limit the number of times this gets executed so that we know whether it timed out (never returned)
or whether the request returned successfully and table entry was deleted by backend virtual
if {$i > $loop_cnt} {
Exceeded loop count
if {$debug} {log local0. "${prefix}Exceeded $loop_cnt, break out of loop"}
break
}
Wait for $static::wait_ms before checking again
hopefully by the time the request moves through, WAM will have a valid copy of the page cached
after $static::wait_ms
incr i
}
if {$debug && $i > 1 } {log local0. "${prefix}Stopped waiting after $i loops"}
if {[HTTP::header exists X-mcms-queued]} {
wait just a few milli-seconds to ensure request gets into cache
after 10
}
Choose downstream virtual (append backend to this virtual name) - WAM processing will take place before traffic hits this next virtual
virtual "[virtual]_backend"
}
and vs_webapp_backend has the following iRule attached;
when HTTP_REQUEST {
set debug 0
if {[HTTP::header exists "X-tls-debug"] } {
set prefix [HTTP::header "X-tls-debug"]
set debug 1
}
Set table name - this MUST match the name used in the iRule on the front-end VIP
set table "[getfield [HTTP::host] : 1][string tolower [URI::decode [HTTP::path]]]"
Check if conditional GET - this can be used to inform front-end whether to wait a short time (and serve stale content), or a long time
if {[HTTP::header exists "if-none-match"] || [HTTP::header exists "if-modified-since"]} {
set req_stat "COND"
if {[HTTP::header exists X-mcms-queued]} {
This was a conditional queued request that was not served from cache - could mean request still in progress but exceeded high_loop_cnt
if {$debug} {log local0. "${prefix}Request timed out $table.....invoke stand-in functionality"}
HTTP::respond 500 noserver Retry-After 600
return
} elseif {[table lookup $table] ne "" || [table lookup "200$table"] ne ""} {
Flag indicating request in progress is now set or flag indicating response just returned is set - serve stale
if {$debug} {log local0. "${prefix}race condition $table.....invoke stand-in functionality"}
HTTP::respond 500 noserver Retry-After 600
return
}
} else {
Mark as non conditional GET
set req_stat "N_COND"
}
}
Create table entry with timeout that will allow the high watermark to be reached by queued requests
table set $table $req_stat indefinite [expr {int($static::high_loop_cnt * $static::wait_ms / 1000) + 1}]
if {$debug} {log local0. "${prefix}session key $table is set to '[table lookup $table]' with expiry [expr {int($static::high_loop_cnt * $static::wait_ms / 1000) + 1}]"}
}
when LB_FAILED {
Either the pool is down, or the selected pool member has failed to complete the 3-way handshake
if {$debug} {log local0. "${prefix}LB_FAILED"}
Return a 500 to trigger WAM caching 'stand-in' functionality
HTTP::respond 500 noserver Retry-After 600
return
}
when HTTP_RESPONSE {
It was a page request for which we have now received a response - delete the flag (table entry) indicating that there is currently an request for this page in progress
table delete $table
}
Recent Discussions
Related Content
DevCentral Quicklinks
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com
Discover DevCentral Connects