ProjectFlusher.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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(
  47. cursor,
  48. 'MATCH',
  49. pattern,
  50. 'COUNT',
  51. batchSize,
  52. function (error, reply) {
  53. let keys
  54. if (error != null) {
  55. return callback(error)
  56. }
  57. ;[cursor, keys] = Array.from(reply)
  58. for (const key of Array.from(keys)) {
  59. keySet[key] = true
  60. }
  61. keys = Object.keys(keySet)
  62. const noResults = cursor === '0' // redis returns string results not numeric
  63. const limitReached = limit != null && keys.length >= limit
  64. if (noResults || limitReached) {
  65. return callback(null, keys)
  66. } else {
  67. return setTimeout(doIteration, 10)
  68. }
  69. }
  70. )
  71. return doIteration()
  72. },
  73. // extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b
  74. // or docsInProject:{57fd0b1f53a8396d22b2c24b} (for redis cluster)
  75. _extractIds(keyList) {
  76. const ids = (() => {
  77. const result = []
  78. for (const key of Array.from(keyList)) {
  79. const m = key.match(/:\{?([0-9a-f]{24})\}?/) // extract object id
  80. result.push(m[1])
  81. }
  82. return result
  83. })()
  84. return ids
  85. },
  86. flushAllProjects(options, callback) {
  87. logger.log({ options }, 'flushing all projects')
  88. return ProjectFlusher._getKeys(
  89. docUpdaterKeys.docsInProject({ project_id: '*' }),
  90. options.limit,
  91. function (error, project_keys) {
  92. if (error != null) {
  93. logger.err({ err: error }, 'error getting keys for flushing')
  94. return callback(error)
  95. }
  96. const project_ids = ProjectFlusher._extractIds(project_keys)
  97. if (options.dryRun) {
  98. return callback(null, project_ids)
  99. }
  100. const jobs = _.map(
  101. project_ids,
  102. project_id => cb =>
  103. ProjectManager.flushAndDeleteProjectWithLocks(
  104. project_id,
  105. { background: true },
  106. cb
  107. )
  108. )
  109. return async.parallelLimit(
  110. async.reflectAll(jobs),
  111. options.concurrency,
  112. function (error, results) {
  113. const success = []
  114. const failure = []
  115. _.each(results, function (result, i) {
  116. if (result.error != null) {
  117. return failure.push(project_ids[i])
  118. } else {
  119. return success.push(project_ids[i])
  120. }
  121. })
  122. logger.log({ success, failure }, 'finished flushing all projects')
  123. return callback(error, { success, failure })
  124. }
  125. )
  126. }
  127. )
  128. },
  129. }
  130. module.exports = ProjectFlusher