Forum Discussion

Andy_McGrath's avatar
Andy_McGrath
Icon for Cumulonimbus rankCumulonimbus
Feb 14, 2018

Best practive for iRules-LX calls with a proc

I have entered the world of iRules LX (and it is greate 😄 ) however, I have a question.

If want to be able to make iLX calls within iRule procs so I have setup the iLX handler as a static variable:

 

set static::iLXHandler [ILX::init myiLX myiLX]

 

Then within a proc I can call any of my iLX methods without needing to pass in the iLX Handler:

 

proc setLogLevel {logLevel} {
    set result [ILX::call $static::iLXHandler setLogLevel [virtual] $logLevel]
    if {$result ne "true"} {
        log local0.error "ILX, Log Level not set, error=$catchError"
    }
    return $result
}

 

This works fine but I am unsure if this is the best approach or if a better practice to setup and use the iLX handler within each proc call, like this:

 

proc setLogLevel {logLevel} {
    set iLXHandler [ILX::init myiLX myiLX]
    set result [ILX::call $iLXHandler setLogLevel [virtual] $logLevel]
    if {$result ne "true"} {
        log local0.error "ILX, Log Level not set, error=$catchError"
    }
    return $result
}

 

In short what is the best approach when using iLX calls within an iRule proc?

 

5 Replies

  • I personally prefer to have ILX::init close to ILX::call (within the same proc) because it makes the code easier to read (especially when you have a loooong iRule), static variable is not exactly a great feature and sometime causes pain (IMHO), and I assume the init call is not expensive (not 100% sure though).

  • I have just found out that ILX::init cannot be called in a RULE_INIT block: The ILX iRule doc says "Valid During: ANY_EVENT with the exception of RULE_INIT". So, static is not necessary.

    If you want to reuse the RPC handle, create the handle in the first event block, and make a proc to accept the handle as an argument. e.g.,

     

        proc send_req {message handle} {
          set response [ILX::call $handle AsrFunc1 $message]
          return $response
        }
    
        when CLIENT_ACCEPTED {
          set RPC_HANDLE [ILX::init AsrPlugin "AsrExt"]
          log local0. "ACCPT: [call send_req "hello_req" $RPC_HANDLE]"
        }
    
        when HTTP_REQUEST {
          log local0. "REQ: [call send_req "hello_req" $RPC_HANDLE]"
        }
    
        when HTTP_RESPONSE {
          log local0. "RESP: [call send_req "hello_resp" $RPC_HANDLE]"
        }
    

     

    An issue is maintainability. When the code becomes longer, finding the RPC_HANDLE variable would become tedious. Also, when you add another event that occurs before the handle initialisation (e.g., FLOW_INIT starts before CLIENT_ACCEPTED), you get a TCL error. So, I would still (personally) go for the init next to call style (assuming ILX::init is cheap).

  • I did some performance tests. In short, there was no significant difference found between two iRules: one calls ILX::init every time for each ILX::call, and the other calls just once at the very beginning of execution (per HTTP request to the virutal: See the code in the previous post).

     

    Yet, the millage may vary. If you are planning to use iRules LX RPC, perform your own tests that reflect your actual environment.

     

  • Been a busy couple of weeks but managed to get some basic testing underway.

    Test kit:

    • F5 BIG-IP VE (2x CPU, 8GB)
    • BIG-IP 12.1.2 Build 1.0.271 Hotfix HF1

    Test Setup

    Built a set of procs which made very simple calls to iRulesLX which stores some data. Initial test calls a proc 'setup' in CLIENT_ACCEPTED which set the ilx rpc published to a static variable Then, still within CLIENT_ACCEPTED, call a proc sets a value of 5 (this is stored in a object in Node.js), this uses the static variable u

    Within HTTP_REQUEST call a proc which checks the value stored within Node.js object via a ILX call and if '5' logs locally the message

    All timings where captured using another iRule storing [clock clicks -milliseconds] before and after each event and logs them out.

    This was called 10 times from a bash script.

     

    when CLIENT_ACCEPTED priority 5 {
        call ilx_test::setupilx
        call ilx_test::setNum 5
    }
    
    when HTTP_REQUEST priority 5 {
        call ilx_test::logTest "ilx TEST 1"
        call ilx_test::logTest "ilx TEST 2" 
        call ilx_test::logTest "ilx TEST 3" 
        call ilx_test::logTest "ilx TEST 4" 
        call ilx_test::logTest "ilx TEST 5"
        call ilx_test::logTest "ilx TEST 6"
        call ilx_test::logTest "ilx TEST 7" 
        call ilx_test::logTest "ilx TEST 8" 
        call ilx_test::logTest "ilx TEST 9" 
        call ilx_test::logTest "ilx TEST 10"
    }
    

     

    The second test was to run the same iRule with one change, the setupilx proc was not needed to commented out and the procs updated so each proc set a ilx rpc published to a local variable for use just within that proc.

     

    when CLIENT_ACCEPTED priority 5 {
        call ilx_test::setupilx
        call ilx_test::setNum 5
    }
    

     

    From the first test the average delay from CLIENT_ACCEPTED was 1.1 ms and from HTTP_REQUEST was 10.4 ms From the second test the average delay from CLIENT_ACCEPTED was 1.5 ms and from HTTP_REQUEST was 11.5 ms

    My consulsion is that the CLIENT_ACCEPTED was almost no different, in the number of ILX commands calls it was the same so put the 0.4 ms difference down the expected environmental differences, where the HTTP_REQUEST calls between 1.1ms and 3ms (from the max difference, not shown above).

    However, the test was to make 10 proc calls per request so the difference is actually around an estimated 0.1-0.3ms per proc call.

    NOTE: These was all run on a shared low environment F5 VE device and the capture of the timings would also have created some overhead so my guess is the difference is even lower than you results shown.

    I would like to, and hopefully will, do more testing and get more data for a better idea of the best approach to utilising iRules LX in a large iRules environment. Once I do I will keep posting them to DevCentral in some form.

     

  • Such an interesting question. It seems the last post does not answer the question.

     

    I tried this code:

     

    proc global_init_proc {handler} {
        ILX::call $handler -timeout 12000 HelloWorld
    }
    
    proc per_call_init_proc {} {
        set rpc_handle [ ILX::init HelloWorld HelloWorld ]
        ILX::call $rpc_handle -timeout 12000 HelloWorld
    }
    
    
    when RULE_INIT {
        set static::ilx_init_perf_report_loop 1000
    }
    
    when HTTP_REQUEST {
        set global_rpc_handle [ ILX::init HelloWorld HelloWorld ]
        set start [clock clicks]
        for {set i 0} {$i<$static::ilx_init_perf_report_loop } {incr i} {
            call global_init_proc $global_rpc_handle
        }
        set stop [clock clicks]
        set global_ilx_init_clicks [expr {$stop - $start}]
    
        set start [clock clicks]
        for {set i 0} {$i<$static::ilx_init_perf_report_loop } {incr i} {
            call per_call_init_proc
        }
        set stop [clock clicks]
        set per_proc_ilx_init_clicks [expr {$stop - $start}]
    
    
        HTTP::respond 200 content "
                    
                        ILX Init Performance Report
                    
                        ILX calls executed : $static::ilx_init_perf_report_loop times
                        when ILX init is done once : $global_ilx_init_clicks clicks
                        when ILX init is done in each call : $per_proc_ilx_init_clicks clicks
                    
                    
        " noserver
    }

    with ILX code:

     

    var f5 = require('f5-nodejs');
    var ilx = new f5.ILXServer();
     
    ilx.addMethod('HelloWorld', function(req, res) {
        res.reply("Hellow World");
     });
     
    ilx.listen();

    This code execute 1000 times the proc running ILX for each scenario (ILX init in each proc or global)

     

    here is the result:

     

    $ curl http://192.168.2.11

     

                
                    ILX Init Performance Report
                
                    ILX calls executed : 1000 times
                    when ILX init is done once : 283381 clicks
                    when ILX init is done in each call : 289762 clicks
                
                

    $ curl http://192.168.2.11

     

                
                    ILX Init Performance Report
                
                    ILX calls executed : 1000 times
                    when ILX init is done once : 280417 clicks
                    when ILX init is done in each call : 281854 clicks
                
                

    $ curl http://192.168.2.11

     

                
                    ILX Init Performance Report
                
                    ILX calls executed : 1000 times
                    when ILX init is done once : 281807 clicks
                    when ILX init is done in each call : 285148 clicks
                
                

    So we can say that the ILX init in each proc call uses a little more clicks than a global ILX Init.

     

    but when running only 10 times the proc:

     

    $ curl http://192.168.2.11

     

                
                    ILX Init Performance Report
                
                    ILX calls executed : 10 times
                    when ILX init is done once : 3701 clicks
                    when ILX init is done in each call : 2841 clicks
                
                

    $ curl http://192.168.2.11

     

                
                    ILX Init Performance Report
                
                    ILX calls executed : 10 times
                    when ILX init is done once : 10398 clicks
                    when ILX init is done in each call : 10427 clicks
                
                

    $ curl http://192.168.2.11

     

                
                    ILX Init Performance Report
                
                    ILX calls executed : 10 times
                    when ILX init is done once : 3073 clicks
                    when ILX init is done in each call : 2822 clicks
                
                

    The the ILX init in each proc call is faster than global init.