September 7, 2011

Handling memory within iterations in LoadRunner

Written by

Recently, I was asked to provide some assistance to a colleague for a particular data scenario that I had not come across previously. This particular scenario required that a user logged on to the Application can only perform particular searches based on criteria defined and assigned to that particular user. That is, VUSER1 can only search on terms1, terms2 and terms3 whilst VUSER2 can only search on terms4, terms5 and so on. The diagram below best describes the requirement.

My initial response to resolve this conundrum was to set up a database, import the data and query for the relevant data from within the LoadRunner script. This should work, Right?

Wrong. With timelines tight and the Projects (in)ability to install the database software on appropriate machines meant possible delay in testing that could be ill afforded. Getting the data was not a problem, however setting up a database instance required that 7 letter word starting with a P (Process). The Process required approval to install the software and also schedule times and resources to implement. These inhabitants plus the lack of experience by the tester to execute and setup database scripts within LoadRunner led me to think of a Plan B!

Therefore, Plan B was to handle this data requirement with some code within LoadRunner. How was this achieved? Firstly, two parameter files had to be created and handcrafted with the following format.

And below is the first cut of code to get a matched searchTerm for the loggedOnUser.

Action { // assumed vuser_init contains your logon details but for this example i'll assume the username is coming from a param file // LoggedOnUser therefore "loggedOnUser" is defined in vuser_init.... as a Unique & Once Parameter. // Basically loop through your param file to ensure that the loggedon user the right search terms. // Your param file will need to look something like this //  searchUser, searchTerm //   vuser1, john //   vuser1, odecee //   vuser1, perform //   vuser2, test //   vuser2, simple //   vuser3, wow //   vuser3, expert while  ( (strcmp(lr_eval_string("{loggedOnUser}"),lr_eval_string("{searchUser}")) != 0)) { lr_advance_param("searchTerm"); lr_advance_param("searchUser"); }
 lr_output_message("Test variables are Iteration: [%s], LoggedOnUser: [%s], SearchUser: [%s], SearchTerm : [%s]", lr_eval_string("{Iteration}"), lr_eval_string("{loggedOnUser}"), lr_eval_string("{searchUser}"), lr_eval_string("{searchTerm}")); return 0; }

Very simple piece of code to meet the requirements and no database in sight! Less than 30 minutes. Only overhead is crafting together parameter files to allow for the code to work.

However, (always one but!) a couple of issues were discovered. When the script was executed in a Scenario some of the Virtual users were failing with the error:

Error (-17991): Failed to add item to mfifo data structure

Some troubleshooting investigations found a couple of issues. First issue was that the data provided for the two files had a mismatch of data. The loggedOnUser file contained users that did not match to the searchUser therefore the script would iterate and loop through and eventually cause the virtual-user to abort due memory issues.

This leads quite nicely to the second issue with the above approach – the way lr_eval_string allocates memory internally within LoadRunner. For lr_eval_string, the memory is freed at the end of each iteration and in the code sample above, the virtual-user is evaluating a parameter as part of a loop. Generally loops of smaller size the lr_eval_string function would not be an issue. However, for this particular requirement the loop was over 200K+ records.   This memory overhead impacts the scalability of the load generators as well.

As part of the more thorough testing, I monitored the mmdrv process with the above code only in a script. The mmdrv process grew from ~8000Kb to just over 40,000Kb looping through only 100K records. If the mmdrv process is large already than this code can cause a memory issue (or two!).

The solution to conserve memory is basically to not to use the lr_eval_string function at all. Instead, use lr_eval_string_ext to assign the variable and free the memory on each iteration of the loop using the lr_eval_string_ext_free.

So here is the new code with (i) an additionally condition to check for data mismatches and (ii) more importantly using the lr_eval_string_ext and lr_eval_string_ext_free functions to handle the memory within the iteration and its multiple loops.

When tested, the mmdrv process for 300K loop within an iteration stays at ~8000Kb (previously grew to 40 000Kb). Here is the code.

 Action2() { unsigned long prmLen; //required to be set for use by lr_eval_string_ext char* buf; //required to be set for use by lr_eval_string_ext int loop = 0; int match = 0; int maxfilesize = 200000; // set this variable to stop the script from goin into a loop. Set to Size of your Param file +1 char loggedOnUser[16]; //might need to change this dependant on string-length of usernames // save logged on user Id here strcpy(loggedOnUser,""); strcpy(loggedOnUser, lr_eval_string("{loggedOnUser}")); while( (match == 0) && (loop < maxfilesize)) //can change the loop variable {  
 lr_eval_string_ext("{searchUser}",strlen("{searchUser}"), &buf, &prmLen, 0, 0, -1);  
 lr_output_message("ONE => buf=%s and loggedOnUser = %s", buf, loggedOnUser); if (strcmp(loggedOnUser,buf) != 0) { lr_advance_param("searchUser"); } else { match =1 ; //break out after match to searchuser and loggedOnUser is made! } // lr_output_message("Iteration %s => buf=%s, loggedOnUser = %s, searchTerm = %s , and loop = %i", lr_eval_string("{Iteration}"), buf, loggedOnUser, // lr_eval_string("{searchTerm}"), loop); lr_eval_string_ext_free(&buf); //dont forget to free! loop++; } //end of while loop if (loop >= maxfilesize) { lr_error_message("Iteration %s => buf=%s and loggedOnUser = %s and loop = %i", lr_eval_string("{Iteration}"), buf, loggedOnUser, loop); //exit user cos if this max is reached the script is looping through as NO match to users can be made in the param file. This is the first issue! } return 0; } 

Overall, once the memory issues were resolved and appropriately catered for, then this code was more than adequate to meet the requirements specified above. Testing continued and was a success, all without a need for a database!

Categorised in: Uncategorized

This post was written by John Natsioulas