Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: do not always retry load tests requests #3300

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 94 additions & 102 deletions load-tests/class-scenario.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import http from "k6/http";
import exec from "k6/execution";
import { Trend } from "k6/metrics";
import { Counter, Trend } from "k6/metrics";

import { renkuLogin } from "./oauth.js";
import { check, fail, sleep } from "k6";
import { check, fail, group, sleep } from "k6";
import { uuidv4 } from "https://jslib.k6.io/k6-utils/1.2.0/index.js";

import {
baseUrl,
Expand All @@ -16,34 +17,49 @@ export const options = {
scenarios: {
lecture: {
executor: "per-vu-iterations",
vus: 10, // tested up to 30
vus: 4, // tested up to 30
iterations: 1,
maxDuration: '30m',
gracefulStop: '2m',
},
},
};

// k6 custom metrics
const sessionStartupTrend = new Trend("session_startup");
const sessionStartupDuration = new Trend("session_startup_duration", true);
const sessionCreateReqDuration = new Trend("session_create_req_duration", true);
const sessionGetReqDuration = new Trend("session_get_req_duration", true);
const sessionDeleteReqDuration = new Trend("session_delete_req_duration", true);
const requestRetries = new Counter("http_request_retries", false);

function httpRetry(httpRequest, n, logMessage) {
function DoHttpRequest(httpRequest, nRetries = 0) {
let res,
i = 0;
do {
sleep(i);
res = httpRequest;
console.log(
`${exec.vu.idInInstance}-vu: ${logMessage}, status: ${res.status}, retries: ${i}`
);
i++;
} while (!(res.status >= 200 && res.status < 300) && i < n);
while (i <= nRetries) {
res = httpRequest();
if (res.status >= 400 || res.status < 200) {
i++;
requestRetries.add(1)
sleep(i);
continue;
}
break;
};

generalResponseCheck(res);
return res;
}

if (res.status >= 400) {
throw new Error(
`${exec.vu.idInInstance}-vu: FAILED ${logMessage}, status: ${res.status}, retry: ${i}`
function generalResponseCheck(res) {
if (
!check(res, {
"request succeeded with 2XX": (res) => res.status >= 200 && res.status < 300,
})
) {
fail(
`request at ${res.url} failed with ${res.status} and body ${res.body}`
);
}

return res;
}

function showProjectInfo(baseUrl, gitUrl) {
Expand All @@ -52,73 +68,63 @@ function showProjectInfo(baseUrl, gitUrl) {
is_delayed: false,
migrate_project: false,
};
const res = http.post(
`${baseUrl}/ui-server/api/renku/project.show`,
JSON.stringify(payload),
{ headers: { "Content-Type": "application/json" } }
);
console.log(res.status);
const res = DoHttpRequest(
() => http.post(
`${baseUrl}/ui-server/api/renku/project.show`,
JSON.stringify(payload),
{ headers: { "Content-Type": "application/json" } }
)
)
if (
!check(res, {
"getting project info succeeded with 2XX": (res) =>
res.status >= 200 && res.status < 300,
"getting project info response has no error": (res) =>
res.json().error === undefined,
})
) {
fail(
`getting project info failed with status ${res.status} and body ${res.body}`
`getting project info failed with error ${res.json().error}`
);
}

return JSON.parse(res.body);
return res.json();
}

function forkProject(baseUrl, projectInfo) {
function forkProject(baseUrl, projectInfo, idPostfix) {
const name = projectInfo.result.name;
const projectPathComponents = projectInfo.result.id.split("/");
const path = projectPathComponents.pop();
const namespace_path = projectPathComponents.pop();
const id = namespace_path + "%2F" + path;

const vuIdPostfix = "-" + String(exec.vu.idInInstance);

console.log(`${exec.vu.idInInstance}-vu: project id: ${id}`);

const payload = {
id: id,
name: name + vuIdPostfix,
name: name + idPostfix,
namespace_path: namespace_path,
path: path + vuIdPostfix,
path: path + idPostfix,
};

const res = httpRetry(
http.post(
const res = DoHttpRequest(
() => http.post(
`${baseUrl}/ui-server/api/projects/${id}/fork`,
JSON.stringify(payload),
{ headers: { "Content-Type": "application/json" } }
),
10,
"fork project"
);

return JSON.parse(res.body);
return res.json();
}

function getCommitShas(baseUrl, projectInfo) {
const id = projectInfo.id;
console.log(`${exec.vu.idInInstance}-vu: project id to fork ${id}`);

const res = httpRetry(
http.get(
const res = DoHttpRequest(
() => http.get(
`${baseUrl}/ui-server/api/projects/${id}/repository/commits?ref_name=master&per_page=100&page=1`
),
10,
"get commit sha"
);

//console.log(`${exec.vu.idInInstance}-vu: commit sha request status: ${res.status}`)

return JSON.parse(res.body);
}

Expand All @@ -131,114 +137,100 @@ function startServer(baseUrl, forkedProject, commitShas) {
serverOptions: serverOptions,
};

const res = httpRetry(
http.post(
const res = DoHttpRequest(
() => http.post(
`${baseUrl}/ui-server/api/notebooks/servers`,
JSON.stringify(payload),
{ headers: { "Content-Type": "application/json" } }
),
10,
"start server/session"
);

console.log(
`${exec.vu.idInInstance}-vu: start server, status: ${res.status}`
1,
);

return JSON.parse(res.body);
sessionCreateReqDuration.add(res.timings.duration)
return res.json();
}

function pollServerStatus(baseUrl, server) {
const serverName = server.name;
console.log(`${exec.vu.idInInstance}-vu: server name: ${serverName}`);

const ServerStates = {
Starting: "starting",
Running: "running",
};

let resBody,
counter = 0;
let resJson, res, counter = 0;
do {
sleep(1);
resBody = JSON.parse(
http.get(`${baseUrl}/ui-server/api/notebooks/servers/${serverName}`).body
);
res = DoHttpRequest(
() => http.get(`${baseUrl}/ui-server/api/notebooks/servers/${serverName}`)
)
sessionGetReqDuration.add(res.timings.duration)
resJson = res.json()
counter++;
} while (
resBody.status === undefined ||
resBody.status.state == ServerStates.Starting
resJson.status === undefined ||
resJson.status.state == ServerStates.Starting
);

sessionStartupTrend.add(counter);
sessionStartupDuration.add(counter * 1000);

return resBody;
return resJson;
}

function stopServer(baseUrl, server) {
const serverName = server.name;
const res = http.del(
`${baseUrl}/ui-server/api/notebooks/servers/${serverName}`
);
const res = DoHttpRequest(
() => http.del(
`${baseUrl}/ui-server/api/notebooks/servers/${serverName}`
),
)
sessionDeleteReqDuration.add(res.timings.duration)

return res.status;
}

function deleteProject(baseUrl, projectInfo) {
const id = projectInfo.id;

const res = httpRetry(
http.del(`${baseUrl}/ui-server/api/projects/${id}`),
const res = DoHttpRequest(
() => http.del(`${baseUrl}/ui-server/api/projects/${id}`),
10,
"delete project"
);

console.log("shuttdown");

return res.status;
}

// Test

// Test setup
export function setup() {
renkuLogin(baseUrl, credentials);

const projectInfo = showProjectInfo(baseUrl, sampleGitProjectUrl);

return projectInfo;
}

// Test code
export default function test(projectInfo) {
const vu = exec.vu.idInInstance;

sleep(vu); // lets VUs start in sequence
let forkedProject, commitShas, server;
const uuid = uuidv4();

console.log(`${vu}-vu: login to renku`);
renkuLogin(baseUrl, credentials);

console.log(`${vu}-vu: fork 'test' project -> 'test-${vu}'`);
const forkedProject = forkProject(baseUrl, projectInfo);

sleep(90); // workaround

console.log(`${vu}-vu: get latest commit hash from forked project`);
const commitShas = getCommitShas(baseUrl, forkedProject);

console.log(`${vu}-vu: start server/session with latest commit`);
const server = startServer(baseUrl, forkedProject, commitShas);

console.log(`${vu}-vu: wait for server to enter state 'running'`);
pollServerStatus(baseUrl, server);
console.log(`${vu}-vu: server 'running'`);
group("fork", function () {
forkedProject = forkProject(baseUrl, projectInfo, uuid);
sleep(90); // waiting for fork to complete
commitShas = getCommitShas(baseUrl, forkedProject);
})

console.log(`${vu}-vu: let server run for 200 seconds`);
sleep(200);
group("launch session", function () {
server = startServer(baseUrl, forkedProject, commitShas);
pollServerStatus(baseUrl, server);
})

console.log(`${vu}-vu: shutdown server`);
stopServer(baseUrl, server);
sleep(10); // simulate users being idle

console.log(`${vu}-vu: delete 'project-${vu}'`);
deleteProject(baseUrl, forkedProject);
group("shutdown server", function () {
stopServer(baseUrl, server);
})

console.log(`${vu}-vu: test finished`);
group("remove project", function () {
deleteProject(baseUrl, forkedProject);
})
}
Loading