Token expiration timer using exec.scenario.startTime

Hello folks. I have a dilemma. I have a use case where a required access token expired within a period of time that requires a token refresh at least twice during a performance test. the test script is named after two scenarios which are executed. with all stages the test run is 28min. I"m using the exec.scenario.startTime function but the problem is that during the first scenario the “expired” value properly meets the condition to enter the “if statement”, call for a new token. however during the second scenario I’m finding it challenging to reset the “expired” variable in a manner that will again meet the condition to call for a new token when the timed period meets the expired threshold. say for instance. every 5min I need to call for a new token. this works for the first scenario using exec.scenario.startTime, however during the second scenario the call for a new token never occurs. The attempt below is to multiply the expiration time by a multiplier each time the token expires during the test run. the first instance multiply =1. when the timer expires multiply is incremented by 1. then the token_expiration constant is doubled and tripled as needed and evaluated against “expired” as it increases from exec.scenario.startTime

Is there some trick to reset exec.scenario.startTime between scenario’s? does someone have an example?

scenario’s:

scenarios: {
        peak: {
            // peak scenario name
            executor: 'ramping-arrival-rate',
            startRate: 0,
            timeUnit: '1s',
            preAllocatedVUs: 50,
            maxVUs: 3000,
            stages: [
                { target: peak, duration: peak_ramp },
                { target: peak, duration: peak_sustain },
                { target: 0, duration: ramp_down },
            ],
            gracefulStop: after_peak_delay, // do not wait for iterations to finish in the end
            tags: { test_type: 'peak' }, // extra tags for the metrics generated by this scenario
            exec: 'peak_gate_rush', // the function this scenario will execute
        },
        gate_rush: {
            // gate_rush scenario name
            executor: 'ramping-arrival-rate',
            startRate: 0,
            startTime: start_delay,
            timeUnit: '1s',
            preAllocatedVUs: 50,
            maxVUs: 3000,
            stages: [
                { target: gate_rush, duration: gr_ramp },
                { target: gate_rush, duration: gr_sustain },
                { target: 0, duration: ramp_down },
            ],
            gracefulStop: after_peak_delay, // do not wait for iterations to finish in the end
            tags: { test_type: 'gate_rush' }, // extra tags for the metrics generated by this scenario
            exec: 'peak_gate_rush', // the function this scenario will execute
        },
    },

test code:

export function peak_gate_rush() {
    // The first VU iteration will always perform a login operation in order to get an API 
    // token we need to access the  API end point that we want to test

    expired = `${new Date() - new Date(exec.scenario.startTime)}`;
    if (api_token === null || expired >= token_expiration * multiply) {
        if (expired >= subs_token_expiration * multiply) {
            multiply += 1;
        }
        // console.log(`step1: scenario ran for ${new Date() - new Date(exec.scenario.startTime)}ms`);
        **var res = get_token(); // NEED THIS EXECUTED EVERY 5MIN.**
        var res_json = JSON.parse(res.body);
        token = res_json['access_token'];
    }
    // Below is the actual test case for the API endpoint
    group("v1_API", function () {
        var res = v1_API(api_token);

Hi @PlayStay,

exec.scenario.startTime gets (re)setted on each scenario start. You can test this with a simple script such as

import exec from "k6/execution";

export let options = {
  scenarios: {
    peak: {
      // peak scenario name
      executor: 'ramping-arrival-rate',
      startRate: 0,
      timeUnit: '1s',
      preAllocatedVUs: 5,
      stages: [
        { target: 1, duration: "5s" },
        { target: 1, duration: "20s" },
        { target: 0, duration: "5s" },
      ],
      gracefulStop: 0,
    },
    gate_rush: {
      // gate_rush scenario name
      executor: 'ramping-arrival-rate',
      startRate: 0,
      startTime: "30s",
      timeUnit: '1s',
      preAllocatedVUs: 5,
      stages: [
        { target: 1, duration: "5s" },
        { target: 1, duration: "20s" },
        { target: 0, duration: "5s" },
      ],
    },
  },
}

export default function() {
  console.log(exec.scenario.name, exec.scenario.startTime)
}

Where you will see how once the scenario name changes the exec.scenario.startTime changes as well.

Looking tat the calculation code I don’t see you resetting multiply which likely is the issue. You can probably use vu’s iterationInScenario for this :person_shrugging: but see alternative below.

I would argue though that it will probably be a lot easier if you record when the token was made and then get a new one only if it was more than 5 minutes … or 4minutes 30 seconds or something like that. There is no need for k6/execution in that and seems a lot more stable.

You can even wrap http.get and co. in a helper function and have them do that check eveyrytime so that if you are doing 10 requests you won’t be logged off in the middle.

Hope this helps you.

Thanks @mstoykov you’re right I goofed the multiply reset. your tips are what I needed to get the “timer” to work as I need. I agree I should track the lifecycle of the token but I don’t trust my skill to do so in a manner that doesn’t blow up under my test conditions. I just needed to add a couple of if statements, given the startTime function resets on each scenario. Not pretty but it works :).

export function peak_gate_rush() {
    // The first VU iteration will always perform a login operation in order to get an API 
    // token we need to access the /account/me API end point that we want to test
    if (exec.scenario.name != 'peak') {
        multiply = 1;
    }
    expired = `${new Date() - new Date(exec.scenario.startTime)}`;
    // console.log(`${exec.scenario.name}: scenario ran for ${expired}ms`);
    if (api_token === null || expired >= subs_token_expiration * multiply) {
        if (expired > token_expiration) {
            multiply += 1;
        }
        console.log(`${exec.scenario.name}: getting a token at  ${expired}ms: multiple by ${multiply}`);
        var res = get_token();
        var res_json = JSON.parse(res.body);
        api_token = res_json['token'];
    }

@mstoykov given that startTime resets between scenarios. Do you know if VU’s “accumulated” in a scenario are reset to the “preAllocatedVUs: 50,” value?? Meaning are the VU utilized in a scenario terminated at the end of the scenario?

I agree I should track the lifecycle of the token but I don’t trust my skill to …

I would argue what you are doing is harder. All you need is is to have a global variable expiresAt (for example) to which you write whenever you get a token and you write when it expires so Date.now() + 5 minutes. And then you just compare that you aren’t after this time.

You are kind of doing this … just in really roundabout way with way more math IMO.

Also

if (exec.scenario.name != 'peak') {
        multiply = 1;
    }

IMO means that in the second scenario your multiply will always be 1 (as you reset it whenever you call the function. This will work but it basically means you relogin every time. Unless I am getting something wrong.

@mstoykov given that startTime resets between scenarios. Do you know if VU’s “accumulated” in a scenario are reset to the “preAllocatedVUs: 50,” value?? Meaning are the VU utilized in a scenario terminated at the end of the scenario?

They aren’t “terminated” - k6 doesn’t terminate VUs until the very end of the test. But they won’t be used at the start. Instead you will start with the preallocation you have defined and if you need more it’s possible to (in your case you will) reuse the exact same that you initialized in the previous scenario.

Interesting that you’d say this approach is harder, but I do things the hard way sometimes :smiley: I brute forced a solution but via your suggestions I accept there is an easier, simpler, less Maths approach. I’ll come up with something :+1:t5:

Thanks for the info regarding lifespan of VUs in the face of sequential scenario execution. Given the price structure I’ll have to consider the financial impact of using ramping-arrival-rate to achieve my load profile goals before I launch any scripts in k6 Cloud. A few minutes per scenario of idle time will add up with all the use cases we deploy.

Cheers @mstoykov and regards,

PlayStay

1 Like