Feather Background Waves Background
Skip to main content
Feather Background Waves Background
Feather Background Waves Background
  1. JMeter in English/

Random Unique Users

Intermediate - This article is part of a series.
Part 15: This Article

usuarios

Hello everyone, after a time without writing blog entries on the blog, we are back. This time I will share with you a solution to one of the most common issues when executing load or stress tests: avoiding data collisions by concurrent user sessions. Let me explain in detail the problem and our elegant solution, as generally what we seek is good rotation of test data to avoid internal cache.

Premises:>

Premises: #

  1. We have a load test with 100 concurrent users.
  2. We need 2 to 3 times the data for testing to avoid caching, between 200 and 300 different users.
  3. User names and passwords should preferably follow a pattern.

For this example, we will perform a load test with 100 concurrent users, which can be easily scaled up to 1,000 or 10,000. The required test data to avoid caching in the application server or database is 2 to 3 times, so we need at least 200 or 300 test data or usernames. Finally, for this scheme to work perfectly, we need that the user names have a pattern and it would be nice if their passwords were the same for all of them, something like user001, user002,…,user299,user300. Therefore, the pattern would be user + random number, obviously this random number should be a number between 1 and 300 with a mask to add zeros on the left to complete the format of the pattern.

Then we have an experimental random experiment to obtain 100 random numbers, with a sample space of 300 (S={1,2,…,299,300}) and an event or outcome of obtaining these values for users to sign. If the application allows multi-session, we could have fewer problems, because it is valid to use the same account multiple times to sign. Otherwise, if not allowed multi-session, this would become a problem. Because when generating 100 random numbers, the probability of obtaining unique values from the sample space decreases as we advance in the sequence of events. Therefore, it is necessary to implement a mechanism to avoid collisions or carrera problem for our test.

If we randomly select a number between 1 and 300, the probability of obtaining that value is P(1/300) or 0.33%, but for the second event there is an increasing probability of repeating this value as we advance, the probability of obtaining unique values decreases since pseudo-randomly generated numbers are uniformly distributed and therefore it is maintained that uniformity in some iteration close to us will have a randomly selected number repeated, for example, the first random number is equal to 344, but in the iteration 450, we again obtain this value, so they are not unique and irreproducible.

How can we avoid this problem?>

How can we avoid this problem? #

The solution is easier than you think and easily scalable if you are using multiple generators of load, first we will solve the initial problem. The solution is to generate a data structure in which we can store these random values and continue generating until we obtain the desired quantity. Once we have obtained the subsequent values, we will check if this value exists in the structure or not; if it does not exist, we introduce it, otherwise, we remove it and continue until we obtain the necessary quantity. Now, this must be done between each iteration of the group of threads so that we can guarantee that on the first iteration of the group of threads certain random users will have been used, and in subsequent iterations or subsequent ones, a completely fresh set of random values will be used.

The following code is documented for ease of reading; it should be placed in a JSR223 pre-processor on the first request of our script.

ArrayList<Integer> Users = new ArrayList<Integer>()         //List of users
int getIteration = vars.getIteration().toInteger()          //Current iteration of the thread group
Random randomGenerator = new Random() 
def iteration = props.get("iteration")                      //Property to store iterations
def getUsers = props.get("Users")                           //Property to store the list of users
                                                            //We use def if we can get null values
int random                                                  //Current random value

if ( iteration == null || iteration < getIteration || getUsers.size() < 1) {    //We validate if the iteration is null
                                                                                //That the list has at least 1 element
        while (Users.size() <= n) {                                             //Minimum size of the list
                                                                            // nth elements
        RandUser = randomGenerator.nextInt(n)                               //Generate the random number from 
                                                                            // 1 to n
        if (!Users.contains(RandUser)) {                                    //If the value is not in the array
            Users.add(RandUser)                                             //Introduce it
        }
}
iteration = getIteration                            //Do the current iteration
props.put("iteration",iteration)                    //Save the global iteration
props.put("Users",Users)                            //Save the global list
log.info("Users: "+props.get("Users").toString())   //Print the list to the log
}

Once we obtain the list of values, all that remains is to assign each thread its corresponding value. This can also be done through another JSR223 pre-processor in which we assign the value of the list to a local variable because remember that this list is stored in a property that is a global variable.

int threadNum = ctx.getThreadNum().toInteger()
def getUsers = props.get("Users")
vars.put("User",getUsers[threadNum].toString())

Ready in the variable Users, which can be accessed via ${User}, we can replace the value with a random number that is not repeated. This solution is much better than using CSV files, because the method of accessing information is never sequential, even if we are talking about database access. And it gives us simplicity and elegance to our scripts.

You can also leave a link for you to download the example script here.

script

Conclusion and Scalability>

Conclusion and Scalability #

For finalization, this mechanism is easily scalable to up to load generator with 1,000 users per load generator, but to avoid collisions between multiple load generators, it’s as simple as assigning each one a unique index value. Thus, the first load generator would be assigned the value of 0 and so on up to the value of n. To ensure that the load generator with index 0 handles the first 2,000 or 3,000 values by executing only 1,000 concurrent users, as shown below:

GeneratorConcurrent UsersRange
01,0000 - 3,000
11,0003k - 6k
21,0006k - 9k
31,0009k - 12k
n1,000nk

This is possible through a centralized or decentralized execution, both ways my recommendation is to use a property in the users.properties file of each generator of load with the purpose of manually assigning these values, and within the JSR223 in groovy, use this property value as a multiplier for ranges.



Intermediate - This article is part of a series.
Part 15: This Article