router/bench-limited-cardinality.js (163 lines of code) (raw):
import http from 'k6/http';
import { check } from 'k6';
import { randomString } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
/*
Benchmarking script to run a graphql query with a random operation name from a fixed size pool.
Useful to test metric attributes.
*/
export const options = {
stages: [
{ duration: '15s', target: 20 },
{ duration: '15s', target: 50 },
{ duration: '20s', target: 100 },
{ duration: '30m', target: 100 },
],
};
// in the simple case from a clean state it's around (operationName)*5 series per metric
// mostly due to wg_subgraph_id and wg_subgraph_name array exploding
// 300 should be under the default cardinality limit (1500 < 2000)
// 500 should be slightly over the default cardinality limit (2500 > 2000)
const distinctNames = 300;
export function setup() {
let randomNames = [];
for (let i = 0; i < distinctNames; i++) {
randomNames.push(randomString(10, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'));
}
console.log('Generated ' + distinctNames + ' random names');
return { randomNames };
}
export default function ({ randomNames }) {
let query = `
query $$__REPLACE_ME__$$ {
employees {
# resolved through employees subgraph
id
# overridden by the products subgraph
notes
details {
# resolved through either employees or family subgraph
forename
surname
# resolved through employees subgraph
location {
key {
name
}
}
# resolved through family subgraph
hasChildren
# maritalStatus can return null
maritalStatus
nationality
# pets can return null
pets {
class
gender
name
... on Cat {
type
}
... on Dog {
breed
}
... on Alligator {
dangerous
}
}
}
# resolved through employees subgraph
role {
departments
title
... on Engineer {
engineerType
}
... on Operator {
operatorType
}
}
# resolved through hobbies subgraph
hobbies {
... on Exercise {
category
}
... on Flying {
planeModels
yearsOfExperience
}
... on Gaming {
genres
name
yearsOfExperience
}
... on Other {
name
}
... on Programming {
languages
}
... on Travelling {
countriesLived {
key {
name
}
}
}
}
# resolved through products subgraph
products
}
# can return null
employee(id: 1) {
# resolved through employees subgraph
id
details {
forename
location {
key {
name
}
}
}
}
teammates(team: OPERATIONS) {
# resolved through employees subgraph
id
...EmployeeNameFragment
# resolved through products subgraph
products
}
productTypes {
... on Documentation {
url(product: SDK)
urls(products: [COSMO, MARKETING])
}
... on Consultancy {
lead {
...EmployeeNameFragment
}
name
}
}
a: findEmployees(criteria: {
hasPets: true, nationality: UKRAINIAN, nested: { maritalStatus: ENGAGED }
}) {
...EmployeeNameFragment
}
b: findEmployees(criteria: {
hasPets: true, nationality: GERMAN, nested: { maritalStatus: MARRIED, hasChildren: true }
}) {
...EmployeeNameFragment
}
}
fragment EmployeeNameFragment on Employee {
details {
forename
}
}`;
let headers = {
'Content-Type': 'application/json',
'GraphQL-Client-Name': 'k6',
'GraphQL-Client-Version': '0.0.1',
};
let operationName = randomNames[Math.floor(Math.random() * randomNames.length)];
query = query.replace(/\$\$__REPLACE_ME__\$\$/g, operationName);
let res = http.post('http://localhost:3002/graphql', JSON.stringify({ query: query, operationName: operationName }), {
headers: headers,
});
check(res, {
'is status 200': (r) => r.status === 200 && r.body.includes('errors') === false,
});
}