Search code examples
load-testingmsloadtest

Load Tests, trying to generate random names but getting same names for many virtual users


I'm using Visual Studio Performance Tests. I want to generate a random name before each of my requests. I'm using this WebTestRequestPlugin for that:

using System;
using System.ComponentModel;
using System.Linq;
using Microsoft.VisualStudio.TestTools.WebTesting;

namespace TransCEND.Tests.Performance.Plugins
{
    public class RandomStringContextParameterWebRequestPlugin : WebTestRequestPlugin
    {
        [Description("Name of the Context Paramter that will sotre the random string.")]
        [DefaultValue("RandomString")]
        public string ContextParameter { get; set; }

        [Description("Length of the random string.")]
        [DefaultValue(10)]
        public int Length { get; set; }

        [Description("Prefix for the random string.")]
        [DefaultValue("")]
        public string Prefix { get; set; }

        private readonly string _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        private Random _random = new Random();

        public RandomStringContextParameterWebRequestPlugin()
        {
            ContextParameter = "RandomString";
            Prefix = "";
            Length = 10;
        }

        public override void PreRequestDataBinding(object sender, PreRequestDataBindingEventArgs e)
        {
            e.WebTest.Context[ContextParameter] = CreateNewRandomString();            

            base.PreRequestDataBinding(sender, e);
        }

        private string CreateNewRandomString()
        {
            var randomString = new string(Enumerable.Repeat(_chars, Length).Select(s => s[_random.Next(s.Length)]).ToArray()).ToLower();
            return $"{Prefix}{randomString}";
        }
    }
}

My problem is that when I start a load test with multiple virtual users, the preRequest code runs for the first few users immediately, rewriting the RandomName context parameter on every run. So when my requests are actually running, they are using the same random name, causing a conflict in my back-end code.

My question is how can I generate random names for each of my requests even when the user load is high?


Solution

  • I think the problem is that the standard random number routines are not thread safe. Thus each virtual user (VU) gets the same random seed value and hence the same random numbers. See here and here for fuller explanations.

    The code for CreateNewRandomString is not shown in the question but it probably uses the basic C# random number code which has the problem described above. The solution is to use a safer random number. This question provides some ideas on better random number generators.

    I have used code based on the following in several performance tests:

    public static class RandomNumber
    {
        private static Random rand = new Random(DateTime.Now.Millisecond);
        private static object randLock = new object();
    
        /// <summary>
        /// Generate a random number.
        /// </summary>
        /// <param name="maxPlus1">1 more than the maximum value wanted.</param>
        /// <returns>Value between 0 and maxPlus1-1 inclusive. Ie 0 .le. returned value .lt. maxPlus1</returns>
        public static int Next(int maxPlus1)
        {
            int result;
    
            lock (randLock)
            {
                result = rand.Next(maxPlus1);
            }
    
            return result;
        }
    }
    

    It should be simple to add a string creation method to the above code, something that generates the wanted string within a lock{ ... } statement.

    The part of the question that states "rewriting the RandomName context parameter on every run. So when my requests are actually running, they are using the same random name" is misunderstanding what is happening. Each VU gets its own set of CPs, it is just that the random numbers are the same.