// Flush docs found in docsWithHistory sets in the project-history redis. // We normally write docsWithHistory in the main redis, but some keys // got into the project-history redis. // Usage: DRY_RUN=false node scripts/flush_track_changes.js const Settings = require('@overleaf/settings') const logger = require('@overleaf/logger') const rclientProjectHistory = require('@overleaf/redis-wrapper').createClient( Settings.redis.history ) const ProjectFlusher = require('../app/js/ProjectFlusher') const keys = Settings.redis.history.key_schema const request = require('request') const util = require('util') async function findAndProcessDocs(options) { logger.info({ options }, 'flushing docs with pending track-changes') let cursor = 0 do { const [newCursor, projectKeys] = await rclientProjectHistory.scan( cursor, 'MATCH', keys.docsWithHistoryOps({ project_id: '*' }), 'COUNT', options.limit ) await flushProjects(projectKeys, options) cursor = newCursor } while (cursor !== '0') } async function flushProjects(projectKeys, options) { const projectIds = ProjectFlusher._extractIds(projectKeys) for (const projectId of projectIds) { const docIds = await rclientProjectHistory.smembers( keys.docsWithHistoryOps({ project_id: projectId }) ) logger.debug({ projectId, docIds }, 'checking docs') if (docIds) { for (const docId of docIds) { try { await flushDoc(projectId, docId, options) } catch (err) { logger.error({ projectId, docId, err }, 'error flushing doc') } } } } } function makeRequest(url, callback) { request.post(url, function (err, res, body) { if (err) { return callback(err) } else { return callback(err, { res, body }) } }) } const makeRequestPromise = util.promisify(makeRequest) async function flushDoc(projectId, docId, options) { const url = `${Settings.apis.trackchanges.url}/project/${projectId}/doc/${docId}/flush` logger.debug( { projectId, docId, url, options }, 'flushing doc in track changes api' ) if (options.dryRun) { return } const { res, body } = await makeRequestPromise(url) if (res && res.statusCode === 204) { logger.debug( { projectId, docId, statusCode: res.statusCode }, 'successful flush in track changes api' ) // on a successful flush the doc id is removed from docsWithHistoryOps by track-changes // in deleteAppliedDocUpdates BUT it will do it for the main redis, not the project history redis. // So we need to remove the docId here: await rclientProjectHistory.srem( keys.docsWithHistoryOps({ project_id: projectId }), docId ) } else { logger.debug( { projectId, docId, res, body }, 'problem flushing in track changes api' ) } } findAndProcessDocs({ limit: 1000, dryRun: process.env.DRY_RUN !== 'false' }) .then(result => { rclientProjectHistory.quit() console.log('DONE') }) .catch(function (error) { console.error(error) process.exit(1) })