Use k6 to load test log queries
When designing a test scenario for load testing the read path of a Loki installation, it is important to know what types of queries you expect.
Supported query types
Loki has 5 types of queries:
- instant query
- range query
- labels query
- label values query
- series query
In a real-world use-case, such as querying Loki using it as a Grafana data source, all of these queries are used. Each of them has a different API endpoint. The xk6-loki extension provides a Javascript API for all these query types.
Instant query
Instant queries can be executed using the function instantQuery(query, limit)
on the loki.Client
instance:
Javascript example code fragment:
export default () => {
client.instantQuery(`rate({app="my-app-name"} | logfmt | level="error" [5m])`);
}
Range query
Range queries can be executed using the function rangeQuery(query, duration, limit)
on the loki.Client
instance:
Javascript example code fragment:
export default () => {
client.rangeQuery(`{app="my-app-name"} | logfmt | level="error"`, "15m");
}
Labels query
Labels queries can be executed using the function labelsQuery(duration)
on the loki.Client
instance:
Javascript example code fragment:
export default () => {
client.labelsQuery("10m");
}
Label values query
Label values queries can be executed using the function labelValuesQuery(label, duration)
on the loki.Client
instance:
Javascript example code fragment:
export default () => {
client.labelValuesQuery("app", "10m");
}
Series query
Series queries can be executed using the function seriesQuery(matcher, range)
on the loki.Client
instance:
Javascript example code fragment:
export default () => {
client.seriesQuery(`match[]={app=~"loki-.*"}`, "10m");
}
Metrics
The extension collects metrics that are printed in the end-of-test summary in addition to the built-in metrics. These metrics are collected only for instant and range queries.
name | description |
---|---|
loki_bytes_processed_per_second | amount of bytes processed by Loki per second |
loki_bytes_processed_total | total amount of bytes processed by Loki |
loki_lines_processed_per_second | amount of lines processed by Loki per second |
loki_lines_processed_total | total amount of lines processed by Loki |
Labels pool
With the xk6-loki extension, you can use the field labels
on the Config
object. It contains label names and values that are generated in a reproducible
manner. Use the same labels cardinality configuration for both write
and
read
testing.
Javascript example code fragment:
const labelCardinality = {
"app": 5,
"namespace": 2,
};
const conf = new loki.Config(BASE_URL, 10000, 1.0, labelCardinality);
const client = new loki.Client(conf);
function randomChoice(items) {
return items[Math.floor(Math.random() * items.length)];
}
export default() {
let app = randomChoice(conf.labels.app);
let namespace = randomChoice(conf.labels.namespace);
client.rangeQuery(`{app="${app}", namespace="${namespace}"} | logfmt | level="error"`, "15m");
}
Alternatively, you can define your own pool of label names and values, and then randomly select labels from your pool instead of a generated pool.
Javascript example
A more complex example of a read scenario can be found in xk6-loki repository. The test file read-scenario.js can be resused and extended for your needs.
It allows you to configure ratios for each type of query and the ratios of time ranges.
Javascript example:
const queryTypeRatioConfig = [
{
ratio: 0.1,
item: readLabels
},
{
ratio: 0.15,
item: readLabelValues
},
{
ratio: 0.05,
item: readSeries
},
{
ratio: 0.5,
item: readRange
},
{
ratio: 0.2,
item: readInstant
},
];
This configuration would execute approximately
- 10% labels requests
- 15% label values requests
- 5% requests for series
- 50% range queries
- 20% instant queries
during a test run.