Hundreds of call with different profiles

I need to load test a single api call. The call takes a single parameter. The parameter has about 600 unique possible values. Each parameter value has a different usage frequency. I would like to test how the system performs when all 600 values are being sent over the same time period but at different frequencies.

/api/val1 has 600 calls in an hour
/api/val2 has 1200 calls in an hour
/api/val600 has 50 calls in an hour

What is the best way to achieve this in k6?

Hi Sean! Welcome to the forum.

Normally, we would suggest using Scenarios, where each Scenario has its own options that define executors that are capable of calculating how many iterations need to be executed in order to reach the target rate.

However, because of:

and the fact that you have 600 variants, this would require the creation of a rather unwieldy options object (i.e. 600 scenarios). Are there perhaps different “tiers” of usage frequency that could reduce the 600 to something more manageable?

If not, one possible approach would be to configure a test with 600 VUs, where each VU handles calls to a specific parameter value at the predetermined rate. This would however still necessitate some form of “mapping” between parameter value and its target rate.

You could do this in JSON:

{
	"val1": 600,
	"val2": 1200,
	"val3": 50
}

The VUs could read from this file, selecting a val based on the __VU index, during the init phase:

let targetRate; // assumes target rate is expressed as requests per hour

// the init code is called a few additional times, so only perform the code if the VU index is greater than 0:
if (__VU > 0) {
  // load the mapping:
  const mapping = JSON.parse(open('./mapping.json'));

  targetRate = mapping['val' + __VU];

  // optional debug console output:
  console.log(`val${__VU} target rate: ${targetRate}`);
}

Note that __VU would not be useable on the k6 cloud where multiple load generators might be used (we’re working on that).

Since we don’t know the target rate beforehand, the looping logic would need to be handled by the default function which would only be executed once per VU; assuming there exist 600 entries in the JSON, you would use k6 run test.js --vus 600 --iterations 600 to have each VU end up with a single entry from the file and a single iteration to execute.

It then becomes a case of calculating how long to sleep between your http.get calls (or whatever it may be). You may also need to compensate for how long it takes the server to respond as execution is blocked until a response is received (if your servers start taking longer to respond, your request rate would end up lower too).

There may very well be a better way to handle this, and having “tiers” of frequency might make using the built-in options more feasible.

Thx. I will consider strategies to reduce the 600 variants.
I am going to auto generate the options file from a script where each variant is a scenario with the following definition:

"Variant_N": {
  "executor": "constant-arrival-rate",
  "env": {
    "operation": "Variant_N"
  },
  "rate": 50,
  "timeUnit": "1m",
  "duration": "1m",
  "preAllocatedVUs": 1,
  "maxVUs": 100

and then use the following k6 script:

export let options = JSON.parse(open("./options.json"));;
export default function () {
  let res = http.get(`api/${__ENV.operation}`, { timeout: threePointFiveSeconds });
  check(res, { 'status was 200': r => r.status == 200 });
}