My previous post covered finding a random point within a bounds, which worked great for testing random locations throughout the country for an app. Then we realized that we did not have a way to reliably load test our API endpoint with random locations.
After some googling and some scripting, I ended up with something that worked pretty well.
There are a couple of requirements we need to meet:
- Make it throttle-able
- Make it trackable
- Make it random
In the past I’ve used Apache load balancer, but unfortunately this won’t work for us because 1) we’re going to be doing a POST request and 2) the data is going to be random.
I’ll save the hour of trial/error to get it working with my requirements, and just post the final script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
max="$1" date # optional echo "url: https://mywebsite.local/setuserlocation rate: $max calls / second" START=$(date +%s); get () { curlscript=$(php loadtest.php) echo $curlscript 2>&1 | { bash -C; echo $curlscript; } 2>&1 | awk -v date="$(date +'%r')" '{print $0"\n-----", date}' >> perf-test.log } while true do echo $(($(date +%s) - START)) | awk '{print int($1/60)":"int($1%60)}' sleep 1 for i in `seq 1 $max` do get & done done |
This will execute a PHP script (similar to the script we created in the previous post which I will post below) which outputs a CURL command X number of times per second, and outputs the result into a log file. This works by spawning our script each time in a new thread. Run it with:
1 |
sh ./loadtest.sh 10 |
The output will look something like:
1 2 3 4 5 6 7 8 |
Dload Upload Total Spent Left Speed ----- 08:08:41 PM 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 0 0 0 0 0 0 0 0 --:--:-- 0:00:01 --:--:-- 0 100 60 100 14 100 46 8 26 0:00:01 0:00:01 --:--:-- 26 ----- 08:08:41 PM {"locationdata":"{}"}curl --header "Content-Type: application/json" --request POST --data '{"latitude":31.411381,"longitude":-111.159139}' https://mywebsite.local/setuserlocation |
Here is our slightly modified PHP file. This adds a new function to create a CURL string and then echo it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
// define bounding points // "cardinal" key not required, but useful for // keeping track of the points $points = [ [ "cardinal" => "NW", "Y" => 48.098490, "X" => -124.420681 ], [ "cardinal" => "SW", "Y" => 32.469436, "X" => -117.482643 ], [ "cardinal" => "SE", "Y" => 25.121507, "X" => -81.038997 ], [ "cardinal" => "NE", "Y" => 47.162174, "X" => -68.432636 ] ]; $randomPosition = getRandomCoordinateWithinBounds($points); echo getCurlCommandFromPosition($randomPosition); /** * Format a string into a curl command with our given X/Y coordinate * @param array $position a given point within our testing area * @return string a string formatted curl command */ function getCurlCommandFromPosition($position) { return 'curl --header "Content-Type: application/json" --request POST --data \'' . json_encode(["latitude" => $position['Y'], "longitude" => $position['X']]) . '\' https://mywebsite.local/setuserlocation'; } /** * Find the bounds of all given points and return a point within * those bounds * @param array $points an array of X/Y points * @return array an array containing a random point * within the given bounds */ function getRandomCoordinateWithinBounds($points) { $minx = 0; $maxx = 0; $miny = 0; $maxy = 0; // find the minimum and maximum X and Y points // across all given points for ($i = 0; $i < count($points); $i++) { if ($points[$i]["X"] > $maxx) { $maxx = $points[$i]["X"]; } if ($points[$i]["X"] < $minx) { $minx = $points[$i]["X"]; } if ($points[$i]["Y"] > $maxy) { $maxy = $points[$i]["Y"]; } if ($points[$i]["Y"] < $minx) { $miny = $points[$i]["Y"]; } } // create a random point within our minX/maxX and minY/maxY bounds $randomPoint = [ "Y" => round($miny + ($maxy - $miny) * (mt_rand() / mt_getrandmax()), 6), "X" => round($minx + ($maxx - $minx) * (mt_rand() / mt_getrandmax()), 6) ]; // check if the random point is within our bounds // if not, create a new random point and try again while(!pointIsInCoordinates($points, $randomPoint)) { $randomPoint = [ "Y" => round($miny + ($maxy - $miny) * (mt_rand() / mt_getrandmax()), 6), "X" => round($minx + ($maxx - $minx) * (mt_rand() / mt_getrandmax()), 6) ]; } return $randomPoint; } function pointIsInCoordinates($points, $point) { // get the point at the end of the array $j = count($points) - 1; $output = false; for ($i = 0; $i < count($points); $i++) { if ($points[$i]["X"] < $point["X"] && $points[$j]["X"] >= $point["X"] || $points[$j]["X"] < $point["X"] && $points[$i]["X"] >= $point["X"]) { if ($points[$i]["Y"] + ($point["X"] - $points[$i]["X"]) / ($points[$j]["X"] - $points[$i]["X"]) * ($points[$j]["Y"] - $points[$i]["Y"]) < $point["Y"]) { return true; } } $j = $i; } return $output; } |