Skip to content

Commit

Permalink
feat(performance): Refactoring and tracing (#1489)
Browse files Browse the repository at this point in the history
Added more users to testdata, from 70 to 2799.
Output 50 slowest requests
Improve tracing 

## Description

<!--- Describe your changes in detail -->

## Related Issue(s)

- #1326 

## Verification

- [ ] **Your** code builds clean without any errors or warnings
- [ ] Manual testing done (required)
- [ ] Relevant automated test added (if you find this hard, leave it and
we'll help out)

## Documentation

- [ ] Documentation is updated (either in `docs`-directory, Altinnpedia
or a separate linked PR in
[altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if
applicable)

---------

Co-authored-by: Ole Jørgen Skogstad <skogstad@softis.net>
  • Loading branch information
dagfinno and oskogstad authored Nov 20, 2024
1 parent 130d6bb commit 760c345
Show file tree
Hide file tree
Showing 10 changed files with 2,848 additions and 53 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/workflow-run-k6-performance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ jobs:
- name: Run K6 tests (${{ inputs.testSuitePath }})
run: |
./tests/k6/tests/scripts/generate_tokens.sh ./tests/k6/tests/performancetest_data ${{ inputs.tokens }}
k6 run ${{ inputs.testSuitePath }} --quiet --log-output=stdout --include-system-env-vars --vus=${{ inputs.vus }} --duration=${{ inputs.duration }} --out=cloud
k6 run ${{ inputs.testSuitePath }} --quiet --log-output=stdout --include-system-env-vars \
--vus=${{ inputs.vus }} --duration=${{ inputs.duration }} --out=cloud --out csv=./results.csv
grep http_req_duration ./results.csv | sort --field-separator=',' --key=3 -nr | head -10
env:
API_ENVIRONMENT: ${{ inputs.environment }}
API_VERSION: ${{ inputs.apiVersion }}
Expand Down
11 changes: 7 additions & 4 deletions tests/k6/tests/enduser/performance/enduser-search.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { enduserSearch } from '../../performancetest_common/simpleSearch.js'
import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js';
import { endUsersWithTokens } from '../../performancetest_common/readTestdata.js';
import { randomItem } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js';

const isSingleUserMode = (__ENV.isSingleUserMode ?? 'false') === 'true';
const traceCalls = (__ENV.traceCalls ?? 'false') === 'true';

export let options = {
summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'],
Expand All @@ -22,12 +24,13 @@ export default function() {
throw new Error('No end users loaded for testing');
}

const isSingleUserMode = (options.vus ?? 1) === 1 && (options.iterations ?? 1) === 1 && (options.duration ?? 0) === 0;
if (isSingleUserMode) {
enduserSearch(endUsersWithTokens[0]);
enduserSearch(endUsersWithTokens[0], traceCalls);
}
else {
enduserSearch(randomItem(endUsersWithTokens));
for (let i = 0; i < endUsersWithTokens.length; i++) {
enduserSearch(endUsersWithTokens[i], traceCalls);
}
}
}

11 changes: 7 additions & 4 deletions tests/k6/tests/graphql/performance/graphql-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
* Run: k6 run tests/k6/tests/graphql/performance/graphql-search.js --vus 1 --iterations 1 -e env=yt01
*/

import { randomItem } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js';
import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js';
import { endUsersWithTokens as endUsers } from '../../performancetest_common/readTestdata.js';
import { graphqlSearch } from "../../performancetest_common/simpleSearch.js";

const isSingleUserMode = (__ENV.isSingleUserMode ?? 'false') === 'true';
const traceCalls = (__ENV.traceCalls ?? 'false') === 'true';


/**
* The options object for configuring the performance test for GraphQL search.
Expand All @@ -27,12 +29,13 @@ export default function() {
if (!endUsers || endUsers.length === 0) {
throw new Error('No end users loaded for testing');
}
const isSingleUserMode = (options.vus ?? 1) === 1 && (options.iterations ?? 1) === 1 && (options.duration ?? 0) === 0;
if (isSingleUserMode) {
graphqlSearch(endUsers[0]);
graphqlSearch(endUsers[0], traceCalls);
}
else {
graphqlSearch(randomItem(endUsers));
for (let i = 0; i < endUsers.length; i++) {
graphqlSearch(endUsers[i], traceCalls);
}
}
}

Expand Down
21 changes: 14 additions & 7 deletions tests/k6/tests/performancetest_common/createDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@ import dialogToInsert from "../performancetest_data/01-create-dialog.js";
* @param {Object} serviceOwner - The service owner object.
* @param {Object} endUser - The end user object.
*/
export function createDialog(serviceOwner, endUser) {
export function createDialog(serviceOwner, endUser, traceCalls) {
var traceparent = uuidv4();

var paramsWithToken = {
headers: {
Authorization: "Bearer " + serviceOwner.token,
traceparent: traceparent
},
tags: { name: 'create dialog', traceparent: traceparent, enduser: endUser.ssn }
tags: { name: 'create dialog' }
};
if (traceCalls) {
paramsWithToken.tags.traceparent = traceparent;
paramsWithToken.tags.enduser = endUser.ssn;
}

describe('create dialog', () => {
let r = postSO('dialogs', dialogToInsert(endUser.ssn, endUser.resource), paramsWithToken);
Expand All @@ -36,19 +41,23 @@ export function createDialog(serviceOwner, endUser) {
* @param {Object} serviceOwner - The service owner object.
* @param {Object} endUser - The end user object.
*/
export function createAndRemoveDialog(serviceOwner, endUser) {
export function createAndRemoveDialog(serviceOwner, endUser, traceCalls) {
var traceparent = uuidv4();
var paramsWithToken = {
headers: {
Authorization: "Bearer " + serviceOwner.token,
traceparent: traceparent
},
tags: { name: 'create dialog', traceparent: traceparent, enduser: endUser.ssn }
tags: { name: 'create dialog' }
}
if (traceCalls) {
paramsWithToken.tags.traceparent = traceparent;
paramsWithToken.tags.enduser = endUser.ssn;
}

let dialogId = 0;
describe('create dialog', () => {
paramsWithToken.tags.name = 'create dialog';
paramsWithToken.tags.name = 'create dialog';
let r = postSO('dialogs', dialogToInsert(endUser.ssn, endUser.resource), paramsWithToken);
expect(r.status, 'response status').to.equal(201);
dialogId = r.json();
Expand All @@ -57,8 +66,6 @@ export function createAndRemoveDialog(serviceOwner, endUser) {
describe('remove dialog', () => {
traceparent = uuidv4();
paramsWithToken.tags.name = 'remove dialog';
paramsWithToken.tags.traceparent = traceparent;
paramsWithToken.headers.traceparent = traceparent
if (dialogId) {
let r = purgeSO('dialogs/' + dialogId, paramsWithToken);
expect(r.status, 'response status').to.equal(204);
Expand Down
53 changes: 40 additions & 13 deletions tests/k6/tests/performancetest_common/simpleSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { describe } from '../../common/describe.js';
import { getEU, postGQ, getSO } from '../../common/request.js';
import { getGraphqlParty } from '../performancetest_data/graphql-search.js';


/**
* Retrieves the content for a dialog.
* Get dialog, dialog activities, seenlogs, labellog, and transmissions.
Expand All @@ -19,7 +18,6 @@ import { getGraphqlParty } from '../performancetest_data/graphql-search.js';
function retrieveDialogContent(response, paramsWithToken, getFunction = getEU) {
const items = response.json().items;
if (!items?.length) return;

const dialogId = items[0].id;
if (!dialogId) return;

Expand All @@ -32,18 +30,29 @@ function retrieveDialogContent(response, paramsWithToken, getFunction = getEU) {
getContentChain(dialogId, paramsWithToken, 'get transmissions', 'get transmission', '/transmissions/', getFunction);
}

function log(items, traceCalls, enduser) {
if (items?.length && traceCalls) {
console.log("Found " + items.length + " dialogs" + " for enduser " + enduser.ssn);
}
}

/**
* Performs a enduser search.
* @param {Object} enduser - The end user.
* @returns {void}
*/
export function enduserSearch(enduser) {
export function enduserSearch(enduser, traceCalls) {
var traceparent = uuidv4();
let paramsWithToken = {
headers: {
Authorization: "Bearer " + enduser.token,
traceparent: uuidv4()
traceparent: traceparent
},
tags: { name: 'enduser search' }
tags: { name: 'enduser search' }
}
if (traceCalls) {
paramsWithToken.tags.traceparent = traceparent;
paramsWithToken.tags.enduser = enduser.ssn;
}
let defaultParty = "urn:altinn:person:identifier-no:" + enduser.ssn;
let defaultFilter = "?Party=" + defaultParty;
Expand All @@ -52,6 +61,7 @@ export function enduserSearch(enduser) {
expectStatusFor(r).to.equal(200);
expect(r, 'response').to.have.validJsonBody();
retrieveDialogContent(r, paramsWithToken);
log(r.json().items, traceCalls, enduser);
});
}

Expand All @@ -60,7 +70,8 @@ export function enduserSearch(enduser) {
* @param {string} dialogId - The dialog id.
* @param {Object} paramsWithToken - The parameters with token.
* @param {string} tag - Tagging the request.
* @param {string} path - The path to append to the URL. Can be empty or /labellog.
* @param {string} path - The path to append to the URL. Can be empty or /labellog.
* @param {function} getFunction - The get function to use.
* @returns {void}
*/
export function getContent(dialogId, paramsWithToken, tag, path = '', getFunction = getEU) {
Expand All @@ -77,7 +88,8 @@ export function getContent(dialogId, paramsWithToken, tag, path = '', getFunctio
* @param {Object} paramsWithToken - The parameters with token.
* @param {string} tag - Tagging the request.
* @param {string} subtag - Tagging the sub request.
* @param {string} endpoint - The endpoint to append to the URL.
* @param {string} endpoint - The endpoint to append to the URL.
* @param {function} getFunction - The get function to use.
* @returns {void}
*/
export function getContentChain(dialogId, paramsWithToken, tag, subtag, endpoint, getFunction = getEU) {
Expand All @@ -100,6 +112,7 @@ export function getContentChain(dialogId, paramsWithToken, tag, subtag, endpoint
* Performs a GET request to the specified URL with the provided parameters.
* @param {string} url - The URL to send the GET request to.
* @param {Object} paramsWithToken - The parameters with token.
* @param {function} getFunction - The get function to use.
* @returns {Object} The response object.
*/
export function getUrl(url, paramsWithToken, getFunction = getEU) {
Expand All @@ -115,36 +128,46 @@ export function getUrl(url, paramsWithToken, getFunction = getEU) {
* @param {Object} enduser - The enduser object containing the token.
* @returns {void}
*/
export function graphqlSearch(enduser) {
export function graphqlSearch(enduser, traceCalls) {
let traceparent = uuidv4();
let paramsWithToken = {
headers: {
Authorization: "Bearer " + enduser.token,
traceparent: traceparent,
'User-Agent': 'dialogporten-k6-graphql-search'
},
tags: { name: 'graphql search', traceparent: traceparent }
tags: { name: 'graphql search' }
};
if (traceCalls) {
paramsWithToken.tags.traceparent = traceparent;
paramsWithToken.tags.enduser = enduser.ssn;
}
describe('Perform graphql dialog list', () => {
let r = postGQ(getGraphqlParty(enduser.ssn), paramsWithToken);
expectStatusFor(r).to.equal(200);
expect(r, 'response').to.have.validJsonBody();
log(r.json().data.searchDialogs.items, traceCalls, enduser);
});
}

/**
* Performs a serviceowner search.
* @param {P} serviceowner
* @param {*} enduser
* @param {*} enduser
* @param {*} tag_name
*/
export function serviceownerSearch(serviceowner, enduser, tag_name) {
export function serviceownerSearch(serviceowner, enduser, tag_name, traceCalls, doSubqueries = true) {
let traceparent = uuidv4();
let paramsWithToken = {
headers: {
Authorization: "Bearer " + serviceowner.token,
traceparent: traceparent
},
tags: { name: tag_name, traceparent: traceparent, enduser: enduser.ssn }
tags: { name: tag_name }
}

if (traceCalls) {
paramsWithToken.tags.traceparent = traceparent;
}

let enduserid = encodeURIComponent(`urn:altinn:person:identifier-no:${enduser.ssn}`);
Expand All @@ -154,6 +177,10 @@ export function serviceownerSearch(serviceowner, enduser, tag_name) {
let r = getSO('dialogs' + defaultFilter, paramsWithToken);
expectStatusFor(r).to.equal(200);
expect(r, 'response').to.have.validJsonBody();
retrieveDialogContent(r, paramsWithToken, getSO);
if (doSubqueries) {
retrieveDialogContent(r, paramsWithToken, getSO);
}
log(r.json().items, traceCalls, enduser);
return r
});
}
Loading

0 comments on commit 760c345

Please sign in to comment.