From 19ce460d733c9f3f3b7a97671ee40580fd2b6b8c Mon Sep 17 00:00:00 2001 From: June Kelly Date: Fri, 8 Jul 2022 10:20:30 +0100 Subject: [PATCH] Merge pull request #8787 from overleaf/msm-force-doc-flush [web] script to force flushing docs to mongo GitOrigin-RevId: 6ce3c1e456b92610fe4f90993b373db2070ab392 --- .../DocumentUpdater/DocumentUpdaterHandler.js | 15 +++- services/web/scripts/force_doc_flush.js | 74 +++++++++++++++++++ .../DocumentUpdaterHandlerTests.js | 36 +++++++++ 3 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 services/web/scripts/force_doc_flush.js diff --git a/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js b/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js index ffd1805726..07edc68bf1 100644 --- a/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js +++ b/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js @@ -81,11 +81,20 @@ function flushDocToMongo(projectId, docId, callback) { ) } -function deleteDoc(projectId, docId, callback) { +function deleteDoc(projectId, docId, ignoreFlushErrors, callback) { + if (typeof ignoreFlushErrors === 'function') { + callback = ignoreFlushErrors + ignoreFlushErrors = false + } + let path = `/project/${projectId}/doc/${docId}` + if (ignoreFlushErrors) { + path += '?ignore_flush_errors=true' + } + const method = 'DELETE' _makeRequest( { - path: `/project/${projectId}/doc/${docId}`, - method: 'DELETE', + path, + method, }, projectId, 'delete.mongo.doc', diff --git a/services/web/scripts/force_doc_flush.js b/services/web/scripts/force_doc_flush.js new file mode 100644 index 0000000000..27be7d32e4 --- /dev/null +++ b/services/web/scripts/force_doc_flush.js @@ -0,0 +1,74 @@ +const { ObjectId } = require('mongodb') +const { db, waitForDb } = require('../app/src/infrastructure/mongodb') +const DocumentUpdaterHandler = require('../app/src/Features/DocumentUpdater/DocumentUpdaterHandler') + +const PROJECT_ID = process.env.PROJECT_ID +const DOC_ID = process.env.DOC_ID +const VERBOSE_LOGGING = process.env.VERBOSE_LOGGING === 'true' +const DRY_RUN = process.env.DRY_RUN !== 'false' + +console.log({ + PROJECT_ID, + DOC_ID, + VERBOSE_LOGGING, + DRY_RUN, +}) + +async function main() { + await waitForDb() + + const { lines, version, ranges } = await getDocument() + const size = lines.reduce((size, line) => size + line.length + 1, 0) + + console.log('doc stats:', { + lineCount: lines.length, + size, + version, + }) + if (!DRY_RUN) { + console.log(`updating doc ${DOC_ID} in mongo for project ${PROJECT_ID}`) + const { result } = await db.docs.updateOne( + { _id: ObjectId(DOC_ID), project_id: ObjectId(PROJECT_ID) }, + { + $set: { lines, version, ranges }, + $inc: { rev: 1 }, // maintain same behaviour as Docstore upsertIntoDocCollection + $unset: { + inS3: true, + }, + } + ) + console.log('mongo result', result) + if (result.n !== 1 || result.nModified !== 1 || result.ok !== 1) { + throw new Error('unexpected result from mongo update') + } + console.log(`deleting doc ${DOC_ID} from redis for project ${PROJECT_ID}`) + await DocumentUpdaterHandler.promises.deleteDoc(PROJECT_ID, DOC_ID, true) + } +} + +function getDocument() { + return new Promise((resolve, reject) => { + DocumentUpdaterHandler.getDocument( + PROJECT_ID, + DOC_ID, + -1, + (error, lines, version, ranges) => { + if (error) { + reject(error) + } else { + resolve({ lines, version, ranges }) + } + } + ) + }) +} + +main() + .then(() => { + console.error('Done.') + process.exit(0) + }) + .catch(error => { + console.error({ error }) + process.exit(1) + }) diff --git a/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js b/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js index 7e90a52ee3..c54c8bc24a 100644 --- a/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js +++ b/services/web/test/unit/src/DocumentUpdater/DocumentUpdaterHandlerTests.js @@ -320,6 +320,42 @@ describe('DocumentUpdaterHandler', function () { .should.equal(true) }) }) + + describe("with 'ignoreFlushErrors' option", function () { + beforeEach(function () { + this.request.callsArgWith(1, null, { statusCode: 204 }, '') + }) + + it('when option is true, should send a `ignore_flush_errors=true` URL query to document-updater', function () { + this.handler.deleteDoc( + this.project_id, + this.doc_id, + true, + this.callback + ) + this.request + .calledWithMatch({ + url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}?ignore_flush_errors=true`, + method: 'DELETE', + }) + .should.equal(true) + }) + + it("when option is false, shouldn't send any URL query to document-updater", function () { + this.handler.deleteDoc( + this.project_id, + this.doc_id, + false, + this.callback + ) + this.request + .calledWithMatch({ + url: `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}`, + method: 'DELETE', + }) + .should.equal(true) + }) + }) }) describe('setDocument', function () {