Forum Discussion

Amit_371770's avatar
Amit_371770
Icon for Altostratus rankAltostratus
Jan 18, 2019

catch in irule

Hi There, I was debugging a irule problem but struck on one point. Can someone please explain the below code. Here member_num is numbers passes in query string and its value varies from 0-9.

 

"[0-9]*" { if {[catch {scan [lindex [lsort [members -list [LB::server pool]]] [expr {$member_num - 1}]] {%[^ ] %d} ip port} result]}{

 

        } elseif {$result == 2}{

           if {not [catch {pool [LB::server pool] member $ip $port}]}{

               Selecting pool member succeeded so exit this event of this iRule
              return
           }
        }
     }

1 Reply

  • Hi Amit,

    The [catch] command allows you to execute unstable TCL syntax, that MAY run into a TCL run time error. If a error has happend you will be able to handle this, instead of crashing the TCL runtime (aka. reset the individual network connection).

    The command is often used by lazy developers to make unstable code stable (aka. make the code just work stable without those errors), but it is also used by high experienced developers to write highly performance optimized code (aka. skip complex verification, see if works out and perform verification just in the case it would fail in the first try).

    The [catch] command can be implemented in different ways.

    Suppress errors only

     

    catch { 
        log local0.debug "This will fail: $variable_does_not_exist"
        ... subsequent code will not become executed if the previous command fails ....
    }
    

     

    Note: This syntax should be only used when the action is not that important, or the functionality does not depend on the sucessful execution of the catch { command }.

    Suppress errors with simple error information save into a variable

     

    catch { 
        log local0.debug "This will fail: $variable_does_not_exist"
        ... subsequent code will not become executed if the previous command fails ....
    } simple_error
    
    if { $simple_error ne "" } then {
         This code will be triggered if catch has caugth an error
        log local0.debug "The catch command has catched the error: Simple Error: $simple_error"
    }
    

     

    Note: The simple_error variable should never be used! Since it would degrade your performance in the case the catch { command } executes successfully. Accessing the global and extended [subst \$::errorInfo] variable right after the error happend is much more CPU friendly.

    Suppress errors with return code values

     

    if { [catch { 
        log local0.debug "This will fail: $variable_does_not_exist" 
        ... subsequent code will not become executed if the previous command fails ....
    }] } then {
         This code will be triggered if catch has caugth an error
        log local0.debug "The catch command has catched the error: Extended errorInfo: [subst \$::errorInfo]"
    }
    

     

    Note: This syntax should be used prefered, when the functionality does depend on the sucessful execution of the catch { command }. It causes very little overhead if the catch { command } runs successfully and allows you to trigger your error handle via an if ... then ... syntax.

    Your iRule (commented)

    Now that you have a brief understanding of the [catch] command, lets see what the individual lines of your existing iRule are doing...

     

    "[0-9]*" { 
        if { [catch { 
    
             The [member -list] command will fetch the member list of the currently selected pool > [members -list [LB::server pool]]
             The fetched member list will be sorted > [lsort [members -list [LB::server pool]]
             The variable $member_num will be used in a math operation > [expr { $member_num - 1}]. This could be interesting for $member_num=0 ;-)
             The result of the math operation will be used to pick an entry of the member list (list positions are starting with 0) > [lindex [lsort [members -list [LB::server pool]]] [expr { $member_num - 1}]]
             The picked entry of the member list will be parsed to extract the IP and Port Information > scan [lindex [lsort [members -list [LB::server pool]]] [expr { $member_num - 1}]] {%[^ ] %d} ip port 
    
            scan [lindex [lsort [members -list [LB::server pool]]] [expr { $member_num - 1}]] {%[^ ] %d} ip port 
    
             The catch command will now write the $result variable. It will hold the last "return" information. In your case the "return" information of the [scan] command (e.g. the number of extracted elements).
    
        } result] } then {
    
             If [catch] has for what ever reason caugth an error nothing will happen. So catch will silently supress the error and the remaining iRule will become executed...
    
        } elseif { $result == 2} {
    
             If [catch] didn't caugth an error and the $result of the [scan] command is 2 (aka. a IP and Port could be extracted) the pool member would be selected.       
    
            if {not [catch {pool [LB::server pool] member $ip $port}]}{
    
                 This catch part is slightly paranoid. Since the extracted member list information (e.g. IP / Port) should not throw an error at all.
    
                 Selecting pool member succeeded so exit this event of this iRule
                return
            }
        }
    }
    

     

    Clean Version of your iRule

    Below you will find a cleaner version of your iRule. It does the same, but slightly faster and much better to understand... 😉

     

    "[0-9]*" { 
        if { [catch { 
    
             Lets try this slightly unstable but very performance optimized code without any further verification...
    
            scan [lindex [lsort [members -list [LB::server pool]]] [expr { $member_num - 1}]] {%[^ ] %d} ip port                
            pool [LB::server pool] member $ip $port
    
        }] } then {
    
             Something went wrong with the code. Lets use the default pool then... 
    
        } else {
    
             Everything went well. Forward traffic to the selected pool member...
    
            return
    
        }
    }
    

     

    Cheers, Kai