series-the-table-command
9 TopicsThe table Command: Basics
The BIG-IP is a powerful tool when it comes to managing your application traffic. One of the reasons is F5's CMP technology: the ability to scale across many CPUs by running independently on each of them. As any advanced iRule writer could tell you, though, this independence can sometimes make it difficult or impossible to treat the system as one cohesive whole. The problem is that maintaining a consistent set of data spread across processors like that is a long-standing one in computer science. There's just no way to do it in a general enough way for something like Tcl global variables to work and still get good performance. In v10, the session table was the one small escape hatch for any iRule to have a globally available set of shared data and not be pinned to a single processor. Although it has existed in iRules for some time, the session command was never designed to be a general purpose tool for this sort of thing: the interface has a lot of unnecessary complexity, and there is significant missing functionality. But when you get down to brass tacks, it works; it solves the problem and maintains the speed we need.With version 10.1, we've given the session table some long-sought functionality, and revamped its iRules interface completely to give users a new, cleaner, full-featured way to keep track of global data. We are very proud to introduce the table command...There's quite a lot to cover, so we'll take it slowly.The BasicsAt its heart, the table command is just another way to access the session table, and the fundamental concepts are not much different than they were before. The session table is a global table shared across all processing units in the BIG-IP system. It stores values that are indexed by keys. An entry in the session table consists of a key, a value, and some associated metadata (which will be covered later). The session command allows you to set a key/value pair, lookup a value for a given key, or delete a key/value pair. It looks like this: session add <mode> <key> <data> [<timeout>] session lookup <mode> <key> session delete <mode> <key> where <mode> is a value that no longer has any meaning in v10, and <key> can take some baroque syntax, which is also meaningless in v10. When those commands are used, it might look like: session add uie "$key any pool any virtual" $data 180 session lookup uie "$key any pool" session delete uie "$key" To someone unfamiliar with iRules, this might not make a lot of sense. What's "uie"? Why the "any pool" business? And what does any of this have to do with sessions?When we set out to improve this interface, the first thing we did was to come up with a new command name, one that might hint that this is something general purpose that we're dealing with: hence, the table command. A new command name also ensures that any existing iRules using the session command wouldn't be interfered with. The second thing we did was get rid of all those bulky unused parameters. The table command’s basic form looks like: table set $key $data 180 table lookup $key table delete $key That's already an improvement! But we didn't stop there. Not by a long shot.2.8KViews0likes2CommentsThe table Command: Subtables
With version 10.1, we've given the session table some long-sought functionality, and revamped its iRules interface completely to give users a new, cleaner, full-featured way to keep track of global data. We are very proud to introduce the table command... Basics New Ways To Set Data New Ways To Alter Data Data Expiration Advanced Data Expiration Subtables Counting The Fine Print Examples In this sixth part of the series on the usage of the new table command, we discuss how you can create and manage subtables within the session table. Subtables Whew! Still with me? Good, because we're just now getting to some of best stuff. One of the most powerful changes to the session table in v10.1 is the concept of a subtable, which is simply a named set of entries. It lets you organize your entries in groups, and lets you act on those groups. A subtable is basically a table in and of itself, with some special abilities. Subtables only contain entries, though; you can’t nest subtables. Here's what they look like: They are trivial to use: every current table subcommand simply takes a -subtable <name> parameter. So you can do things like: table set -subtable $tbl $key $data table lookup -subtable $othertbl $key table delete -subtable $thirdtbl $key That's all there is to it! Doesn't look like much, does it? But this is very powerful stuff! Maybe you want a different set of data for each user, or for each client IP address, or for each virtual server? No problem! table set -subtable "$username" $key $data table add -subtable "[IP::client_addr]" $key $data table replace -subtable "[virtual]" $key $data Since you can name your subtables however you want, you can now have shared variables with any arbitrary scope! And there's an easy way to clean up: table delete -all -subtable "$username" will remove every entry in a subtable in one fell swoop. But wait! There's more! With subtables comes a new and long-desired ability: the ability to list or count the number of keys: table keys -subtable <name> [-count|-notouch] You’ll note that unlike all the other table subcommands, the subtable name is not optional here: there’s no way to use it on entries that aren’t in a subtable. Also note that by default, retrieving the list of keys will touch each one, but getting the count will never touch any entries. Isn't this AWESOME!?! This feature is going to simplify a lot of your iRules, and give you the ability to do things that simply weren't feasible before. I can't wait to see all the creative uses you come up with! That covers all of the new session table features. Now we'll cover some ways of putting everything together... Continue reading part seven in our series: Counting3KViews0likes5CommentsThe table Command: The Fine Print
With version 10.1, we've given the session table some long-sought functionality, and revamped its iRules interface completely to give users a new, cleaner, full-featured way to keep track of global data. We are very proud to introduce the table command... Basics New Ways To Set Data New Ways To Alter Data Data Expiration Advanced Data Expiration Subtables Counting The Fine Print Examples In this eighth part of the series on the usage of the new table command, we discuss the limitations, a few advanced details and plans for the future. Limitations As fantastic as the new session table is, it does have its limitations. You can't use the table command in RULE_INIT or any other global event. There's no way to access the session table outside of tmm. The ability to count and list keys is limited to subtables. Timestamps are limited to 1-second resolution, so there's no way to do counts for sub-second events. As you saw, you can use it for accurate counting, but that's not nearly as easy to use as we'd like it to be. We tried very hard to come up with a command, or set of commands, that would hide all the table manipulation and just do the counting for you, but everything we came up with was either very complicated or would always use the most computationally expensive method for counting, and that seemed like a bad tradeoff. Advanced details Because there is only one global session table, the session command and the table command both act on the same data. For example, if you insert an entry into the session table with the session command, you could then later look up that data (perhaps in another iRule) using the table command. However, the session command cannot access any data in a subtable in any way. The session table is mirrored to the standby, and this cannot be disabled. While most of the table commands are fairly low weight, getting the list of keys in a subtable is very heavy weight. Because of the new features that subtables have, you might be tempted to use one giant subtable for all your data. This is probably a bad idea, because of the way that the session table is arranged across all the CPUs in a CMP system. Entries in the session table are distributed across all processors. Subtables themselves are also distributed across all processors. However, all of the entries in a given subtable are on the same processor. So if you put all of your entries (or the vast majority of them) into the same subtable, then one CPU will take a disproportionate amount of memory and load. Which you probably don't want. So, in general, more, smaller subtables are a better arrangement than fewer, larger subtables. Plans for the future We think that we've provided a great set of features here, and that they will help you a great deal, but we recognize that it's probably not perfect. Also, we have lots of other plans for future enhancements to the session table and the table command. Ideally, this would completely take the place of global variables in iRules. We would love to get your feedback on what is most important to fix or add. The best way to do that is not to merely suggest features, but to supply us with actual use cases. What can't you do with the existing commands? What can you do, but only in an ugly way? Continue reading part nine in the series: Examples1.1KViews0likes4CommentsThe table Command: New Ways To Set Data
With version 10.1, we've given the session table some long-sought functionality, and revamped its iRules interface completely to give users a new, cleaner, full-featured way to keep track of global data. We are very proud to introduce the table command... In this second part of the series on the usage of the new table command, we discuss ways to assign data to the session table. New Ways To Set Data The session add command will always set a value associated with a key, overwriting anything that was there before it. If you are keeping track of the last URI that a user visited, for example, then this isn't a problem: the new URI value in the table overwrites the old one. But if you were trying to roll your own type of persistence, then this behavior is less good. (In reality, you should use the persist command for this sort of thing, as it's designed to handle situations like this. I'm just using persistence as a concrete example.) You might try to write an iRule that looks something like: set newpmbr [session lookup uie $key] if { $newpmbr == {} } { session add uie $key $curpmbr } else { pool [LB::server pool] member $newpmbr } Makes sense: use an existing entry if there is one, or add one if not. But what happens if two connections come in at very close to the same time? The first one will do a lookup, see that there's no entry in the table, and start to add an entry. But since there are now multiple processors working at the same time, the second connection could do its lookup after the first connection did its lookup, but before the first connection added an entry. If that happens, both connections will act as if they are the first, and they could go to different servers! Oops! To address problems like that, we've added two other ways to add data to the session table: table set $key $data table add $key $data table replace $key $data table set acts just like session add does: if the key doesn't already exist in the table, it adds the entry, and if it does exist, it overwrites what's there. table add won't overwrite an existing entry, it will only add an entry if the key doesn't already exist. table replace is the opposite: it will only update an existing entry. If the key doesn't already exist in the session table, it does not add a new entry. If you've seen memcache, then this syntax will seem very familiar to you. But if you haven't, and you don't want to have three different commands in your iRules that all set data, you can instead supply flags to the table set command that provide the exact same behavior. table set -excl is equivalent to table add. table set -mustexist is equivalent to table replace. Some users might prefer one way over the other, and using the flags might be more obvious to a newcomer over the more subtle set/add/replace distinction. But they really are just synonyms. Also, even though it's not shown right here, all of these commands take an optional timeout, as well as many other parameters. The examples of table command syntax in this series won't give a complete command set, so we can focus on just one feature at a time, and won't strictly follow the usual syntax conventions. A complete syntax reference can be found in the iRules documentation. With these commands, we can now rewrite the above example in a way that will actually work: set newpmbr [table add $key $curpmbr] if { $newpmbr != $curpmbr } { pool [LB::server pool] member $newpmbr } (Note that this isn’t a complete example of persistence, because things like timeout handling and persistence for different pools aren’t covered.) Because table add will never overwrite an existing entry, it doesn't matter how many connections happen at the same time. The first one will always win and write its pool member to the session table entry, and all the other connections will get that pool member as a result of the table add command. Note that no matter if the table add command wrote the entry or not, it returns the value of the entry, so all connections accessing this session table entry will get the same result, no matter which one was first.1.5KViews0likes2CommentsThe table Command: New Ways To Alter Data
With version 10.1, we've given the session table some long-sought functionality, and revamped its iRules interface completely to give users a new, cleaner, full-featured way to keep track of global data. We are very proud to introduce the table command... Basics New Ways To Set Data New Ways To Alter Data Data Expiration Advanced Data Expiration Subtables Counting The Fine Print Examples In this third part of the series on the usage of the new table command, we discuss ways to alter data in the session table. New Ways To Alter Data Of course, that's not the only way that two iRules running at the same time could stomp on each others changes. Very often you'll want to use iRules to do something like count how many requests a user makes. Without the table command, you might try to do something like: set reqs [session lookup uie $user] if { $reqs == {} } { set reqs 0 } incr reqs session add uie $user $reqs But this runs into the same problem: two different connections could lookup the value at the same time, each one will incremement it once, and each one will then write back the same value. So even though two connections came in, the value only increases by one. Not good. With the table command, there are two ways to alter values in the session table: table incr [-mustexist] $key [$value] table append [-mustexist] $key $value table incr will add the specified value (or 1 if you don't specify a value) to the existing entry (or 1 if you don't specify a value), and table append will append the specified value. Both commands will return the value after it was changed. What happens if there isn't an entry with the specified key? By default, an entry is created with an empty value (for incr, that would be 0, and for append it would be the empty string), which is then incremented or appended to as appropriate. If you don't want an entry created by default, you can specify the -mustexist flag, which will make the command do nothing and return the empty string if there’s no existing entry. Because these are atomic operations (meaning that they can't be interrupted by another iRule running another table command), that means the above example become just one line: set reqs [table incr $user] How spiffy is that? There are two important caveats with table incr: first, don't try to increment a value that isn't a number. For example, if you have a key with an associated value of "foo", trying to increment that will cause a Tcl error ("Illegal value"). Second, you'll note that there is no table decr command. Why? If you want decr, then that means that you're counting things that go away (e.g. counting the number of users that are logged in). What happens if the connection is killed and you never get to run table decr? Your count will be wrong, and there won't be any way to fix it. There are ways of keeping an accurate count of things like that, which we'll cover in another section. If you're counting things that can't go away (e.g. a user can't "unmake" an HTTP request), then table incr is fine. Continue reading part four in the series: Data Expiration929Views0likes1CommentThe table Command: Examples
With version 10.1, we've given the session table some long-sought functionality, and revamped its iRules interface completely to give users a new, cleaner, full-featured way to keep track of global data. We are very proud to introduce the table command... Basics New Ways To Set Data New Ways To Alter Data Data Expiration Advanced Data Expiration Subtables Counting The Fine Print Examples In this last part of the series on the usage of the new table command, we discuss various examples of the use of the table command. Examples Now that we've covered all of the pieces of the table command, let's see some larger real-world uses of making them work together, not only simplfying existing iRules, but making new things possible. Limiting Connections To A VIP On the Counting page, we gave one powerful example iRule that would have been too bulky and/or unreliable to do before: limit a VIP to a certain number of connections. That example can be easily modified to limit those connections by IP address as well. If you compare it to this example when CLIENT_ACCEPTED { set tbl "connlimit:[IP::client_addr]" set key "[TCP::client_port]" if { [table keys -subtable $tbl -count] > 1000 } { event CLIENT_CLOSED disable reject } else { table set -subtable $tbl $key "ignored" 180 set timer [after 60000 -periodic { table lookup -subtable $tbl $key }] } } when CLIENT_CLOSED { after cancel $timer table delete -subtable $tbl $key } you can see that only the first two lines changed! We've gone from one global subtable to a subtable for each IP address. This is an example of the power of subtables and using them for scoping. Blocking DNSFlood Attacks This example to blacklist IPs if they make too many DNS queries per second, uses global arrays to keep track of the number of requests made in the current second, as well as the blacklist of IPs. when RULE_INIT { set ::maxquery 100 set ::holdtime 600 } when CLIENT_DATA { set srcip [IP::remote_addr] set curtime [clock second] if { [ info exists ::blacklist($srcip) ] } { if { $::holdtime > [expr ${curtime} - $::blacklist($srcip) ] } { drop return } unset ::blacklist($srcip) } if { [ info exists ::usertable(time,$srcip)] and $curtime == $::usertable(time,$srcip) } { incr ::usertable(freq,$srcip) if { $::usertable(freq,$srcip) > $::maxquery } { set ::blacklist($srcip) $curtime unset ::usertable(freq,$srcip) unset ::usertable(time,$srcip) drop return } } else { set ::usertable(freq,$srcip) 1 set ::usertable(time,$srcip) $curtime } } It has to do all of the work to remove old data from those arrays manually, so not only can that cause all those array entries to just accumulate in memory, but it demotes the VIP it gets attached to (because it uses global variables) . If we use the table command instead as shown in the following code, a lot of the complexity is handled by the system, so it's now cleaner, simpler, and CMP-compatible. A win all around! when RULE_INIT { set static::maxquery 100 set static::holdtime 600 } when CLIENT_DATA { set srcip [IP::remote_addr] if { [table lookup -subtable "blacklist" $srcip] != "" } { drop return } set curtime [clock second] set key "count:$srcip:$curtime" set count [table incr $key] table lifetime $key 2 if { $count > $static::maxquery } { table add -subtable "blacklist" $srcip "blocked" indef $static::holdtime table delete $key drop return } } Limiting POST Requests Per User Another example to limit POSTs per-user uses even more very tricky global array manipulation. when RULE_INIT { set ::windowSecs 10 } when HTTP_REQUEST { if { [HTTP::method] eq "POST" } { if { ![HTTP::header exists Authorization] } { HTTP::respond 401 return } set myUserID [getfield [b64decode [substr [HTTP::header "Authorization"] 6 end]] ":" 1] set myMaxRate [findclass $myUserID $::MaxPOSTRates3 " "] if { $myMaxRate ne "" }{ set currentTime [clock seconds] if { [info exists ::timestamp($myUserID)] } { set i [ expr $currentTime - $::timestamp($myUserID) ] if { $i > $::windowSecs } { set i $::windowSecs } while { $i > 0} { for {set j $::windowSecs} {$j > 0} {set j [expr $j - 1]} { set k [ expr $j - 1 ] set ::count($myUserID.$j) $::count($myUserID.$k) } set ::count($myUserID.0) 0 incr i -1 } set k 0 for {set i 0} { $i < $::windowSecs} {incr i} { incr k $::count($myUserID.$i) } if {$k < $myMaxRate } { incr ::count($myUserID.0) set ::timestamp($myUserID) $currentTime } else { HTTP::respond 302 Location http://asdf:44444/ } } else { for {set i 0} {$i < $::windowSecs} {incr i} { set ::count($myUserID.$i) 0 } set ::timestamp($myUserID) $currentTime set ::count($myUserID.0) 1 } } } } It can be very difficult to understand, to say nothing of trying to maintain or alter it. With the table command to do all of the heavy lifting for us, it becomes much simpler, lighter-weight, and CMP-compatible. when RULE_INIT { set static::windowSecs 10 } when HTTP_REQUEST { if { [HTTP::method] eq "POST" } { if { ! [HTTP::header exists Authorization] } { HTTP::respond 401 return } set myUserID [getfield [b64decode [substr [HTTP::header "Authorization"] 6 end]] ":" 1] set myMaxRate [findclass $myUserID $::MaxPOSTRates3 " "] if { $myMaxRate ne "" } { set reqnum [table incr "req:$myUserId"] set tbl "countpost:$myUserId" table set -subtable $tbl $reqnum "ignored" indef $static::windowSecs if { [table keys -subtable $tbl -count] > $myMaxRate } { HTTP::respond 302 Location http://asdf:44444/ return } } } } That's pretty impressive, if I do say so myself. And once again, because the table command will expire the entries automatically, there's no need to worry about old data taking up memory. We cannot wait to see all of the new (or old!) problems you solve with the help of the table command. Don't forget to show off your work in the iRules CodeShare!3KViews0likes4CommentsThe table Command: Counting
With version 10.1, we've given the session table some long-sought functionality, and revamped its iRules interface completely to give users a new, cleaner, full-featured way to keep track of global data. We are very proud to introduce the table command... Basics New Ways To Set Data New Ways To Alter Data Data Expiration Advanced Data Expiration Subtables Counting The Fine Print Examples In this seventh part of the series on the usage of the new table command, we discuss counting techniques. Counting It turns out that one popular use of iRules is to count things and then report or act on those counts. It also turns out that there are a lot of different ways of counting things, and they require slightly different techniques. The four major ways of counting that we've found so far are (in order of increasing complexity): Events that happened ever (or recently) Events that happened in the past 1 second (or other fixed window) Events that happened in the past N seconds (or other sliding window) Events that are happening right now We'll cover each of these in turn. Events that happened ever How many requests have been made on this VIP? How many POSTs has user X made? This is easy: just use table incr and table lookup. Most of the time you’ll only care about things that happened recently, so you can specify a different timeout for the entry, or just use the default. If you truly want to keep track of things ever, then you can also specify an indefinite timeout. This does mean that that memory will never go away on its own, though, so you should be careful with that option. :) Events that happened in the last fixed window How many DB queries were made in the last second? How many requests has IP X made in the last 10-second window? This is also pretty easy. Each window can have a name (for example, each 1 second window can be named by the result of [clock second]), and you just need to somehow combine that name with the name of the event you're counting. For example: table incr "dbquery:[clock second]" set tensecwin "[string range [clock second] 0 end-1]" table incr "ipreq:[IP::client_addr]:$tensecwin" Events that happened in the last sliding window How many hits on the login page in the last hour? How many bytes sent to IP X in the last minute? How many connections from user Y in the last 15 seconds? What users logged in in the last day? While a fixed window is useful, more often a sliding window is what is desired. This is because a fixed window doesn't move with the clock. For example, if you're using a fixed 60-second window, and it's now 12:30:14, you can't know how many things happened "in the last 60 seconds". You only can know how many things happened from 12:30:00-now, and maybe from 12:29:00-12:29:59, if you kept the result from the previous window. If you had a sliding window instead, it would always contain "the last 60 seconds", meaning that if it's 12:30:14 then the window is from 12:29:15-12:30:14, and when it's 12:30:17, then the window is from 12:29:18-12:30:17. For something like rate-limiting, this makes a huge difference in user experience. Unfortunately, the things that make it more desirable are the very things that make it more complicated. I'll explain this by considering each window as a bucket. With a fixed window, you only ever add things to the bucket ("user X made a request that gets counted in bucket #12:29"). With a sliding window, sometimes things need to come out of the bucket ("the bucket is now counting from 12:29:18 to 12:30:17, so the request that was made at 12:29:17 now needs to be removed"). This tells us that getting a sliding window means managing specific items (so we need to have a handle or name for each item) and expiring them as they move out of the window. Fortunately, a subtable is exactly up for the job. To implement a sliding window, you would do something like: set reqno [table incr "reqs:$username"] table set -subtable "reqrate:$username" $reqno "ignored" indefinite 60 log local0. "User $username made [table keys -count -subtable "reqrate:$username"] requests in the past 60 seconds" This keeps track of the rate of requests for each user. The first line gives each request a serial number (it just increments a variable), and the second line adds an entry to that user's subtable. The entry will expire on its own when it is supposed to leave the window, and counting the keys in the subtable will tell us the number of requests that user made in the window. The more observant among you will note that the table entry we're using for the serial number is going to expire too, so if this user is idle for a while, then the next request will have the serial number go back to 0. This is perfectly OK, though, since as long as the window size (in this example, it’s 60 seconds) is less than the timeout of the serial number entry (in this example, it’s the default of 180 seconds), then by the time the serial number entry expires, all of the entries in the subtable will have expired also. So it doesn't matter that the serial numbers starts at 0 again. If you want to use a longer window than 180 seconds, then you'll need to change the timeout to be longer after the incr line. Now, there's no rule that says you have to have a separate variable to keep track of the identity of each thing you're counting, or that they have to be consecutive. If you already have a serial number (for example, if you're counting new JSESSIONIDs), then you can just use that. Events that are happening right now How many client connections to this VIP? What users are currently logged in? This is slightly tricky, but doable. It builds on the concepts last example, with some additions. One simple example is: when HTTP_REQUEST { switch [HTTP::uri] { "/login.html" { table set -subtable userlist $username "ignored" $static::useridletimeout } "/logout.html" { table delete -subtable userlist $username } default { table lookup -subtable userlist $username } } log local0. "[table keys –count –subtable userlist] users currently logged in" } Once again, we're adding an entry to a subtable to start counting something, but in this case not only are we removing the entry when we want to stop counting it, but we also do a lookup on that entry when the user accesses any page. The reason we do that is to keep the entry alive as long as the user is busy, so if the user doesn't log out via our logout page, we still stop counting that user (because that user's entry in our subtable will expire) when our application does (after the number of seconds in $static::useridletimeout) . With a similar method, we can do something like limit a VIP to 1000 connections: when CLIENT_ACCEPTED { set tbl "connlimit" set key "[IP::client_addr]:[TCP::client_port]" if { [table keys -subtable $tbl -count] > 1000 } { event CLIENT_CLOSED disable reject } else { table set -subtable $tbl $key "ignored" 180 set timer [after 60000 -periodic { table lookup -subtable $tbl $key }] } } when CLIENT_CLOSED { after cancel $timer table delete -subtable $tbl $key } The basics are the same as the previous examples, but now we've added a timer to keep the table entry alive, rather than relying on any particular iRule event. This works because when the connection closes we kill the timer and delete the entry. Now, I know what you're asking: "Why all the extra complexity? Heck, if I had table decr, I could just do something like:" when CLIENT_ACCEPTED { if { [table lookup "conns"] > 1000 } { event CLIENT_CLOSED disable reject } else { table incr "conns" } } when CLIENT_CLOSED { table decr "conns" } The answer is: reliability. What happens if CLIENT_CLOSED never fires? Maybe your box runs low on memory and the connection sweeper hits. Maybe you're running on a VIPRION with no HA and a blade goes down. Your user count never gets to be decremented, and so now you start rejecting connections because you think your VIP is busier than it is. And that's why there is no table decr: to discourage exactly this sort of code. If you think you need it, you probably shouldn’t be using increment and decrement to solve your problem. In the real example above, if CLIENT_CLOSED never fires, there's no problem. No matter how the connection dies, the timer will be removed, so it will stop touching the entry, so the entry will expire on its own. In this way, your connection count may only be wrong temporarily; it will settle on the right answer all on its own. Continue reading part eight in the series: The Fine Print1.9KViews0likes0CommentsThe table Command: Advanced Data Expiration
With version 10.1, we've given the session table some long-sought functionality, and revamped its iRules interface completely to give users a new, cleaner, full-featured way to keep track of global data. We are very proud to introduce the table command... Basics New Ways To Set Data New Ways To Alter Data Data Expiration Advanced Data Expiration Subtables Counting The Fine Print Examples In this fifth part of the series on the usage of the new table command, we discuss advanced concepts around data lifetime and expiration from the session table. Advanced Data Expiration On the off chance that your iRule does need to keep close track of varying timeout or lifetime values, then you should know some important ways that they might or might not be changed. As you might expect, all of the other subcommands to set values take the timeout and lifetime parameters as well: table set [-excl|-mustexist] <key> <value> [<timeout> [<lifetime>]] table add <key> <value> [<timeout> [<lifetime>]] table replace <key> <value> [<timeout> [<lifetime>]] However, the add and replace subcommands are designed to sometimes not update the data. For example, if you call table add with a key that already exists, then the data will not get updated. In all cases, if the table command doesn't update the data, then it doesn't update the timeout and lifetime either. If it does update the data, then it does update the timeout and lifetime. So, if you have a table that looks like: and you run: table set 10.1.2.3 UserQuux indefinite 3600 then you end up with: The timeout and lifetime both got updated because the data was updated. If you then ran: table add 10.1.2.3 UserBar 120 120 then the table would not change at all. Because the data did not get updated, the timeout and lifetime also didn't. Sometimes you might want to update the value and not touch the existing timeout or lifetime values, even though you don't know what they are. By specifying a 0 for either the timeout or the lifetime, or by omitting the lifetime, that parameter is not changed even if the value is updated. So if you had the previous table and then ran: table replace 10.2.9.12 UserZap 180 0 you would get: You can see that the entry's value and the timeout were changed, but the lifetime was not. Continue reading part six in the series: Subtables1.1KViews0likes0CommentsThe table Command: Data Expiration
With version 10.1, we've given the session table some long-sought functionality, and revamped its iRules interface completely to give users a new, cleaner, full-featured way to keep track of global data. We are very proud to introduce the table command... Basics New Ways To Set Data New Ways To Alter Data Data Expiration Advanced Data Expiration Subtables Counting The Fine Print Examples In this fourth part of the series on the usage of the new table command, we discuss data lifetime and expiration from the session table. Data expiration Remember that I mentioned that an entry also has metadata? Prior to v10.1, the only metadata that an entry had was a timestamp, which keeps track of when that entry was last touched (changed or queried), and a timeout. The touch timestamp can’t be directly set by iRules, of course. It’s used internally by the session table to keep track of when to expire entries. So a small session table might have looked like this: You can see a number of different types of keys and values, each one with a timeout and a timestamp. One common request to enhance the session table has been to have some way of looking up an entry without affecting the touch timestamp, so we added the -notouch flag to the table command: table set [-notouch] $key $data table lookup [-notouch] $key With this flag, you can query the session table without changing when the entries will expire. But we felt that that didn't go far enough for some uses, so while the session table looks much the same in v10.1, it has two additions: Just as before, an entry has a touch timestamp and can have a timeout, but now it also has a create timestamp (to record when the entry was created), and it can also have a lifetime. By setting a lifetime on an entry, you can have it expire after a certain period of time no matter how many changes or lookups are performed on it. An entry can have a lifetime and a timeout at the same time. It will expire (be removed from the table) whenever the timeout OR the lifetime expires, whichever comes first. You can just specify the lifetime right after the timeout value: table set <key> <value> [<timeout> [<lifetime>]] If you want an entry to only have a lifetime, and not a timeout, you can specify an indefinite timeout. For example: table set $key $data indefinite 3600 will set an entry that will be removed after 3600 seconds. By default, an entry has a timeout of 180 seconds (just like previous versions), and an indefinite lifetime. For most uses of the session table, the user will specify timeout and lifetime values when an entry gets set, and never explicitly worry about them afterwards. If you do need to query or change those values on an entry, then you can do so directly: table timeout [-remaining] $key table timeout $key <value> table lifetime [-remaining] $key table lifetime $key <value> These commands operate exactly as they read: you can query or set the lifetime or timeout parameter which the entry has, or you can query the remaining time that an entry has left. Note that these commands never affect the expiration of a record. In other words, they always implicitly have the -notouch flag set. Continue reading part five in the series: Advanced Data Expiration1.7KViews0likes0Comments