import { IImpressionsCacheAsync } from '../types'; import { IMetadata } from '../../dtos/types'; import SplitIO from '../../../types/splitio'; import { StoredImpressionWithMetadata } from '../../sync/submitters/types'; import { ILogger } from '../../logger/types'; import { impressionsToJSON } from '../utils'; import type { RedisAdapter } from './RedisAdapter'; const IMPRESSIONS_TTL_REFRESH = 3600; // 1 hr export class ImpressionsCacheInRedis implements IImpressionsCacheAsync { private readonly log: ILogger; private readonly key: string; private readonly redis: RedisAdapter; private readonly metadata: IMetadata; constructor(log: ILogger, key: string, redis: RedisAdapter, metadata: IMetadata) { this.log = log; this.key = key; this.redis = redis; this.metadata = metadata; } track(impressions: SplitIO.ImpressionDTO[]): Promise { // @ts-ignore return this.redis.rpush( this.key, ...impressionsToJSON(impressions, this.metadata), ).then((queuedCount: number) => { // If this is the creation of the key on Redis, set the expiration for it in 1hr. if (queuedCount === impressions.length) { return this.redis.expire(this.key, IMPRESSIONS_TTL_REFRESH); } }); } count(): Promise { return this.redis.llen(this.key).catch(() => 0); } drop(count?: number): Promise { if (!count) return this.redis.del(this.key); return this.redis.ltrim(this.key, count, -1); } popNWithMetadata(count: number): Promise { return this.redis.lrange(this.key, 0, count - 1).then((items: string[]) => { return this.redis.ltrim(this.key, items.length, -1).then(() => { // This operation will simply do nothing if the key no longer exists (queue is empty) // It's only done in the "successful" exit path so that the TTL is not overriden if impressons weren't // popped correctly. This will result in impressions getting lost but will prevent the queue from taking // a huge amount of memory. this.redis.expire(this.key, IMPRESSIONS_TTL_REFRESH).catch(() => { }); // noop catch handler return items.map((item: string) => JSON.parse(item) as StoredImpressionWithMetadata); }); }); } }