flush_track_changes.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // Flush docs found in docsWithHistory sets in the project-history redis.
  2. // We normally write docsWithHistory in the main redis, but some keys
  3. // got into the project-history redis.
  4. // Usage: DRY_RUN=false node scripts/flush_track_changes.js
  5. const Settings = require('@overleaf/settings')
  6. const logger = require('@overleaf/logger')
  7. const rclientProjectHistory = require('@overleaf/redis-wrapper').createClient(
  8. Settings.redis.history
  9. )
  10. const ProjectFlusher = require('../app/js/ProjectFlusher')
  11. const keys = Settings.redis.history.key_schema
  12. const request = require('request')
  13. const util = require('util')
  14. async function findAndProcessDocs(options) {
  15. logger.info({ options }, 'flushing docs with pending track-changes')
  16. let cursor = 0
  17. do {
  18. const [newCursor, projectKeys] = await rclientProjectHistory.scan(
  19. cursor,
  20. 'MATCH',
  21. keys.docsWithHistoryOps({ project_id: '*' }),
  22. 'COUNT',
  23. options.limit
  24. )
  25. await flushProjects(projectKeys, options)
  26. cursor = newCursor
  27. } while (cursor !== '0')
  28. }
  29. async function flushProjects(projectKeys, options) {
  30. const projectIds = ProjectFlusher._extractIds(projectKeys)
  31. for (const projectId of projectIds) {
  32. const docIds = await rclientProjectHistory.smembers(
  33. keys.docsWithHistoryOps({ project_id: projectId })
  34. )
  35. logger.debug({ projectId, docIds }, 'checking docs')
  36. if (docIds) {
  37. for (const docId of docIds) {
  38. try {
  39. await flushDoc(projectId, docId, options)
  40. } catch (err) {
  41. logger.error({ projectId, docId, err }, 'error flushing doc')
  42. }
  43. }
  44. }
  45. }
  46. }
  47. function makeRequest(url, callback) {
  48. request.post(url, function (err, res, body) {
  49. if (err) {
  50. return callback(err)
  51. } else {
  52. return callback(err, { res, body })
  53. }
  54. })
  55. }
  56. const makeRequestPromise = util.promisify(makeRequest)
  57. async function flushDoc(projectId, docId, options) {
  58. const url = `${Settings.apis.trackchanges.url}/project/${projectId}/doc/${docId}/flush`
  59. logger.debug(
  60. { projectId, docId, url, options },
  61. 'flushing doc in track changes api'
  62. )
  63. if (options.dryRun) {
  64. return
  65. }
  66. const { res, body } = await makeRequestPromise(url)
  67. if (res && res.statusCode === 204) {
  68. logger.debug(
  69. { projectId, docId, statusCode: res.statusCode },
  70. 'successful flush in track changes api'
  71. )
  72. // on a successful flush the doc id is removed from docsWithHistoryOps by track-changes
  73. // in deleteAppliedDocUpdates BUT it will do it for the main redis, not the project history redis.
  74. // So we need to remove the docId here:
  75. await rclientProjectHistory.srem(
  76. keys.docsWithHistoryOps({ project_id: projectId }),
  77. docId
  78. )
  79. } else {
  80. logger.debug(
  81. { projectId, docId, res, body },
  82. 'problem flushing in track changes api'
  83. )
  84. }
  85. }
  86. findAndProcessDocs({ limit: 1000, dryRun: process.env.DRY_RUN !== 'false' })
  87. .then(result => {
  88. rclientProjectHistory.quit()
  89. console.log('DONE')
  90. })
  91. .catch(function (error) {
  92. console.error(error)
  93. process.exit(1)
  94. })