mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 20:43:41 -05:00
Merge pull request #16368 from overleaf/em-remove-project-archiver-code
Remove project-archiver related code GitOrigin-RevId: b3331033658c14a7c4d8745fd7085cd7c65c94bc
This commit is contained in:
parent
4a8a811cc1
commit
b0f3f00c43
5 changed files with 0 additions and 204 deletions
|
@ -17,7 +17,6 @@ const DocstoreManager = require('../Docstore/DocstoreManager')
|
||||||
const EditorRealTimeController = require('../Editor/EditorRealTimeController')
|
const EditorRealTimeController = require('../Editor/EditorRealTimeController')
|
||||||
const HistoryManager = require('../History/HistoryManager')
|
const HistoryManager = require('../History/HistoryManager')
|
||||||
const FilestoreHandler = require('../FileStore/FileStoreHandler')
|
const FilestoreHandler = require('../FileStore/FileStoreHandler')
|
||||||
const TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')
|
|
||||||
const ChatApiHandler = require('../Chat/ChatApiHandler')
|
const ChatApiHandler = require('../Chat/ChatApiHandler')
|
||||||
const moment = require('moment')
|
const moment = require('moment')
|
||||||
const { promiseMapWithLimit } = require('@overleaf/promise-utils')
|
const { promiseMapWithLimit } = require('@overleaf/promise-utils')
|
||||||
|
@ -382,9 +381,6 @@ async function expireDeletedProject(projectId) {
|
||||||
historyId
|
historyId
|
||||||
),
|
),
|
||||||
FilestoreHandler.promises.deleteProject(deletedProject.project._id),
|
FilestoreHandler.promises.deleteProject(deletedProject.project._id),
|
||||||
TpdsUpdateSender.promises.deleteProject({
|
|
||||||
projectId: deletedProject.project._id,
|
|
||||||
}),
|
|
||||||
ChatApiHandler.promises.destroyProject(deletedProject.project._id),
|
ChatApiHandler.promises.destroyProject(deletedProject.project._id),
|
||||||
hardDeleteDeletedFiles(deletedProject.project._id),
|
hardDeleteDeletedFiles(deletedProject.project._id),
|
||||||
ProjectAuditLogEntry.deleteMany({ projectId }),
|
ProjectAuditLogEntry.deleteMany({ projectId }),
|
||||||
|
|
|
@ -169,35 +169,6 @@ async function createProject(params) {
|
||||||
await enqueue(userId, 'standardHttpRequest', job)
|
await enqueue(userId, 'standardHttpRequest', job)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteProject(params) {
|
|
||||||
const { projectId } = params
|
|
||||||
// deletion only applies to project archiver
|
|
||||||
const projectArchiverUrl = _.get(settings, [
|
|
||||||
'apis',
|
|
||||||
'project_archiver',
|
|
||||||
'url',
|
|
||||||
])
|
|
||||||
// silently do nothing if project archiver url is not in settings
|
|
||||||
if (!projectArchiverUrl) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
metrics.inc('tpds.delete-project')
|
|
||||||
// send the request directly to project archiver, bypassing third-party-datastore
|
|
||||||
try {
|
|
||||||
await fetchNothing(
|
|
||||||
`${settings.apis.project_archiver.url}/project/${projectId}`,
|
|
||||||
{ method: 'DELETE' }
|
|
||||||
)
|
|
||||||
return true
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(
|
|
||||||
{ err, projectId },
|
|
||||||
'error deleting project in third party datastore (project_archiver)'
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function enqueue(group, method, job) {
|
async function enqueue(group, method, job) {
|
||||||
const tpdsWorkerUrl = _.get(settings, ['apis', 'tpdsworker', 'url'])
|
const tpdsWorkerUrl = _.get(settings, ['apis', 'tpdsworker', 'url'])
|
||||||
// silently do nothing if worker url is not in settings
|
// silently do nothing if worker url is not in settings
|
||||||
|
@ -296,7 +267,6 @@ const TpdsUpdateSender = {
|
||||||
addFile: callbackify(addFile),
|
addFile: callbackify(addFile),
|
||||||
deleteEntity: callbackify(deleteEntity),
|
deleteEntity: callbackify(deleteEntity),
|
||||||
createProject: callbackify(createProject),
|
createProject: callbackify(createProject),
|
||||||
deleteProject: callbackify(deleteProject),
|
|
||||||
enqueue: callbackify(enqueue),
|
enqueue: callbackify(enqueue),
|
||||||
moveEntity: callbackify(moveEntity),
|
moveEntity: callbackify(moveEntity),
|
||||||
pollDropboxForUser: callbackify(pollDropboxForUser),
|
pollDropboxForUser: callbackify(pollDropboxForUser),
|
||||||
|
@ -306,7 +276,6 @@ const TpdsUpdateSender = {
|
||||||
addFile,
|
addFile,
|
||||||
deleteEntity,
|
deleteEntity,
|
||||||
createProject,
|
createProject,
|
||||||
deleteProject,
|
|
||||||
enqueue,
|
enqueue,
|
||||||
moveEntity,
|
moveEntity,
|
||||||
pollDropboxForUser,
|
pollDropboxForUser,
|
||||||
|
|
|
@ -1,134 +0,0 @@
|
||||||
const Settings = require('@overleaf/settings')
|
|
||||||
const { fetchJson } = require('@overleaf/fetch-utils')
|
|
||||||
const { waitForDb } = require('../app/src/infrastructure/mongodb')
|
|
||||||
const { promiseMapWithLimit } = require('@overleaf/promise-utils')
|
|
||||||
const { getHardDeletedProjectIds } = require('./delete_orphaned_data_helper')
|
|
||||||
const TpdsUpdateSender = require('../app/src/Features/ThirdPartyDataStore/TpdsUpdateSender')
|
|
||||||
const { promisify } = require('util')
|
|
||||||
const { ObjectId } = require('mongodb')
|
|
||||||
const sleep = promisify(setTimeout)
|
|
||||||
|
|
||||||
const START_OFFSET = process.env.START_OFFSET
|
|
||||||
|
|
||||||
const BATCH_SIZE = parseInt(process.env.BATCH_SIZE, 10) || 1000
|
|
||||||
const DRY_RUN = process.env.DRY_RUN !== 'false'
|
|
||||||
const READ_CONCURRENCY_SECONDARY =
|
|
||||||
parseInt(process.env.READ_CONCURRENCY_SECONDARY, 10) || 1000
|
|
||||||
const READ_CONCURRENCY_PRIMARY =
|
|
||||||
parseInt(process.env.READ_CONCURRENCY_PRIMARY, 10) || 500
|
|
||||||
const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY, 10) || 10
|
|
||||||
|
|
||||||
const LET_USER_DOUBLE_CHECK_INPUTS_FOR =
|
|
||||||
parseInt(process.env.LET_USER_DOUBLE_CHECK_INPUTS_FOR, 10) || 10 * 1000
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
await letUserDoubleCheckInputs()
|
|
||||||
await waitForDb()
|
|
||||||
|
|
||||||
let processed = 0
|
|
||||||
let hardDeleted = 0
|
|
||||||
let pageToken = ''
|
|
||||||
let startOffset = START_OFFSET
|
|
||||||
while (pageToken !== undefined) {
|
|
||||||
const url = new URL(`${Settings.apis.project_archiver.url}/project/list`)
|
|
||||||
url.searchParams.append('pageToken', pageToken)
|
|
||||||
url.searchParams.append('startOffset', startOffset)
|
|
||||||
const { nextPageToken, entries } = await fetchJson(url)
|
|
||||||
pageToken = nextPageToken
|
|
||||||
startOffset = undefined
|
|
||||||
|
|
||||||
hardDeleted += await processBatch(entries)
|
|
||||||
processed += entries.length
|
|
||||||
console.log(
|
|
||||||
'processed:',
|
|
||||||
processed.toString().padStart(10, '0'),
|
|
||||||
'hard deleted:',
|
|
||||||
hardDeleted.toString().padStart(10, '0'),
|
|
||||||
'nextPageToken:',
|
|
||||||
nextPageToken,
|
|
||||||
'START_OFFSET:',
|
|
||||||
entries.pop()?.prefix
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processBatch(entries) {
|
|
||||||
const projectIdToPrefix = new Map()
|
|
||||||
for (const { prefix, projectId } of entries) {
|
|
||||||
const prefixes = projectIdToPrefix.get(projectId) || []
|
|
||||||
prefixes.push(prefix)
|
|
||||||
projectIdToPrefix.set(projectId, prefixes)
|
|
||||||
}
|
|
||||||
const projectIds = Array.from(projectIdToPrefix.keys()).map(
|
|
||||||
id => new ObjectId(id)
|
|
||||||
)
|
|
||||||
const projectsWithOrphanedArchive = await getHardDeletedProjectIds({
|
|
||||||
projectIds,
|
|
||||||
READ_CONCURRENCY_PRIMARY,
|
|
||||||
READ_CONCURRENCY_SECONDARY,
|
|
||||||
})
|
|
||||||
|
|
||||||
await promiseMapWithLimit(
|
|
||||||
WRITE_CONCURRENCY,
|
|
||||||
projectsWithOrphanedArchive.flatMap(id =>
|
|
||||||
projectIdToPrefix.get(id.toString())
|
|
||||||
),
|
|
||||||
hardDeleteProjectArchiverData
|
|
||||||
)
|
|
||||||
return projectsWithOrphanedArchive.length
|
|
||||||
}
|
|
||||||
|
|
||||||
async function hardDeleteProjectArchiverData(prefix) {
|
|
||||||
console.log(`Destroying hard deleted project archive at '${prefix}/'`)
|
|
||||||
if (DRY_RUN) return
|
|
||||||
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
await sleep(1000 * i)
|
|
||||||
try {
|
|
||||||
const ok = await TpdsUpdateSender.promises.deleteProject({
|
|
||||||
projectId: encodeURIComponent(prefix),
|
|
||||||
})
|
|
||||||
if (ok) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`deletion failed for '${prefix}/'`, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error(`deletion failed for '${prefix}/', check logs`)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function letUserDoubleCheckInputs() {
|
|
||||||
console.error(
|
|
||||||
'Options:',
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
BATCH_SIZE,
|
|
||||||
DRY_RUN,
|
|
||||||
LET_USER_DOUBLE_CHECK_INPUTS_FOR,
|
|
||||||
READ_CONCURRENCY_SECONDARY,
|
|
||||||
READ_CONCURRENCY_PRIMARY,
|
|
||||||
START_OFFSET,
|
|
||||||
WRITE_CONCURRENCY,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
console.error(
|
|
||||||
'Waiting for you to double check inputs for',
|
|
||||||
LET_USER_DOUBLE_CHECK_INPUTS_FOR,
|
|
||||||
'ms'
|
|
||||||
)
|
|
||||||
await sleep(LET_USER_DOUBLE_CHECK_INPUTS_FOR)
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
||||||
.then(() => {
|
|
||||||
console.log('Done.')
|
|
||||||
process.exit(0)
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error({ error })
|
|
||||||
process.exit(1)
|
|
||||||
})
|
|
|
@ -127,11 +127,6 @@ describe('ProjectDeleter', function () {
|
||||||
deleteProject: sinon.stub().resolves(),
|
deleteProject: sinon.stub().resolves(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
this.TpdsUpdateSender = {
|
|
||||||
promises: {
|
|
||||||
deleteProject: sinon.stub().resolves(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
this.Features = {
|
this.Features = {
|
||||||
hasFeature: sinon.stub().returns(true),
|
hasFeature: sinon.stub().returns(true),
|
||||||
}
|
}
|
||||||
|
@ -154,7 +149,6 @@ describe('ProjectDeleter', function () {
|
||||||
this.DocumentUpdaterHandler,
|
this.DocumentUpdaterHandler,
|
||||||
'../Tags/TagsHandler': this.TagsHandler,
|
'../Tags/TagsHandler': this.TagsHandler,
|
||||||
'../FileStore/FileStoreHandler': this.FileStoreHandler,
|
'../FileStore/FileStoreHandler': this.FileStoreHandler,
|
||||||
'../ThirdPartyDataStore/TpdsUpdateSender': this.TpdsUpdateSender,
|
|
||||||
'../Chat/ChatApiHandler': this.ChatApiHandler,
|
'../Chat/ChatApiHandler': this.ChatApiHandler,
|
||||||
'../Collaborators/CollaboratorsHandler': this.CollaboratorsHandler,
|
'../Collaborators/CollaboratorsHandler': this.CollaboratorsHandler,
|
||||||
'../Collaborators/CollaboratorsGetter': this.CollaboratorsGetter,
|
'../Collaborators/CollaboratorsGetter': this.CollaboratorsGetter,
|
||||||
|
@ -496,14 +490,6 @@ describe('ProjectDeleter', function () {
|
||||||
).to.have.been.calledWith(this.deletedProjects[0].project._id)
|
).to.have.been.calledWith(this.deletedProjects[0].project._id)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should destroy the files in project-archiver', function () {
|
|
||||||
expect(
|
|
||||||
this.TpdsUpdateSender.promises.deleteProject
|
|
||||||
).to.have.been.calledWith({
|
|
||||||
projectId: this.deletedProjects[0].project._id,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should destroy the chat threads and messages', function () {
|
it('should destroy the chat threads and messages', function () {
|
||||||
expect(
|
expect(
|
||||||
this.ChatApiHandler.promises.destroyProject
|
this.ChatApiHandler.promises.destroyProject
|
||||||
|
@ -553,11 +539,6 @@ describe('ProjectDeleter', function () {
|
||||||
.called
|
.called
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not destroy the files in project-archiver', function () {
|
|
||||||
expect(this.TpdsUpdateSender.promises.deleteProject).to.not.have.been
|
|
||||||
.called
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not destroy the chat threads and messages', function () {
|
it('should not destroy the chat threads and messages', function () {
|
||||||
expect(this.ChatApiHandler.promises.destroyProject).to.not.have.been
|
expect(this.ChatApiHandler.promises.destroyProject).to.not.have.been
|
||||||
.called
|
.called
|
||||||
|
|
|
@ -18,7 +18,6 @@ const projectName = 'project_name_here'
|
||||||
const thirdPartyDataStoreApiUrl = 'http://third-party-json-store.herokuapp.com'
|
const thirdPartyDataStoreApiUrl = 'http://third-party-json-store.herokuapp.com'
|
||||||
const siteUrl = 'http://www.localhost:3000'
|
const siteUrl = 'http://www.localhost:3000'
|
||||||
const filestoreUrl = 'filestore.sharelatex.com'
|
const filestoreUrl = 'filestore.sharelatex.com'
|
||||||
const projectArchiverUrl = 'project-archiver.overleaf.com'
|
|
||||||
|
|
||||||
describe('TpdsUpdateSender', function () {
|
describe('TpdsUpdateSender', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
@ -440,21 +439,6 @@ describe('TpdsUpdateSender', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('deleteProject', function () {
|
|
||||||
it('should not call request if there is no project archiver url', async function () {
|
|
||||||
await this.TpdsUpdateSender.promises.deleteProject({ projectId })
|
|
||||||
this.FetchUtils.fetchNothing.should.not.have.been.called
|
|
||||||
})
|
|
||||||
it('should make a delete request to project archiver', async function () {
|
|
||||||
this.settings.apis.project_archiver = { url: projectArchiverUrl }
|
|
||||||
await this.TpdsUpdateSender.promises.deleteProject({ projectId })
|
|
||||||
this.FetchUtils.fetchNothing.should.have.been.calledWith(
|
|
||||||
`${projectArchiverUrl}/project/${projectId}`,
|
|
||||||
{ method: 'DELETE' }
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('user not linked to dropbox', function () {
|
describe('user not linked to dropbox', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.UserGetter.promises.getUsers
|
this.UserGetter.promises.getUsers
|
||||||
|
|
Loading…
Reference in a new issue