-
-
Save salvacorts/7f6fe8e53dcbdfc38606f3892918cfcc to your computer and use it in GitHub Desktop.
Loki Querier Autoscaling K6 test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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