All files / redis-smq-api/src/plugins/message-rate/common hash-time-series.ts

92.85% Statements 39/42
80.95% Branches 17/21
100% Functions 11/11
97.29% Lines 36/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 7957x     57x     57x       877x 877x 877x               75x 75x 75x 75x 13x 13x     75x 22x 22x 22x 53x       218x 218x 218x         218x 218x 1x 1x 1x 1x 217x           521x         521x 521x   31040x 521x 521x   521x 31040x       521x            
import { TimeSeries } from './time-series';
import { ICallback, TRedisClientMulti } from 'redis-smq/dist/types';
import { RedisClient } from 'redis-smq/dist/src/common/redis-client/redis-client';
import { ArgumentError } from 'redis-smq/dist/src/common/errors/argument.error';
import { IHashTimeSeriesParams, TTimeSeriesRange } from '../../../../types';
 
export class HashTimeSeries extends TimeSeries<IHashTimeSeriesParams> {
  protected indexKey: string;
 
  constructor(redisClient: RedisClient, params: IHashTimeSeriesParams) {
    super(redisClient, params);
    const { indexKey } = params;
    this.indexKey = indexKey;
  }
 
  add(
    ts: number,
    value: number,
    mixed: ICallback<void> | TRedisClientMulti,
  ): void {
    const process = (multi: TRedisClientMulti) => {
      multi.hincrby(this.key, String(ts), value);
      multi.zadd(this.indexKey, ts, ts);
      if (this.expireAfter) {
        multi.expire(this.key, this.expireAfter);
        multi.expire(this.indexKey, this.expireAfter);
      }
    };
    if (typeof mixed === 'function') {
      const multi = this.redisClient.multi();
      process(multi);
      this.redisClient.execMulti(multi, (err) => mixed(err));
    } else process(mixed);
  }
 
  cleanUp(cb: ICallback<void>): void {
    const ts = TimeSeries.getCurrentTimestamp();
    const max = ts - this.retentionTime;
    this.redisClient.zrangebyscore(
      this.indexKey,
      '-inf',
      `${max}`,
      (err, reply) => {
        Iif (err) cb(err);
        else if (reply && reply.length) {
          const multi = this.redisClient.multi();
          multi.zrem(this.indexKey, ...reply);
          multi.hdel(this.key, ...reply);
          this.redisClient.execMulti(multi, (err) => cb(err));
        } else cb();
      },
    );
  }
 
  getRange(from: number, to: number, cb: ICallback<TTimeSeriesRange>): void {
    Iif (to <= from) {
      cb(
        new ArgumentError(`Expected parameter [to] to be greater than [from]`),
      );
    } else {
      const length = to - from;
      const timestamps = new Array(length)
        .fill(0)
        .map((_: number, index: number) => String(from + index));
      this.redisClient.hmget(this.key, timestamps, (err, reply) => {
        Iif (err) cb(err);
        else {
          const replyRange = reply ?? [];
          const range = timestamps.map((i, index) => ({
            timestamp: Number(i),
            value: Number(replyRange[index] ?? 0),
          }));
          cb(null, range);
        }
      });
    }
  }
}