Complex Data Types

Arrays

An array in TrafficScript is a structured variable that stores a list of values. For example, you can define a list of names using the following code:

$array = [ “Alex”, “Matt”, “Oliver”, “Laurence” ];

The values in this array can then be looked up:

$someone = $array[0];

log.info($someone);

This instructs the Traffic Manager to print the string “Alex” to the event log. TrafficScript has functions that make it easy to work with array structures. For example, to print all of the names stored in $array using a “for” loop, first determine the number of elements that $array stores. TrafficScript provides the function array.length() for this purpose. For example:

$array = [ "Alex", "Matt", "Oliver", "Laurence" ];

 

$arraylen = array.length($array);

log.info("My array has " . $arraylen . " elements.\n");

 

for ( $i = 0; $i < $arraylen; $i++ ){

log.info ( "Element #" . $i . " " . $array[$i]);

}

When applied as a rule, this code causes the following output in the event log.

Alternatively, you can bypass the requirement to know the array length by using a “foreach” loop. The code below produces output similar to the previous example:

$array = [ "Alex", "Matt", "Oliver", "Laurence" ];

$i = 0;

 

log.info ("My array has " . array.length($array) . " elements.\n");

 

foreach ( $element in $array ) {

log.info( "Element #" . $i . " " . $element );

$i++;

}

For further information on array-specific functions, refer to the TrafficScript Reference in the Traffic Manager Online Help.

Hashes

A hash in TrafficScript is similar to an array, but instead of storing a list of values it stores a list of key/value pairs. Hashes are sometimes referred to as associative arrays. You can define a hash using the following code:

$hash = [ "orange" => "fruit",

"apple" => "fruit",

"cabbage" => "vegetable",

"pear" => "fruit" ];

To print all of the keys and values stored in the hash, first get a list of keys in the form of an array - using the function hash.keys() - that you can then use in a foreach loop to print all of the values. For example:

foreach ( $key in hash.keys($hash)){

log.info("Key: " . $key . "; Value: " . $hash[$key] . ";");

}

Combining the above two samples of code results in the following output in the event log:

The Global Associative Array

This is the first of three persistent associative arrays that TrafficScript can access.

Access the global array using the data.set() and data.get() functions. Data set in this array is persistent, and can be read from a later script. This array is of fixed size (the size is defined by the Traffic Manager global setting trafficscript!data_size). When it fills up, you cannot add further entries without first removing existing entries.

Maintaining the Array

As mentioned, use data.set() and data.get() to add and lookup array items. To delete individual elements, use data.remove().

To determine the amount of memory in use by the global array, use the function data.getMemoryUsage(). To delete all entries in the array, use data.reset(). You can delete a subset of the entries using data.reset( "prefix" ).

If you want to store several different types of data globally, use a consistent prefix to start the name of each key. Then, if you need to free memory, you can identify and delete all of the data that is stored for one particular purpose (for example, a global cache that grows continually, but can safely be deleted and reconstructed if necessary).

Example: An Indexed Array

You can use the global associative array to create an indexed array named “myarray” as follows:

# Declare a subroutine to calculate factorials

sub factorial( $n ) {

if( $n == 0 ) return 1;

return $n*factorial( $n-1 );

}

 

# Put entries into the array

$c = 0;

while( $c <= 10 ) {

$msg = "Did you know that ". $c ."! is ". factorial( $c ) ."?" ;

data.set( "myarray".$c, $msg );

$c++;

}

 

# Look up several entries. Note: the 1000th entry is empty

$msg = "";

$msg .= "Index 5: ".data.get( "myarray5" )."\n";

$msg .= "Index 10: ".data.get( "myarray10" )."\n";

$msg .= "Index 1000: ".data.get( "myarray1000" )."\n";

 

# delete the entire array (but no other data stored by data.set)

data.reset( "myarray" );

 

http.sendResponse( "200 OK", "text/plain", $msg, "" );

The Process-Local Associative Array

This is the second of three persistent associative arrays that TrafficScript can access.

When data has to be persistent to the whole system, but not globally unique, the process-local associative array provides the best balance between flexibility and performance. Entries in the global array (using the TrafficScript commands data.set() and data.get()), are unique across the whole system. This means that on a Symmetric Multiprocessing (SMP) system, when a process inserts a new array entry, it has to prevent all the other processes from accessing that same entry at the same time. The Traffic Manager achieves this with locks that hold the other processes back until the first process has finished. As such, if entry insertions and updates are happening very frequently, the system can spend a lot of time waiting to access the global array.

To alleviate this, the Traffic Manager provides the process-local array; accessed using data.local.*. Entries in the process-local array can be read from any TrafficScript context that takes place in the same process as the one that added them, across requests and connections. Essentially, the process-local array trades memory for performance: access and manipulation is instant, no locks are involved, but data is present once per process instead of just once across the system. In most cases this trade-off is acceptable, and in some cases, especially where the application logic requires the use of the data.reset() function, the performance benefits of using the process-local array are substantial.

The Connection-Local Array

This is the third of three persistent associative arrays that TrafficScript can access.

When processing a connection, it is sometimes useful to store information calculated in one rule for retrieval in a later rule. You can do this using a connection-local associative array, using the connection.data.set() and connection.data.get() functions.

Information stored in this way can only be retrieved by a TrafficScript rule that is processing the same connection, and all information is destroyed (and the memory freed) when the connection completes.

Libraries

TrafficScript rules that contain subroutines can be used as libraries. Take the following rule for example:

sub headbug(){

# Prints each header to the event log.

 

$headers = http.listHeaderNames();

 

foreach ($header in $headers){

log.info( $header . ": " . http.getheader($header));

}

}

The http.listHeaderNames() function returns an array of header names that were sent in the HTTP request.

To make this routine available as a library, save it as a separate rule and use the import statement to use it in your other rules. For example, by saving the above routine as a rule named “foo”, you can then import and use the headbug() subroutine using the following code:

import foo;

 

foo.headbug();

You can alternatively employ a locally declared alias for the imported library, such as:

import foo as mylib;

 

mylib.headbug();

This provides a means to reference an imported library with a more suitable, or perhaps shorter, local name. Functionally, there is no difference between this mechanism and the directly named example.

Applying this rule to a Virtual Server causes the names and values for each header of each request that the Virtual Server processes to be logged to the event log. You can modify the first rule (or library) so that it creates a hash of each of the headers using the following example:

sub headbug(){

# Prints each header to the event log.

 

$headers = http.listHeaderNames();

 

foreach ($header in $headers){

$headhash = [ $header => http.getheader($header) ];

log.info( $header . ": " . $headhash[ $header ] );

}

}

Although this library performs the same function, it does so slightly differently in that it creates a data structure that can be used later. To be able to use this structure later, however, you must extract it from the subroutine. To achieve this, alter the headbug() subroutine to return the $headhash data structure, then modify the calling code:

# foo (library rule)

 

sub headbug(){

# Prints each header to the event log.

 

$headers = http.listHeaderNames();

 

$headhash = [];

 

foreach ($header in $headers){

$headhash[ $header ] = http.getheader($header);

#log.info( $header . ": " . $headhash[ $header ] );

}

 

return($headhash);

}

 

# bar (calling rule)

 

import foo;

 

$headhash = foo.headbug();

 

foreach ($header in hash.keys($headhash)){

log.info( $header . ": " . $headhash[ $header ]);

}