overleaf/libraries/redis-wrapper/index.coffee

87 lines
2.7 KiB
CoffeeScript

_ = require("underscore")
async = require "async"
module.exports = RedisSharelatex =
createClient: (opts = {port: 6379, host: "localhost"})->
if !opts.retry_max_delay?
opts.retry_max_delay = 5000 # ms
if opts.password?
opts.auth_pass = opts.password
delete opts.password
if opts.endpoints?
standardOpts = _.clone(opts)
delete standardOpts.endpoints
delete standardOpts.masterName
client = require("redis-sentinel").createClient opts.endpoints, opts.masterName, standardOpts
client.healthCheck = RedisSharelatex.singleInstanceHealthCheckBuilder(client)
else if opts.cluster?
Redis = require("ioredis")
standardOpts = _.clone(opts)
delete standardOpts.cluster
delete standardOpts.key_schema
client = new Redis.Cluster(opts.cluster, standardOpts)
client.healthCheck = RedisSharelatex.clusterHealthCheckBuilder(client)
RedisSharelatex._monkeyPatchIoredisExec(client)
else
standardOpts = _.clone(opts)
delete standardOpts.port
delete standardOpts.host
client = require("redis").createClient opts.port, opts.host, standardOpts
client.healthCheck = RedisSharelatex.singleInstanceHealthCheckBuilder(client)
return client
HEARTBEAT_TIMEOUT: 2000
singleInstanceHealthCheckBuilder: (client) ->
healthCheck = (callback) ->
RedisSharelatex._checkClient(client, callback)
return healthCheck
clusterHealthCheckBuilder: (client) ->
healthCheck = (callback) ->
jobs = client.nodes("all").map (node) =>
(cb) => RedisSharelatex._checkClient(node, cb)
async.parallel jobs, callback
return healthCheck
_checkClient: (client, callback) ->
callback = _.once(callback)
timer = setTimeout () ->
error = new Error("redis client ping check timed out")
console.error {
err: error,
key: client.options?.key # only present for cluster
}, "client timed out"
callback(error)
, RedisSharelatex.HEARTBEAT_TIMEOUT
client.ping (err) ->
clearTimeout timer
callback(err)
_monkeyPatchIoredisExec: (client) ->
_multi = client.multi
client.multi = (args...) ->
multi = _multi.call(client, args...)
_exec = multi.exec
multi.exec = (callback = () ->) ->
_exec.call multi, (error, result) ->
# ioredis exec returns an results like:
# [ [null, 42], [null, "foo"] ]
# where the first entries in each 2-tuple are
# presumably errors for each individual command,
# and the second entry is the result. We need to transform
# this into the same result as the old redis driver:
# [ 42, "foo" ]
filtered_result = []
for entry in result or []
if entry[0]?
return callback(entry[0])
else
filtered_result.push entry[1]
callback error, filtered_result
return multi