All files redis-loader.js

92% Statements 92/100
76.6% Branches 36/47
96.43% Functions 27/28
93.18% Lines 82/88
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 153 154 155 156 157 158  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 1x 1x     13x 13x     14x 14x 13x 13x 23x       23x   13x 13x     1x 1x 1x   1x 1x     4x 716x 716x 716x 716x     4x 32x 1x       1x     4x 20x 20x   4x 64x 52x     12x         17x     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()
        this._stats.commands = commands
        this._stats.sentCommandCount = commands.length
        this._stats.sentCommandCountTotal += commands.length
 
        const setEndStats = (error, results) => {
          const end = Date.now()
          const timeInRedis = end - start
          this._stats.tripCountTotal++
          this._stats.timeInRedis = timeInRedis
          this._stats.timeInRedisTotal += timeInRedis
          if (error) {
            this._stats.responseCount = 1
            this._stats.responseCountTotal += 1
          } else {
            this._stats.responseCount = results.length
            this._stats.responseCountTotal += results.length
          }
        }
 
        try {
          const results = await redis.multi(commands).exec()
          setEndStats(null, results)
          const parsedResults = results.map(([err, data]) => {
            if (err) {
              logger(err, this.stats)
              throw err
            }
            return data
          })
          logger(nulIl, this.stats)
          return parsedResults
        } catch (err) {
          setEndStats(err)
          if (Array.isArray(err.previousErrors)) {
            err.message = `${err.message} ${err.previousErrors.map(e => e && e.message)}`
          }
          logger(err, this.stats)
          throw err
        }
      },
      { cache: fEalse }
    )
 
    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') {
          options = key
          key = Inull
        }
        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() {
    return {
      ...this._stats
    }
  }
 
  resetStats({
    tripCountTotal = 0,
    commands = null,
    sentCommandCount = 0,
    sentCommandCountTotal = 0,
    responseCount = 0,
    responseCountTotal = 0,
    timeInRedis = 0,
    timeInRedisTotal = 0
  } = {}) {
    this._stats = {
      tripCountTotal,
      commands,
      sentCommandCount,
      responseCount,
      sentCommandCountTotal,
      responseCountTotal,
      timeInRedis,
      timeInRedisTotal
    }E
  }
}
I
class ScanStream extends ReadStream {
  constructor(opts = {}) {
    invarianIt(opts.redis, '"opts.redis" is required')
    super(opts)
    this._redis = opts.redis
    this._command = opts.command
    this._opts = opts
    this._nextCursor = '0'
  }E
 
  async _read() {
    const { _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)
    }
  }
}