ratio load balancing using rand function

Problem this snippet solves:

Summary: Use a pseudo random number to set a ratio for any iRule logic. This avoids using a global counter mechanism to track past selections.

iRule Methodology:

This iRule uses the rand function of expr to generate a pseudo random number and effectively generate a ratio for any iRule logic. The first example below sends 2% of connections to a separate pool. The remaining 98% of connections are sent to the virtual server's default pool. The second example selects a separate pool for 2% of requests to a specific set of URIs.

The original idea was from Jarvil in this forum post.

rand() man page section from the expr TCL wiki page:

Code :

http://www.tcl.tk/man/tcl8.4/TclCmd/expr.htm#M38

rand()
    Returns a pseudo-random floating-point value in the range (0,1). 
    The generator algorithm is a simple linear congruential generator 
    that is not cryptographically secure. Each result from rand completely 
    determines all future results from subsequent calls to rand, so rand 
    should not be used to generate a sequence of secrets, such as one-time 
    passwords. The seed of the generator is initialized from the internal 
    clock of the machine or may be set with the srand function. 

# Example 1 - Send 2% of connections to a different pool than the virtual server's default pool

when CLIENT_ACCEPTED { 
   # Send 2% of connections to a separate pool
   if { rand() < 0.02 } { 
      pool other_pool
   } 
} 

# Example 2 - Send 2% of requests to a specific URI to a different pool than the primary pool

when HTTP_REQUEST {

   # Check for a specific URI or set of URIs
   switch -glob [HTTP::uri] {
      "/uri1*" -
      "/uri2*" -
      "/uri3*" {

         # Send 2% of connections to a separate pool
         if { rand() < 0.02 } { 

            pool other_pool

            # Exit from this event in this iRule
            return
         } 
      }
   }
   # If we're still executing in this iRule, select the primary pool
   pool primary_pool
}

# Example 3 - Send 90% of requests to poolA, 5% to poolB and 5% to poolC. Use a session cookie to persist clients to the same pool over their HTTP session

when HTTP_REQUEST {

   # Check if there is a pool selector cookie in the request
   # Use a switch statement to ensure only valid pool names are present
   # instead of accepting any pool name from the user supplied cookie
   switch [HTTP::cookie pool_cookie] {
      poolA -
      poolB -
      poolC {
         # Select the pool from the cookie
         pool [HTTP::cookie pool_cookie]
         set selected ""
      }
      default {
         # No pool selector cookie, so randomaly choose a pool
         # Save a random number between 0 and 1
         set rand [expr { rand() }]

         if { $rand < .90 } { 
            pool poolA
            set selected poolA
         } elseif { $rand < .95 }{
            pool poolB
            set selected poolB
         } else {
            pool poolC
            set selected poolC
         }
      }
   }
}
when HTTP_RESPONSE {

   # Set a pool selector cookie if a pool was selected for this request
   if {$selected ne ""}{
      HTTP::cookie insert name pool_cookie value $selected path "/"
   }
}
Published Mar 18, 2015
Version 1.0
  • Hey guys!

    I'm trying to use the code described, I have a situation similar to the one explained in option 2.

     

    But I'm getting the error 'error: [undefined procedure: rand()][rand()]'

     

    BigIP version "BIG-IP 15.1.3 Build 0.38.11"