index.coffee 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. _ = require("underscore")
  2. async = require "async"
  3. module.exports = RedisSharelatex =
  4. createClient: (opts = {port: 6379, host: "localhost"})->
  5. if !opts.retry_max_delay?
  6. opts.retry_max_delay = 5000 # ms
  7. if opts.password?
  8. opts.auth_pass = opts.password
  9. delete opts.password
  10. if opts.endpoints?
  11. standardOpts = _.clone(opts)
  12. delete standardOpts.endpoints
  13. delete standardOpts.masterName
  14. client = require("redis-sentinel").createClient opts.endpoints, opts.masterName, standardOpts
  15. client.healthCheck = RedisSharelatex.singleInstanceHealthCheckBuilder(client)
  16. else if opts.cluster?
  17. Redis = require("ioredis")
  18. standardOpts = _.clone(opts)
  19. delete standardOpts.cluster
  20. delete standardOpts.key_schema
  21. client = new Redis.Cluster(opts.cluster, standardOpts)
  22. client.healthCheck = RedisSharelatex.clusterHealthCheckBuilder(client)
  23. RedisSharelatex._monkeyPatchIoredisExec(client)
  24. else
  25. standardOpts = _.clone(opts)
  26. delete standardOpts.port
  27. delete standardOpts.host
  28. client = require("redis").createClient opts.port, opts.host, standardOpts
  29. client.healthCheck = RedisSharelatex.singleInstanceHealthCheckBuilder(client)
  30. return client
  31. HEARTBEAT_TIMEOUT: 2000
  32. singleInstanceHealthCheckBuilder: (client) ->
  33. healthCheck = (callback) ->
  34. RedisSharelatex._checkClient(client, callback)
  35. return healthCheck
  36. clusterHealthCheckBuilder: (client) ->
  37. healthCheck = (callback) ->
  38. jobs = client.nodes("all").map (node) =>
  39. (cb) => RedisSharelatex._checkClient(node, cb)
  40. async.parallel jobs, callback
  41. return healthCheck
  42. _checkClient: (client, callback) ->
  43. callback = _.once(callback)
  44. timer = setTimeout () ->
  45. error = new Error("redis client ping check timed out")
  46. console.error {
  47. err: error,
  48. key: client.options?.key # only present for cluster
  49. }, "client timed out"
  50. callback(error)
  51. , RedisSharelatex.HEARTBEAT_TIMEOUT
  52. client.ping (err) ->
  53. clearTimeout timer
  54. callback(err)
  55. _monkeyPatchIoredisExec: (client) ->
  56. _multi = client.multi
  57. client.multi = (args...) ->
  58. multi = _multi.call(client, args...)
  59. _exec = multi.exec
  60. multi.exec = (callback = () ->) ->
  61. _exec.call multi, (error, result) ->
  62. # ioredis exec returns an results like:
  63. # [ [null, 42], [null, "foo"] ]
  64. # where the first entries in each 2-tuple are
  65. # presumably errors for each individual command,
  66. # and the second entry is the result. We need to transform
  67. # this into the same result as the old redis driver:
  68. # [ 42, "foo" ]
  69. filtered_result = []
  70. for entry in result or []
  71. if entry[0]?
  72. return callback(entry[0])
  73. else
  74. filtered_result.push entry[1]
  75. callback error, filtered_result
  76. return multi