Wednesday, October 5, 2011

Jmeter Random Transactions and Sharing Variables Between Thread Groups

I am using Jmeter to test applications on my web server.  I ran into some scenarios that Jmeter could not natively support.

Problem 1. I want to sent out certainly requests randomly. 
Problem 2. I need two thread groups and group #2 needs variable data from group 1.
Problem 3. I need elapsed time for all objects in a controller to complete


Problem 1
This was a rather simple solution that eventually came to me based on some other actions I was performing. 
- I added a loop controller
- I added a child If Controller
- I added a child Interleave Controller
- I added transactions in the Interleave Controller
- I created a global user defined variable for the high value of the random number generator range
- I added a BSF Preprocessor
  Language:  Javascript
 The script consisted of
 rand1 = vars.get("randomto");   //get the global variable for the random number range
 var randomx=Math.floor(Math.random()*randt);   //pick a number between 0 and the end of range
 vars.put("GR",randomx);   //share the random number with the thread
- In the If Controller, the condition is ("${GR}" == "1").  If the random number picked is 1, the first transaction from the interleave controller will be sent.  Next time it is matched, the second.  And so on.


Each loop cycle, a new random number is selected for each thread.  This is compared to the If Controller value (I use 1 as shown above). If they match, the If Controller is executed.  If not, it is skipped.

I use 1 as a matching criteria so I can control the frequency down to 50%.   Normally I use a 1% chance to send transactions.  I set the user defined variable "randomto" to 100.  The javascript will pick a number between 0 and 99 giving me a 1% chance. 


Problem 2
Problem 2 required a bit more thought.  Eventually, I came up with a perfectly working solution for my needs.

I needed three sets of variable data from thread group 1 available to thread group 2.  The variable data is thread dependent so thread 1-1 has three variables that thread 2-1 uses. 

My users log in and get a session ID.  They then use this session ID to authenticate to a webapp service on the same server.  They are assigned a second session ID with that service.  The login response also provides the client with their user number.  All requests include the user number.  Requests for for the first app, APP1, include SessionID1 and requests to APP2 include SessionID2.  Again, these are all unique per thread/user. 

My solution was to use Jmeter Property Variables in a unique way.



I use a CSV Data Set Config to read username and password from a CSV file.  I assign these to global variables username,password.  I limit the Sharing Mode to Current Thread Group.
(Note:  I have a lot more error handling logic than I indicate in this posting)

When the client logs in to APP1, I use a Regular Expression Extractor to assign the SessionID1 to a thread variable.

I then use a Beanshell Assertion to set a Jmeter property value.  I create a unique property value by joining the name "sessionidx" with the "username" variable which comes out like sessionidx.123456789
${__setProperty(sessionidx.${username}, ${SESSIONID1})};

In the second thread group, when I need to send SessionID1, use the variable
sessionid=${__property(sessionidx.${username})}

Problem 3
I needed to wait a period of time from when I received request response to sending a followup request.  basically I sent a request and receive a request ID.  I then later send a cancel to that request ID.  I wanted to wait 15 seconds before sending the followup.  In the meantime, I want the thread to continue sending other requests.

My solution was to use a BSF Pre Processor to set thread variables  before I run the initial transaction, then use a BSF Post Processor to calculate the elapsed time.  I use a If Controller to test if the elapsed time is greater than my configured time.  If so, execute the If Controller and cancel my open request.

Script to get current timestamp:
var n=new Date();
var tnow= n.getTime();
vars.put("tnow", tnow);

Script to get elapsed time:
var n=new Date();
var tnow = eval(args[0]);
if ( tnow > 0 )
{
var t2=tnow;
}
else
{
var t2 = n.getTime();
}
var s= n.getTime();
var diff = s - t2;
vars.put("elapsed", diff);

The elapsed time is pushed to a thread variable.

The condition in the IF controller tests for the existence of a request ID for that thread and if so, is the elapsed time greater than 15 seconds.  If so, execute the If Controller.

There we have it.  My low tech solutions to my Jmeter challenges!
(("${requestid}" != "")) && ((${elapsed}>15000))

5 comments:

Anonymous said...

I have ThreadGroup1 which performs login operation and save Username and password in two different variables like

${__setProperty(USERNAMEGlobal, ${USERNAME})}
${__setProperty(PASSWORDGlobal, ${PASSWORD})}
Now in ThreadGroup2 I use these credentials.

using

${__property()}
it works fine for a single user,

but if I tried multiple users(requests).Last value overrides the previous all values and ThreadGroup2 receives only the last credentials.

I want all the credentials to be passed one by one to ThreadGroup2 and then the requests present in ThreadGroup2 should work according to all those credentials respectively.

How this can be done?

PS: I defined ramp-up period=1, Number of Users=3, loop=1

Jomebrew said...

In my example, one you have a login token, you can connect from the same client using the token. Here I assign the token to a global property ${__setProperty(sessionidx.${username}, ${SESSIONID1})};

Thread group 2 assigns the property for that user to a local variable that is used in the HTTP authentication to my server app. sessionid=${__property(sessionidx.${username})}

My method is useful when you need to authenticate based on a server provided token. Your example can just use the same CSV file. You could use my method by creating a unique global parameter such as ${__setProperty(USERNAMEGlobal.${USERNAME}, ${USERNAME})};

I use about 1000 threads per client instance.

Anonymous said...

Apparently it is not working for me

In BeanShell PostProcessor I wrote this in ThreadGroup1
${__setProperty(USERNAMEGlobal.${USERNAME}, ${USERNAME})};

In TG2, in Preprocessor i wrote this
USERNAME=${__property(USERNAMEGlobal.${USERNAME})}

and defined a variable named USERNAME with value "None" in User defined variable but it is not working for me.

USERNAME does not get value it is just USERNAME

I also used debug sampler where the value of USERNAME=USERNAME.

Anonymous said...

Jomebrew could you please answer my last question? I am really stuck .. I have to resolve this issue or our whole test plan would be crashed.if u need, I can send you my test plan for analysis

Jomebrew said...

I set username and password from a CSV file. I set SESSIONID1 using a regex regular expression extractor.

I don't use global variables. Variables set from CVS and regular expression handlers are different and work for me.