2014-09-26 09:46:23 -04:00
|
|
|
_ = require("underscore")
|
2017-05-04 10:38:07 -04:00
|
|
|
async = require "async"
|
2014-09-26 09:46:23 -04:00
|
|
|
|
2014-11-19 06:01:02 -05:00
|
|
|
module.exports = RedisSharelatex =
|
|
|
|
createClient: (opts = {port: 6379, host: "localhost"})->
|
2014-11-19 06:02:27 -05:00
|
|
|
if !opts.retry_max_delay?
|
|
|
|
opts.retry_max_delay = 5000 # ms
|
|
|
|
|
2014-09-29 06:40:40 -04:00
|
|
|
if opts.password?
|
|
|
|
opts.auth_pass = opts.password
|
|
|
|
delete opts.password
|
2014-09-26 09:46:23 -04:00
|
|
|
if opts.endpoints?
|
|
|
|
standardOpts = _.clone(opts)
|
|
|
|
delete standardOpts.endpoints
|
|
|
|
delete standardOpts.masterName
|
|
|
|
client = require("redis-sentinel").createClient opts.endpoints, opts.masterName, standardOpts
|
2017-04-12 09:53:34 -04:00
|
|
|
client.healthCheck = RedisSharelatex.singleInstanceHealthCheckBuilder(client)
|
|
|
|
else if opts.cluster?
|
|
|
|
Redis = require("ioredis")
|
2017-06-16 10:28:55 -04:00
|
|
|
standardOpts = _.clone(opts)
|
|
|
|
delete standardOpts.cluster
|
2017-06-19 10:46:21 -04:00
|
|
|
delete standardOpts.key_schema
|
2017-06-16 10:28:55 -04:00
|
|
|
client = new Redis.Cluster(opts.cluster, standardOpts)
|
2017-04-12 09:53:34 -04:00
|
|
|
client.healthCheck = RedisSharelatex.clusterHealthCheckBuilder(client)
|
|
|
|
RedisSharelatex._monkeyPatchIoredisExec(client)
|
2014-09-25 12:33:27 -04:00
|
|
|
else
|
2014-09-26 09:46:23 -04:00
|
|
|
standardOpts = _.clone(opts)
|
|
|
|
delete standardOpts.port
|
|
|
|
delete standardOpts.host
|
|
|
|
client = require("redis").createClient opts.port, opts.host, standardOpts
|
2017-04-12 09:53:34 -04:00
|
|
|
client.healthCheck = RedisSharelatex.singleInstanceHealthCheckBuilder(client)
|
2014-11-19 18:18:56 -05:00
|
|
|
return client
|
2017-04-12 09:53:34 -04:00
|
|
|
|
|
|
|
HEARTBEAT_TIMEOUT: 2000
|
|
|
|
singleInstanceHealthCheckBuilder: (client) ->
|
|
|
|
healthCheck = (callback) ->
|
|
|
|
RedisSharelatex._checkClient(client, callback)
|
|
|
|
return healthCheck
|
|
|
|
|
|
|
|
clusterHealthCheckBuilder: (client) ->
|
|
|
|
healthCheck = (callback) ->
|
2017-05-04 10:38:07 -04:00
|
|
|
jobs = client.nodes("all").map (node) =>
|
2017-04-12 09:53:34 -04:00
|
|
|
(cb) => RedisSharelatex._checkClient(node, cb)
|
|
|
|
async.parallel jobs, callback
|
2014-11-19 18:18:56 -05:00
|
|
|
|
2017-04-12 09:53:34 -04:00
|
|
|
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)
|
2014-11-19 18:18:56 -05:00
|
|
|
|
2017-04-12 09:53:34 -04:00
|
|
|
_monkeyPatchIoredisExec: (client) ->
|
|
|
|
_multi = client.multi
|
|
|
|
client.multi = (args...) ->
|
|
|
|
multi = _multi.call(client, args...)
|
|
|
|
_exec = multi.exec
|
2017-05-05 08:36:39 -04:00
|
|
|
multi.exec = (callback = () ->) ->
|
|
|
|
_exec.call multi, (error, result) ->
|
2017-04-12 09:53:34 -04:00
|
|
|
# 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
|
|
|
|
|
2014-11-19 18:18:56 -05:00
|
|
|
|
2017-04-12 09:53:34 -04:00
|
|
|
|
2014-11-19 18:18:56 -05:00
|
|
|
|