I am currently using IoRedis and DragonflyDB to implement rate limiting in my web application. I have a TTL of 5 seconds which should, in theory remove the keys from the redis DB after this TTL has been completed. This works, most of the time. However, sometimes the keys never expire, even though they have a ttl of 5 seconds. I completely stumped as to why this is happening and I am unable to recreate it. Does anybody have any ideas?
type RateLimitData = { [key: string]: number };
const prefix = 'rateLimit';
const ttl = 5;
const create = async (userId: string, path: string): Promise<void> => {
const data = { [path]: 1 };
await redis.call(
'JSON.SET',
`${prefix}:${userId}`,
'$',
JSON.stringify(data)
);
await redis.expire(`${prefix}:${userId}`, ttl);
};
const update = async (userId: string, path: string): Promise<void> => {
const previousCache = await get(userId);
if (previousCache) {
const aggregate = previousCache[path] ? previousCache[path] + 1 : 1;
const updatedCache = { ...previousCache, [path]: aggregate };
await redis.call(
'JSON.SET',
`${prefix}:${userId}`,
'$',
JSON.stringify(updatedCache)
);
} else {
await create(userId, path);
}
};
const get = async (userId: string): Promise<RateLimitData | null> => {
const data = await redis.call('JSON.GET', `${prefix}:${userId}`);
if (!data) return null;
return JSON.parse(data as string) as RateLimitData;
};
export const rateLimit = { create, update, get };
Dragonfly version: docker.dragonflydb.io/dragonflydb/dragonfly:v1.2.1
"ioredis": "^5.3.2",
A timeout is removed when you do an update that replaces the value for the key. My guess is that the keys that never expires has been updated.
From the expire
command in Dragonfly documentation (same as for Redis):
The timeout will only be cleared by commands that delete or overwrite the contents of the key, including DEL, SET, GETSET and all the *STORE commands.