index.coffee 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. _ = require("underscore")
  2. module.exports = RedisSharelatex =
  3. createClient: (opts = {port: 6379, host: "localhost"})->
  4. if !opts.retry_max_delay?
  5. opts.retry_max_delay = 5000 # ms
  6. if opts.password?
  7. opts.auth_pass = opts.password
  8. delete opts.password
  9. if opts.endpoints?
  10. standardOpts = _.clone(opts)
  11. delete standardOpts.endpoints
  12. delete standardOpts.masterName
  13. client = require("redis-sentinel").createClient opts.endpoints, opts.masterName, standardOpts
  14. else
  15. standardOpts = _.clone(opts)
  16. delete standardOpts.port
  17. delete standardOpts.host
  18. client = require("redis").createClient opts.port, opts.host, standardOpts
  19. return client
  20. createRobustSubscriptionClient: (opts, heartbeatOpts = {}) ->
  21. sub = RedisSharelatex.createClient(opts)
  22. pub = RedisSharelatex.createClient(opts)
  23. heartbeatInterval = heartbeatOpts.heartbeat_interval or 1000 #ms
  24. reconnectAfter = heartbeatOpts.reconnect_after or 5000 #ms
  25. id = require("crypto").pseudoRandomBytes(16).toString("hex")
  26. heartbeatChannel = "heartbeat-#{id}"
  27. lastHeartbeat = Date.now()
  28. sub.subscribe heartbeatChannel, (error) ->
  29. if error?
  30. console.error "ERROR: failed to subscribe to #{heartbeatChannel} channel", error
  31. sub.on "message", (channel, message) ->
  32. if channel == heartbeatChannel
  33. lastHeartbeat = Date.now()
  34. reconnectIfInactive = () ->
  35. timeSinceLastHeartbeat = Date.now() - lastHeartbeat
  36. if timeSinceLastHeartbeat > reconnectAfter
  37. console.warn "[#{new Date()}] No heartbeat for #{timeSinceLastHeartbeat}ms on #{heartbeatChannel}, reconnecting"
  38. # If the client realises it isn't connected then will be trying to
  39. # restablish the connection, so there's nothing for us to do. If
  40. # it still thinks it's connected, then disconnect it and start to reconnect.
  41. if sub.connected
  42. sub.end()
  43. # We ended the connection, but want to start it up again, so set
  44. # the internal closing variable:
  45. sub.closing = false
  46. # Trigger the reconnect:
  47. sub.connection_gone("no heartbeat for #{timeSinceLastHeartbeat}ms")
  48. # Reset timer after triggering a reconnect to avoid potential cascading failure.
  49. lastHeartbeat = Date.now()
  50. setInterval () ->
  51. pub.publish heartbeatChannel, "PING"
  52. reconnectIfInactive()
  53. , heartbeatInterval
  54. return sub