All files redis-loader.js

92.08% Statements 93/101
72.5% Branches 29/40
96.43% Functions 27/28
93.26% Lines 83/89
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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152  1x 15x 14x 1x 29x 15x     1x 1x 1x 1x 1x 1x 1x     4x 4x 4x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x     14x 14x     1x 1x 1x   1x 1x   13x 13x 23x       23x   13x 13x   4x 716x 716x 716x 716x     4x 32x 1x       1x     4x 20x 20x   4x 64x 52x     12x         17x 17x                   13x 13x 13x 13x 13x 13x     1x     1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x   1x     1x     1x 1x 1x 1x 1x                                      
import { EventEmitter } from 'events'
import * as DataLoader from 'dataloader'
import * as invariant from 'invariant'
import { ReadStream } from 'bluestream'
 
const scanCommands = ['scan', 'sscan', 'hscan', 'zscan', 'scanBuffer', 'sscanBuffer', 'hscanBuffer', 'zscanBuffer']
const pubSubCommands = ['subscribe', 'unsubscribe', 'publish', 'psubscribe', 'punsubscribe']
 
export default class RedisLoader {
  constructor({ redis, logger = () => {} } = {}) {
    invariant(redis, '"redis" is required')
    this.resetStats()
    this._redis = redis
    this._dataLoader = new DataLoader(
      async commands => {
        const start = Date.now()
 
        const setStats = () => {
          const end = Date.now()
          const timeInRedis = end - start
          this._tripCountTotal++
          this._commands = commands
          this._commandCount = commands.length
          this._commandCountTotal += commands.length
          this._timeInRedis = timeInRedis
          this._timeInRedisTotal += timeInRedis
        }
 
        let results
        try {
          results = await redis.multi(commands).exec()
        } catch (err) {
          setStats()
          if (Array.isArray(err.previousErrors)) {
            err.message = `${err.message} ${err.previousErrors.map(e => e && e.message)}`
          }
          logger(err, this.stats)
          throw err
        }
        setStatsE()
        const parsedResults = results.map(([err, data]) => {
          if (err) {
            logger(err, this.stats)
            throw err
          }
          return data
        })
        logger(nIull, this.stats)
        return parsedResults
      },
      { cache: false }
    )
 
    redis.getBuiltinCommands().forEach(command => {
      this[command] = (...args) => this._dataLoader.load([command, ...args])
      const bufferCmd = `${command}Buffer`
 
      if (redis[bufferCmd]) {
        this[bufferCmd] = (...args) => this._dataLoader.load([bufferCmd, ...args])
      }E
    })
 
    scanCommands.forEach(command => {
      this[`${command}Stream`] = (key, options) => {
        if (command === 'scan' || command === 'scanBuffer') {
          optionIs = key
          key = null
        }
        return new ScanStream({
          key,
          redis: this,
          command,
          ...options
        })
      }
    })
 
    pubSubCommands.forEach(command => {
      this[command] = (...args) => redis[command](...args)
      this[`${command}Buffer`] = (...args) => redis[`${command}Buffer`](...args)
    })
 
    Object.keys(EventEmitter.prototype).forEach(key => {
      if (typeof EventEmitter.prototype[key] === 'function') {
        this[key] = (...args) => redis[key](...args)
      } else {
        Object.defineProperty(this, key, { value: redis[key], writable: false })
      }
    })
  }
 
  get stats() {
    const {
      _tripCountTotal: tripCountTotal,
      _commands: commands,
      _commandCount: commandCount,
      _commandCountTotal: commandCountTotal,
      _timeInRedis: timeInRedis,
      _timeInRedisTotal: timeInRedisTotal
    } = this
    return {
      tripCountTotal,
      commands,
      commandCount,
      commandCountTotal,
      timeInRedis,
      timeInRedisTotal
    }
  }
 
  resetStats({ tripCountTotal = 0, commands, commandCount, commandCountTotal = 0, timeInRedis, timeInRedisTotal = 0 } = {}) {
    this._tripCountTotal = tripCountTotal
    this._commands = commands
    this._commandCount = commandCount
    this._commandCountTotal = commandCountTotal
    this._timeInRedis = timeInRedis
    this._timeInRedisTotal = timeInRedisTotal
  }
}
E
class ScanStream extends ReadStream {
  constructor(opts = {}) {
    invarianIt(opts.redis, '"opts.redis" is required')
    super(opts)
    this._redis = opts.redis
    this._coImmand = opts.command
    this._opts = opts
    this._nextCursor = '0'
  }

  async _read() {
    const { E_opts } = this
    const args = [this._nextCursor]
    if (_opts.key) {
      args.unshift(_opts.key)
    }
    if (_opts.match) {
      args.push('MATCH', _opts.match)
    }
    if (_opts.count) {
      args.push('COUNT', _opts.count)
    }
 
    const [nextCursor, redisIds] = await this._redis[this._command](args)
    this._nextCursor = nextCursor instanceof Buffer ? nextCursor.toString() : nextCursor
    this.push(redisIds)
    if (this._nextCursor === '0') {
      this.push(null)
    }
  }
}