<commit_before>import { TimeSeries } from './time-series';
import {
  ICallback,
  TRedisClientMulti,
  TTimeSeriesRange,
} from '../../../../types';
import { ArgumentError } from '../errors/argument.error';
import { RedisClient } from '../../redis-client/redis-client';
import { LockManager } from '../lock-manager/lock-manager';

export class HashTimeSeries extends TimeSeries {
  protected indexKey: string;
  protected lockManager: LockManager | null = null;

  constructor(
    redisClient: RedisClient,
    key: string,
    indexKey: string,
    lockKey: string,
    expireAfter = 0,
    retentionTime = 24 * 60 * 60,
    windowSize = 60,
    readonly = false,
  ) {
    super(redisClient, key, expireAfter, retentionTime, windowSize, readonly);
    this.lockManager = new LockManager(redisClient, lockKey, 60000);
    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 process = (cb: ICallback<void>) => {
      const ts = TimeSeries.getCurrentTimestamp();
      const max = ts - this.retentionTime;
      this.redisClient.zrangebyscore(
        this.indexKey,
        '-inf',
        `${max}`,
        (err, reply) => {
          if (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();
        },
      );
    };
    if (this.lockManager) {
      this.lockManager.acquireLock((err, locked) => {
        if (err) cb(err);
        else if (locked) process(cb);
        else cb();
      });
    } else process(cb);
  }

  getRange(from: number, to: number, cb: ICallback<TTimeSeriesRange>): void {
    if (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) => {
        if (err) cb(err);
        else {
          const replyRange = reply ?? [];
          const range = timestamps.map((i, index) => ({<commit_msg>Clean up<commit_after>import { TimeSeries } from './time-series';
import {
  ICallback,
  TRedisClientMulti,
  TTimeSeriesRange,
} from '../../../../types';
import { ArgumentError } from '../errors/argument.error';
import { RedisClient } from '../../redis-client/redis-client';
import { LockManager } from '../lock-manager/lock-manager';

export class HashTimeSeries extends TimeSeries {
  protected indexKey: string;
  protected lockManager: LockManager;

  constructor(
    redisClient: RedisClient,
    key: string,
    indexKey: string,
    lockKey: string,
    expireAfter = 0,
    retentionTime = 24 * 60 * 60,
    windowSize = 60,
    readonly = false,
  ) {
    super(redisClient, key, expireAfter, retentionTime, windowSize, readonly);
    this.lockManager = new LockManager(redisClient, lockKey, 60000);
    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 process = (cb: ICallback<void>) => {
      const ts = TimeSeries.getCurrentTimestamp();
      const max = ts - this.retentionTime;
      this.redisClient.zrangebyscore(
        this.indexKey,
        '-inf',
        `${max}`,
        (err, reply) => {
          if (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();
        },
      );
    };
    this.lockManager.acquireLock((err, locked) => {
      if (err) cb(err);
      else if (locked) process(cb);
      else cb();
    });
  }

  getRange(from: number, to: number, cb: ICallback<TTimeSeriesRange>): void {
    if (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) => {
        if (err) cb(err);
        else {
          const replyRange = reply ?? [];
          const range = timestamps.map((i, index) => ({
{'fraction_non_alphanumeric': 0.1490748898678414, 'fraction_numerical': 0.0052863436123348016, 'mean_word_length': 2.475811390079608, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4548788', 'n_tokens_mistral': 1820, 'n_tokens_neox': 1787, 'n_words': 415}