Skip to content

Instantly share code, notes, and snippets.

@salvacorts
Last active October 25, 2022 21:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save salvacorts/7f6fe8e53dcbdfc38606f3892918cfcc to your computer and use it in GitHub Desktop.
Save salvacorts/7f6fe8e53dcbdfc38606f3892918cfcc to your computer and use it in GitHub Desktop.
Loki Querier Autoscaling K6 test
import { check, fail } from 'k6';
import loki from 'k6/x/loki';
/*
* Host name with port
* @constant {string}
*/
const HOST = __ENV.LOKI_HOST || fail("provide LOKI_HOST when starting k6");
/**
* Name of the Loki tenant
* @constant {string}
*/
const TENANT_ID = __ENV.LOKI_TENANT_ID || fail("provide LOKI_TENANT_ID when starting k6");
/**
* Access token of the Loki tenant with logs:write and logs:read permissions
* @constant {string}
*/
const ACCESS_TOKEN = __ENV.LOKI_ACCESS_TOKEN || fail("provide LOKI_ACCESS_TOKEN when starting k6");
/**
* URL used for push and query requests
* Path is automatically appended by the client
* @constant {string}
*/
const BASE_URL = `https://${TENANT_ID}:${ACCESS_TOKEN}@${HOST}`;
/**
* Amount of virtual users (VUs)
* @constant {number}
*/
const low_VUS = 50;
const high_VUS = 100;
/**
* Definition of test scenario
*/
export const options = {
scenarios: {
spike: {
exec: 'read',
startTime: '0s',
executor: 'ramping-vus',
startVUs: 5,
stages: [
// Start with gradual increase
{ duration: '2m', target: low_VUS },
// Stay constant
{ duration: '1m', target: low_VUS },
// First spike
{ duration: '30s', target: high_VUS },
{ duration: '30s', target: low_VUS },
// Second spike
{ duration: '30s', target: high_VUS },
{ duration: '30s', target: low_VUS },
// Finish by gradual decrease
{ duration: '2m', target: 0 }, // Go down to 0
],
gracefulRampDown: '0s'
},
},
};
const hourSecs = 3600;
function getOffsetStart() {
return Math.ceil((Date.now() / 1000) - (hourSecs*6))
}
const labelCardinality = {
"namespace": 1,
};
const conf = new loki.Config(BASE_URL, 60000, 0.9, labelCardinality);
const client = new loki.Client(conf);
const queryTypeRatioConfig = [
{
ratio: 0.1,
item: readLabels
},
{
ratio: 0.1,
item: readLabelValues
},
{
ratio: 0.1,
item: readSeries
},
{
ratio: 0.5,
item: readRange
},
{
ratio: 0.2,
item: readInstant
},
];
const rangesRatioConfig = [
// NOTE: we set query_ingesters_within to 3h
{
ratio: 0.2,
item: '15m'
},
{
ratio: 0.2,
item: '30m'
},
{
ratio: 0.3,
item: '1h'
},
{
ratio: 0.2,
item: '3h'
},
{
ratio: 1,
item: '12h'
},
];
let queryTypeCounter = 0;
/**
* Entrypoint for read scenario
*/
export function read() {
let idx = queryTypeCounter % queryTypeRatioConfig.length;
queryTypeCounter++;
let func = queryTypeRatioConfig[idx].item;
func();
}
let rangeCounter = 0;
/**
* Execute labels query with given client
*/
function readLabels() {
let idx = rangeCounter % rangesRatioConfig.length;
rangeCounter++;
const range = rangesRatioConfig[idx].item;
// Execute query.
let res = client.labelsQueryAt(range, getOffsetStart());
// Assert the response from loki.
checkResponse(res, "successful labels query", range);
}
/**
* Execute label values query with given client
*/
function readLabelValues() {
let idx = rangeCounter % rangesRatioConfig.length;
rangeCounter++;
const range = rangesRatioConfig[idx].item;
// Randomly select label name from pull of the labels.
const label = "namespace";
// Execute query.
let res = client.labelValuesQueryAt(label, range, getOffsetStart());
// Assert the response from loki.
checkResponse(res, "successful label values query", range);
}
const limit = 1000;
const instantQuerySuppliers = [
() => `sum by (container) (rate({namespace="loki-cluster"} |~ "^\\b$" [5m]))`,
];
/**
* Execute instant query with given client
*/
function readInstant() {
// Randomly select the query supplier from the pool
// and call the supplier that provides prepared query.
const query = instantQuerySuppliers[0]();
// Execute query.
let res = client.instantQueryAt(query, limit, getOffsetStart());
// Assert the response from loki.
checkResponse(res, "successful instant query");
}
const rangeQuerySuppliers = [
() => `{namespace="loki-cluster"} |~ "^\\b$"`,
]
/**
* Execute range query with given client
*/
function readRange() {
// Randomly select the query supplier from the pool
// and call the supplier that provides prepared query.
const query = rangeQuerySuppliers[0]();
// Randomly select the range.
let idx = rangeCounter % rangesRatioConfig.length;
rangeCounter++;
const range = rangesRatioConfig[idx].item;
// Execute query.
let res = client.rangeQueryAt(query, range, limit, getOffsetStart());
// Assert the response from loki.
checkResponse(res, "successful range query", range);
}
let seriesSelectorSuppliers = [
() => `{namespace="loki-cluster"}`,
];
/**
* Execute series query with given client
*/
function readSeries() {
// Randomly select the range.
let idx = rangeCounter % rangesRatioConfig.length;
rangeCounter++;
const range = rangesRatioConfig[idx].item;
// Randomly select the series selector from the pool of selectors.
let selector = seriesSelectorSuppliers[0]();
// Execute query.
let res = client.seriesQueryAt(selector, range, getOffsetStart());
// Assert the response from loki.
checkResponse(res, "successful series query", range);
}
const checkResponse = (response, name, range) => {
const checkName = `${name}[${range}]`;
const assertion = {};
assertion[range ? checkName : name] = (res) => {
let success = res.status === 200;
if (!success) {
console.log(res.status, res.body);
}
return success;
};
check(response, assertion);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment