ProjectFlusher.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /* eslint-disable
  2. camelcase,
  3. no-unused-vars,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS101: Remove unnecessary use of Array.from
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * DS205: Consider reworking code to avoid use of IIFEs
  12. * DS207: Consider shorter variations of null checks
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. const request = require('request')
  16. const Settings = require('@overleaf/settings')
  17. const RedisManager = require('./RedisManager')
  18. const { rclient } = RedisManager
  19. const docUpdaterKeys = Settings.redis.documentupdater.key_schema
  20. const async = require('async')
  21. const ProjectManager = require('./ProjectManager')
  22. const _ = require('lodash')
  23. const logger = require('logger-sharelatex')
  24. var ProjectFlusher = {
  25. // iterate over keys asynchronously using redis scan (non-blocking)
  26. // handle all the cluster nodes or single redis server
  27. _getKeys(pattern, limit, callback) {
  28. const nodes = (typeof rclient.nodes === 'function'
  29. ? rclient.nodes('master')
  30. : undefined) || [rclient]
  31. const doKeyLookupForNode = (node, cb) =>
  32. ProjectFlusher._getKeysFromNode(node, pattern, limit, cb)
  33. return async.concatSeries(nodes, doKeyLookupForNode, callback)
  34. },
  35. _getKeysFromNode(node, pattern, limit, callback) {
  36. if (limit == null) {
  37. limit = 1000
  38. }
  39. let cursor = 0 // redis iterator
  40. const keySet = {} // use hash to avoid duplicate results
  41. const batchSize = limit != null ? Math.min(limit, 1000) : 1000
  42. // scan over all keys looking for pattern
  43. var doIteration = (
  44. cb // avoid hitting redis too hard
  45. ) =>
  46. node.scan(cursor, 'MATCH', pattern, 'COUNT', batchSize, function (
  47. error,
  48. reply
  49. ) {
  50. let keys
  51. if (error != null) {
  52. return callback(error)
  53. }
  54. ;[cursor, keys] = Array.from(reply)
  55. for (const key of Array.from(keys)) {
  56. keySet[key] = true
  57. }
  58. keys = Object.keys(keySet)
  59. const noResults = cursor === '0' // redis returns string results not numeric
  60. const limitReached = limit != null && keys.length >= limit
  61. if (noResults || limitReached) {
  62. return callback(null, keys)
  63. } else {
  64. return setTimeout(doIteration, 10)
  65. }
  66. })
  67. return doIteration()
  68. },
  69. // extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b
  70. // or docsInProject:{57fd0b1f53a8396d22b2c24b} (for redis cluster)
  71. _extractIds(keyList) {
  72. const ids = (() => {
  73. const result = []
  74. for (const key of Array.from(keyList)) {
  75. const m = key.match(/:\{?([0-9a-f]{24})\}?/) // extract object id
  76. result.push(m[1])
  77. }
  78. return result
  79. })()
  80. return ids
  81. },
  82. flushAllProjects(options, callback) {
  83. logger.log({ options }, 'flushing all projects')
  84. return ProjectFlusher._getKeys(
  85. docUpdaterKeys.docsInProject({ project_id: '*' }),
  86. options.limit,
  87. function (error, project_keys) {
  88. if (error != null) {
  89. logger.err({ err: error }, 'error getting keys for flushing')
  90. return callback(error)
  91. }
  92. const project_ids = ProjectFlusher._extractIds(project_keys)
  93. if (options.dryRun) {
  94. return callback(null, project_ids)
  95. }
  96. const jobs = _.map(project_ids, (project_id) => (cb) =>
  97. ProjectManager.flushAndDeleteProjectWithLocks(
  98. project_id,
  99. { background: true },
  100. cb
  101. )
  102. )
  103. return async.parallelLimit(
  104. async.reflectAll(jobs),
  105. options.concurrency,
  106. function (error, results) {
  107. const success = []
  108. const failure = []
  109. _.each(results, function (result, i) {
  110. if (result.error != null) {
  111. return failure.push(project_ids[i])
  112. } else {
  113. return success.push(project_ids[i])
  114. }
  115. })
  116. logger.log({ success, failure }, 'finished flushing all projects')
  117. return callback(error, { success, failure })
  118. }
  119. )
  120. }
  121. )
  122. }
  123. }
  124. module.exports = ProjectFlusher