RedisManager.coffee 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. Settings = require "settings-sharelatex"
  2. redis = require("redis-sharelatex")
  3. rclient = redis.createClient(Settings.redis.history)
  4. Keys = Settings.redis.history.key_schema
  5. async = require "async"
  6. module.exports = RedisManager =
  7. getOldestDocUpdates: (doc_id, batchSize, callback = (error, jsonUpdates) ->) ->
  8. key = Keys.uncompressedHistoryOps({doc_id})
  9. rclient.lrange key, 0, batchSize - 1, callback
  10. expandDocUpdates: (jsonUpdates, callback = (error, rawUpdates) ->) ->
  11. try
  12. rawUpdates = ( JSON.parse(update) for update in jsonUpdates or [] )
  13. catch e
  14. return callback(e)
  15. callback null, rawUpdates
  16. deleteAppliedDocUpdates: (project_id, doc_id, docUpdates, callback = (error) ->) ->
  17. multi = rclient.multi()
  18. # Delete all the updates which have been applied (exact match)
  19. for update in docUpdates or []
  20. multi.lrem Keys.uncompressedHistoryOps({doc_id}), 1, update
  21. multi.exec (error, results) ->
  22. return callback(error) if error?
  23. # It's ok to delete the doc_id from the set here. Even though the list
  24. # of updates may not be empty, we will continue to process it until it is.
  25. rclient.srem Keys.docsWithHistoryOps({project_id}), doc_id, (error) ->
  26. return callback(error) if error?
  27. callback null
  28. getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) ->
  29. rclient.smembers Keys.docsWithHistoryOps({project_id}), callback
  30. # iterate over keys asynchronously using redis scan (non-blocking)
  31. # handle all the cluster nodes or single redis server
  32. _getKeys: (pattern, callback) ->
  33. nodes = rclient.nodes?('master') || [ rclient ];
  34. doKeyLookupForNode = (node, cb) ->
  35. RedisManager._getKeysFromNode node, pattern, cb
  36. async.concatSeries nodes, doKeyLookupForNode, callback
  37. _getKeysFromNode: (node, pattern, callback) ->
  38. cursor = 0 # redis iterator
  39. keySet = {} # use hash to avoid duplicate results
  40. # scan over all keys looking for pattern
  41. doIteration = (cb) ->
  42. node.scan cursor, "MATCH", pattern, "COUNT", 1000, (error, reply) ->
  43. return callback(error) if error?
  44. [cursor, keys] = reply
  45. for key in keys
  46. keySet[key] = true
  47. if cursor == '0' # note redis returns string result not numeric
  48. return callback(null, Object.keys(keySet))
  49. else
  50. doIteration()
  51. doIteration()
  52. # extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b
  53. # or DocsWithHistoryOps:{57fd0b1f53a8396d22b2c24b} (for redis cluster)
  54. _extractIds: (keyList) ->
  55. ids = for key in keyList
  56. m = key.match(/:\{?([0-9a-f]{24})\}?/) # extract object id
  57. m[1]
  58. return ids
  59. getProjectIdsWithHistoryOps: (callback = (error, project_ids) ->) ->
  60. RedisManager._getKeys Keys.docsWithHistoryOps({project_id:"*"}), (error, project_keys) ->
  61. return callback(error) if error?
  62. project_ids = RedisManager._extractIds project_keys
  63. callback(error, project_ids)
  64. getAllDocIdsWithHistoryOps: (callback = (error, doc_ids) ->) ->
  65. # return all the docids, to find dangling history entries after
  66. # everything is flushed.
  67. RedisManager._getKeys Keys.uncompressedHistoryOps({doc_id:"*"}), (error, doc_keys) ->
  68. return callback(error) if error?
  69. doc_ids = RedisManager._extractIds doc_keys
  70. callback(error, doc_ids)