Using Atomic Variables to Better Handle Updating During Concurrency
The following forum post goes into detail on creating scripts to better handle changing values during performance tests with multiple hits in parallel.
We will do this with the use of Atomic variables and in the case of this forum post example, specifically AtomicLong.
AtomicLongs and the other Atomic variables are part of the Atomic classes to take advantage of compare-and-swap (CAS), an atomic instruction directly supported by most modern CPUs.
This makes Atomic variables a viable option for dealing with concurrency use cases.
Before I begin with the example, I would like to discuss briefly the need for a solution like atomic variables, when speaking in terms of concurrency especially when in need of unique values.
There are scenarios where you need to send a unique value in each response; normally a Responder Suite Variable or a data repo with a CRUD tool are perfect fits for these scenarios. But these solutions have a harder time when tested under load, since there are concurrent hits, which Virtualize will handle in parallel. This is great for performance, but a difficulty when you need to send incremented values.
For example, if I have a performance test scenario where I receive 10000 requests at a rate of 2 hps and the responses need to include a value from 1-10000, without any duplication. If I were to use the normal solution of a responder suite, the responses would look like so:
Request 1 would receive the value 1
Request 2 would receive the value 1
Request 3 would receive the value 2
Request 4 would receive the value 2
Request 5 would receive the value 3
Request 6 would receive the value 3
…….
As we can see Virtualize sends the same value to each requests that arrive at the same time, because the requests are being handle in parallel, therefore they pull the value from the responder suite variable at the same, which leads to behavior above.
If we use an atomic variable, what we expect to see is this:
Request 1 would receive the value 1
Request 2 would receive the value 2
Request 3 would receive the value 3
Request 4 would receive the value 4
Request 5 would receive the value 5
Request 6 would receive the value 6
……..
And this is possible because of the way Atomic variables handle concurrency, which is to utilize the CPU to keep the state of the variable.
To implement the AtomicLong, we will need to create a Java project and code a couple of methods that will create, modify, and reset the Atomic variables.
Steps:
1. Create a Java Project
a. Desktop installations of SOAtest and Virtualize have Java perspectives that we can use to code out the Atomic long script.
b. Open SOAtest/Virtualize and click the Java Perspective button at the top right . If you have never opened the Java perspective before, you will need to add the perspective.
i. Navigate to the top right of the SOAVirt Window, where you will see a small image of a Window with a plus symbol at the top right . This will open the “Open Perspective” wizard, scroll down until you see “Java”, highlight “Java”, and click OK.
c. In the Java perspective Click File>New>Java Project
d. Enter the Java Project creation configurations
i. Add a Project name (For this example, I will use the name Atomic_Increment_Forum, but feel free to name it something else)
ii. Use the default Java 8 for the JRE
iii. Click on Finish
2. Next we will add a Package to the src folder(For this example, I will be referring to the package as the “atomic” package)
a. Next we will add a class to the atomic package
i. Right click package -> New -> Class
ii. Name the class “TrackingAtomicVariable”
iii. Enable the option “public static void main” this will allow us to create a runnable jar that includes the Parasoft api library
Add the Parasoft API library to build path
a. The first step is finding the JAR: com.parasoft.api.jar
b. Open the SOAVirt installation folders in a file explorer and navigate to: ${Parasoft_Installation folder}\SOAtest & Virtualize(If you are using a SOAtest solo installation, the path here will not in the & Virtualize string)\9.10\examples\dev. Here you will find the JAR
c. Back in the Java perspective, right click the Java Project, navigate down to “Build Path”, and select “Add External Archive”d. This will open a file explorer, navigate to the folder that included the com.parasoft.api.jar file, select the JAR, and click open
e. You should now have a new section in your Java Project named “Referenced Libraries” which will contain the JAR file we just added
Coding the methods
a. Your class should look something like the following:b. The first we need to import the libraries into the class, import the following classes
import com.parasoft.api.*;
import java.util.concurrent.atomic.AtomicLong;c. Next, lets add the atomic variable. In this example, I will be using a long.
d. Add the following line in the class, but not within a method
static AtomicLong atomicLong = new AtomicLong();
If you wish to start the value with a specific long, add the long between the parenthesis, for example, if I wanted to start at 1000:
static AtomicLong atomicLong = new AtomicLong(1000);
By leaving it outside of a method, it is not replaced by multiple runs of the method
e. We will not be using the main method in the class, but it is required to create a runnable JAR file. So change the comment to showcase it as a placeholder
f. Next, lets add a method for incrementing the value of the long, along with returning the long back to SOAVirt. This made very easy with default methods provided by the atomicLong class:
Add the following method to the class:
public long incrementAtomic(ScriptingContext context){
long theValue = atomicLong.getAndIncrement();
return theValue;
}
g. For information on additional Atomic methods, please see the following: http://tutorials.jenkov.com/java-util-concurrent/atomiclong.html
h. Next, lets add a method for reset the long back to a specific value. To do this, add the following code:
public void resetValue(){
atomicLong.set(0);
}
i. Your script should now look like the following:
- Now we are ready to create a runnable JAR that can be added to the Virtualize system properties, so it can be utilized in an extension tool
a. First run the Java Project as a Java Application to generate a Launch Configuration, which is required for runnable JAR files. Right click -> Export : Java -> Runnable JAR
b. In the runnable JAR specifications, be sure to enable “Package required libraries into generated JAR” this will package the Parasoft API libraries into the JAR. And click on “Finish”
c. Select a location to export the JAR and select the “Launch Configuration”. Launch configuration should match the class name along with the project
Add the JAR into the Virtualize system properties
a. First step is to switch back to the Virtualize perspective.
b. Navigate to the menu, and select Parasoft -> Preferences
c. In the Preferences menu, scroll down on the left side menu to System Properties
d. Select “Add JARs”, and navigate to the exported JAR file, select, and click on Open
e. Click Apply and the you have the optional choice of restarting Virtualize. It isn’t usually necessary, but helps clean up the build pathCalling the methods from within an extension tool
a. The parameters we added to the increment method of our class is specific to scripts set to an element. If you would like to use a chained extension tool then you would need to use the following parameters: (Object input, ExtensionToolContext context) the primary difference the name for the context
b. Create a new PVA -> Add a Plain XML Message Responder -> Switch to form XML Input mode and DO NOT OVERWRITE
c. In the tree of the body of the payload, scroll down to placeholder and rename the element to IncrementingVariable
d. For the value of the element, click on the dropdown that currently reads “Fixed” and switch to Scripte. Click on “Edit Script”
f. Use the “Language” drop-down to switch to “Java”
g. In the class text box, we need to add atomic.TrackingAtomicVariable which is the name of the package then the name of the class separated by a period
h. If there are no issues, then the method drop-down at the bottom right will populate with the methods within the class declared in the text box
i. Use the “Method” drop-down to select the IncrementAtomic methodj. To test the script, simply grab the PVA’s endpoint from the Virtualize Server view and drop the end point in a browser. This will trigger the responder since there is a no correlation currently and if the browser is refreshed a few times you should see a new number each time
k. Additionally, if you redeploy the asset(Right click asset in the Virtualize server view and click reploy) you will notice that the number is consistent. The long will no reset unless you either recreate the JAR or use the reset methodl. If you are running a lot of performance tests you may want to run the reset method periodically to avoid overflow of the method
m. I recommend adding a separate responder that chains an extension tool to handle the reset.Implementing reset
a. Create a second responder in the PVA. Select literal Message Responder
b. Drag and drop the new responder above the first responder we added and rename the responder to “Reset Variable”
c. Navigate to the
d. Change the payload to “Variable Reset” and then switch to the “Responder Correlation” tab
e. Enable the URL PATH correlation and “reset” to the Path text box. This way if you call the PVA without an specific path, it will simply increment, but if you add /reset at the end of the base URL it will reset the variable to 0
f. Now we just need to chain the extension tool
i. Right click the reset responder -> Add Output -> “Payload” under either the Incoming Request or the Outgoing response, it will not make a difference for this example
ii. Select an extension tool from the list on the right side and click finish
g. In the new extension tool, switch the language to “Java” and enter atomic.TrackingAtomicVariable like before. This will populate the methods, and then simply select the resetValue method. Save and you are finished
Rerunning the increment after the reset
Now lets take a look at how our solution handles a performance test of 2hps
First, here are the statistics from my Load Test, the bottom left has my VUs throughout the scenario.:
Here are the first 4 responses of the performance test:
As we can see, Virtualize responds back with a different value each time.
This concludes my forum post on Atomic variables to better handle concurrency