New Achievement Unlocked: Mastering Redis Performance with xk6-redis

Yusuf Tayman
5 min readAug 24, 2023

In these times when even milliseconds are important, we may experience performance problems with Redis, which is indispensable for our applications. So how do we prevent this and how should we measure our Redis performance? Bringing together the three cornerstones of Observability, k6 gives us the key to performance this time and offers us xk6-redis.

Redis

Whilst Redis is often thought of as a data structure server, it stands out for its ability to store and retrieve data at astonishing speed. Thanks to its in-memory storage mechanism, it minimises latency in the basic building blocks of our applications and offers us the ideal solution for issues such as caching, real-time analytics, and session management.

Imagine a real-time application where messages need to be delivered instantly to users around the world. With Redis, each message can be stored as a key and its recipients as values, enabling lightning-fast message delivery without straining the application’s resources.

However, it can sometimes fall victim to this speed, and as with any application, it can have problems, which can lead to data loss and poor user experience. Let’s talk about how to prevent it simply.

xk6-redis

The marriage of k6 and Redis gives us the key to performance on a golden platter. xk6-redis has the infrastructure to evaluate Redis under real-world conditions. Actually, we can predict everything right now.

For example, you know how many users and an increase will come to your system before Black Friday, but if you cannot simulate it. Thanks to xk6-redis, you can simulate the increase in user traffic or simulate the loads that will be experienced at the time of any sale. Well, good questions come into play here, Can I only test Redis or can I test any external structure that uses Redis?

Welcome to k6, you cannot say no here because the answer to everything is yes.

Measure Redis Performance

In order to use xk6-redis, we must first realise the power of k6 extensions. And we need to perform a small extra operation. Firstly we need to install xk6 and then we need to build the xk6-redis extension. We can do this with two simple commands.

  • Install the xk6:
go install go.k6.io/xk6/cmd/xk6@latest
  • Build the binary:
xk6 build --with github.com/grafana/xk6-redis

Now we can move on to our main operations. To connect to the Redis instance, we define our host and password information with the redis.client command.

import redis from "k6/x/redis";
import exec from "k6/execution";

export const options = {
scenarios: {
redisPerformance: {
executor: "shared-iterations",
vus: 10,
iterations: 200,
exec: "measureRedisPerformance",
},
},
};

const redisClient = new redis.Client({
addrs: __ENV.REDIS_ADDRS.split(",") || new Array("localhost:6379"), // in the form of "host:port", separated by commas
password: __ENV.REDIS_PASSWORD || "",
});

Then we define our function and determine our key at first, Since it will be a parallel run, we assign a special key to each VU (Virtual User). In this way, we do not run our test on the same key all the time. First of all, we create our Redis key, then we increase the value with our incrBy property. Then we delete the key we created and check it.

export function measureRedisPerformance() {
const key = `redisKey-${exec.vu.idInTest}`;

redisClient
.set(`RedisKey-${exec.vu.idInTest}`, 1)
.then(() => redisClient.get(`RedisKey-${exec.vu.idInTest}`))
.then((value) => redisClient.incrBy(`RedisKey-${exec.vu.idInTest}`, value))
.then((_) => redisClient.del(`RedisKey-${exec.vu.idInTest}`))
.then((_) => redisClient.exists(`RedisKey-${exec.vu.idInTest}`))
.then((exists) => {
if (exists !== 0) {
throw new Error("RedisKey should have been deleted");
}
});
}

Measure Redis Performance Using Redis Data

Here we have a structure where we fetch data over Redis using GraphQL. First of all, we provide our connection to Redis again.

import { check } from "k6";
import http from "k6/http";
import redis from "k6/x/redis";
import { textSummary } from "https://jslib.k6.io/k6-summary/0.0.1/index.js";

export const options = {
scenarios: {
usingRedisData: {
executor: "shared-iterations",
vus: 10,
iterations: 200,
exec: "measureUsingRedisData",
},
},
};

const redisClient = new redis.Client({
addrs: __ENV.REDIS_ADDRS.split(",") || new Array("localhost:6379"), // in the form of "host:port", separated by commas
password: __ENV.REDIS_PASSWORD || "",
});

Then, we create our load on the application by selecting random values on the key we set with srandmember via Redis.

const book_ids = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

export function setup() {
redisClient.sadd("book_ids", ...bookIds);
}

export function measureUsingRedisData() {
// Pick a random book id from the dedicated redis set,
// we have filled in setup().
redisClient
.srandmember("book_ids")
.then((randomID) => {
const url = `https://localhost:4000/books/${randomID}`;
const res = http.get(url);

check(res, {
"status is 200": (r) => r.status === 200,
"content-type is application/json": (r) =>
r.headers["content-type"] === "application/json",
});

return url;
})
.then((url) => redisClient.hincrby("k6_book_fetched", url, 1));
}

export function teardown() {
redisClient.del("book_ids");
}

xk6-redis is not only limited to this, you can also create certain custom metrics, detail their outputs and access the metrics you want to focus on. For example, let’s examine redis_latency.

import { Trend } from 'k6/metrics';
import redis from 'k6/x/redis';

const RedisLatencyMetric = new Trend('redis_latency', true);

export const options = {
vus: 40,
duration: '10s',
};

const client = new redis.Client({
addr: 'localhost:6379',
password: '',
});

export function setup() {
client.set('key', 'value', 0);
}

export default function () {
const start = Date.now();
client.get('key');
const latency = Date.now() - start;
RedisLatencyMetric.add(latency);
}

After setting a certain key and value in setup, we can control latency by performing continuous get operation. This situation can definitely change depending on the use of your applications. Our output will be as follows.

iteration_duration...: avg=882.47µs min=56.33µs med=731.12µs max=15.32ms p(90)=1.2ms p(95)=1.5ms
iterations...........: 806755 70660.436149/s
redis_latency........: avg=804.8µs min=0s med=1ms max=16ms p(90)=1.1ms p(95)=1.2ms

On the other hand, you can find what you can do with xk6-redis here.

Conclusion

By merging Redis’s efficiency with xk6-redis’s load testing prowess, developers can proactively fine-tune their Redis setups, preemptively addressing potential bottlenecks, and guaranteeing a seamless user experience even under the most demanding scenarios.

So, if you’re ready to elevate your Redis deployment to its peak performance, unlock the potential of xk6-redis, and embark on a journey to Redis performance mastery. Your applications — and your users — will thank you for it.

--

--