diff --git a/workers/notifier/src/redisHelper.ts b/workers/notifier/src/redisHelper.ts index 56923b75..02f01f02 100644 --- a/workers/notifier/src/redisHelper.ts +++ b/workers/notifier/src/redisHelper.ts @@ -1,6 +1,7 @@ import { createClient, RedisClientType } from 'redis'; import { Rule } from '../types/rule'; import { NotifierEvent } from '../types/notifier-task'; +import { MS_IN_SEC } from '../../../lib/utils/consts'; /** * Class with helper functions for working with Redis @@ -57,12 +58,14 @@ export default class RedisHelper { local key = KEYS[1] local currentTimestamp = tonumber(ARGV[1]) local thresholdPeriod = tonumber(ARGV[2]) + local ttl = tonumber(ARGV[3]) local startPeriodTimestamp = tonumber(redis.call("HGET", key, "timestamp")) if ((startPeriodTimestamp == nil) or (currentTimestamp >= startPeriodTimestamp + thresholdPeriod)) then redis.call("HSET", key, "timestamp", currentTimestamp) redis.call("HSET", key, "eventsCount", 0) + redis.call("EXPIRE", key, ttl) end local newCounter = redis.call("HINCRBY", key, "eventsCount", 1) @@ -71,11 +74,16 @@ export default class RedisHelper { const key = `${projectId}:${ruleId}:${groupHash}:${thresholdPeriod}:times`; + /** + * Treshold period is in milliseconds, but redis expects ttl in seconds + */ + const ttl = Math.floor(thresholdPeriod / MS_IN_SEC); + const currentTimestamp = Date.now(); const currentEventCount = await this.redisClient.eval(script, { keys: [ key ], - arguments: [currentTimestamp.toString(), thresholdPeriod.toString()], + arguments: [currentTimestamp.toString(), thresholdPeriod.toString(), ttl.toString()], }) as number; return (currentEventCount !== null) ? currentEventCount : 0; diff --git a/workers/notifier/tests/redisHelper.test.ts b/workers/notifier/tests/redisHelper.test.ts index 580663ae..5dac298b 100644 --- a/workers/notifier/tests/redisHelper.test.ts +++ b/workers/notifier/tests/redisHelper.test.ts @@ -94,5 +94,37 @@ describe('RedisHelper', () => { expect(currentEventCount).toBe(1); expect(currentlyStoredTimestamp).toBe(Date.now().toString()); }); + + describe('key expiration period', () => { + it('should expire key after ttl period', async () => { + const ruleId = 'ruleId'; + const groupHash = 'groupHash'; + const projectId = 'projectId'; + const thresholdPeriod = 2000; // 2 seconds in milliseconds + const key = `${projectId}:${ruleId}:${groupHash}:${thresholdPeriod}:times`; + + /** + * Call computeEventCountForPeriod to set the key + */ + await redisHelper.computeEventCountForPeriod(projectId, ruleId, groupHash, thresholdPeriod); + + /** + * Verify, that key exists + */ + let value = await redisClient.hGet(key, 'eventsCount'); + expect(value).not.toBeNull(); + + /** + * Wait for the TTL to expire (slightly longer than threshold period) + */ + await new Promise(resolve => setTimeout(resolve, thresholdPeriod + 500)); + + /** + * Verify, that key has expired and removed from the redis storage + */ + value = await redisClient.hGet(key, 'eventsCount'); + expect(value).toBeNull(); + }); + }); }); });