Tutorials 07 June 2022

Rendezvous with k6

Leandro Melendez

Rendezvous is a French word commonly used in the load testing word. It sounds so fancy!

I believe Mercury first coined and implemented it (I may be wrong) in LoadRunner. NeoLoad has it with the same name, and JMeter calls it Synchronizing timer.

But what is it really, and how may we use it?

Rendezvous is a function that stops the virtual users when they reach that instruction in the script. The function makes them wait until more virtual users get to that step or a timer runs out. Then everyone who reaches that point will execute the next instruction simultaneously.

Sounds complex? Let's detail this a bit more.

Rendezvous in action

Thanks to the contribution of Mark Tomlinson, I have a perfect analogy to explain it. Let's go over it.

rendezvous ski

Imagine you are at a ski resort or park. In those places, the people would follow these steps:

  1. Buy passes & rent equipment
  2. Gear up
  3. Head to lift
  4. Wait for the lift - RENDEZVOUS!
  5. Get on the lift
  6. Be lifted
  7. Ski through different lanes
  8. Repeat step 3 until tired
  9. Take off & return the gear
  10. Go home

Step 4 is our point of interest, as it is an excellent rendezvous example.

rendezvous lift2

In a ski park, you have multiple people, each doing their set of steps at their own pace. Some arrived earlier, some just arrived. Once at the park, each person may do things at different speeds. Some may take longer to gear up; for others, the rental area may be slow at rush hours, and some may even pause to go to the bathroom. Once up there, some may ski fast, while others may be slow, depending on if they are beginners or experts.

That sounds a lot like a load test scenario.

Each virtual user executes the steps, iterates, ramps up or down, and has a somewhat different response and wait times on each action. A beautiful load scenario!

But unlike standard scripts where everyone executes at their pace, we have step 4 (wait for the lift), where the users wait as they arrive, and all of them do step 5 (jump on the lift) simultaneously when there are enough people to fill the lift, or the lift has to leave as the next one is coming.

rendezvous ski2

That is rendezvous in essence. It is a process that holds every user arriving at that step (the lift departing) until enough people are waiting, or it has to leave with whoever is waiting there when the next lift comes. In other words, you define time to wait and people per lift.

The rendezvous lift is different from real life, where you have to wait for the next lift once the first one is full. Here, the lift leaves right after it reaches its capacity. There are more ready to get people right away. But, if the time is up and the following lift arrives, it will depart even if there is only one person inside.

rendezvous lift

Example:

rendezvous(numberOfVUsers, timeToWaitForUsers);

How to, in k6

Out of the function's two parameters, a number parameter for users, or the timelapse, I think time is the easiest to implement in k6.

Following the ski lift example, the most straightforward approach for our rendez function is to work like the ski lift, which automatically passes and picks people.

First, we define how many milliseconds the time-lapse should be.

rendezPeriod = 30000; //In milliseconds

Now here is my proposed first and easy rendez function.

// comment: quick implementation of rendezvous function in k6
// stopping at periods, no user count here
function rendez(rendezPeriod) {
//comment: soFar is the time since the test started
const soFar = new Date().getTime() - exec.scenario.startTime;
//comment: calculate how much to wait until next rendez
const waitTime = (rendezPeriod - (soFar % rendezPeriod)) / 1000;
sleep(waitTime);
}

That function will take the start time of the scenario and restart the rendez timer every time it is completed.

With that function, our example script could go like this:

import http from 'k6/http';
import exec from 'k6/execution';
import { sleep } from 'k6';
export default function () {
const rendezPeriod = 30000;
/*Step1*/ http.get('https://test-api.k6.io/public/crocodiles/1/');
sleep(Math.random() * 5);
/*Step2*/ http.get('https://test-api.k6.io/public/crocodiles/2/');
sleep(Math.random() * 5);
/*Step3*/ http.get('https://test-api.k6.io/public/crocodiles/3/');
sleep(Math.random() * 5);
/*Step4*/ http.get('https://test-api.k6.io/public/crocodiles/4/');
rendez(rendezPeriod);
/*Step5*/ http.get('https://test-api.k6.io/public/crocodiles/');
sleep(Math.random() * 5);
/*Step6*/ http.get('https://test-api.k6.io/public/crocodiles/5/');
sleep(Math.random() * 5);
/*Step7*/ http.get('https://test-api.k6.io/public/crocodiles/6/');
sleep(Math.random() * 5);
/*Step8*/ http.get('https://test-api.k6.io/public/crocodiles/1/');
sleep(Math.random() * 5);
}

With the code above, the virtual users will execute their steps and wait for the rendez period to happen before executing step 5. Like in the lift example, the ski lift will leave every 30 seconds with whatever users have arrived at the rendez wait. Yes, this one is a lift with infinite capacity.

rendezvous lift3

Let's do a small scenario with arrivals and multiple users.

export const options = {
stages: [
{ duration: '40s', target: 20 },
{ duration: '2m', target: 20 },
{ duration: '5s', target: 0 },
],
};

We will have 20 users gradually arriving at the ski park (the scenario) for 40 seconds (one every 2 seconds). After that, they will continue skiing for 2 minutes.

The rendez timer is set to 30000 milliseconds. In other words, the first lift will leave 30 seconds after the test starts and repeat in the same interval.

In this example, the first ski lift will leave before everyone arrives at the park. Only about 15 users will have arrived when the first lift departs. So only 15 will get on the lift and execute step 5. Then, the last 5 users will gradually enter the scenario. They will wait for the next lift at step 4 and will be joined by whoever of the other 15 who complete the trip before the next 30 seconds.

Running that scenario, we will observe the following results:

rendezvous grafana graph

As you can see, every 30 seconds, there is a spike in the number of requests. This means that everyone waited for the lift and executed step 5 every time the 30-second lapse was completed.

The entire k6 script is available in this gist.

Implementing other cases

The functionality of waiting based on the number of virtual users is trickier in a distributed environment. Trust me, I have been giving it several tries and thoughts for a while now… So to keep it easier I am sharing this initial functionality while we figure out how to implement the rest. Some ideas have been over redis, proxies, and locks.

These functionalities are continuous work in progress that can be improved by the community as the need arises.

What do you think? Should the user number functionality be fully implemented? Would you like to contribute? You may have a straightforward idea of how to easily implement the functionality based upon VU arrival rate!

Please comment, suggest, or contribute to my gist example!

For now, I will leave it here, hoping that this helped to start implementing the rendezvous functionality on your k6 scripts.

Happy performance tests and besos! -- Leandro

< Back to all posts