| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- /* eslint-disable
- camelcase,
- */
- const request = require('request')
- const _ = require('underscore')
- const logger = require('logger-sharelatex')
- const settings = require('settings-sharelatex')
- const metrics = require('metrics-sharelatex')
- const rclient = require('redis-sharelatex').createClient(
- settings.redis.documentupdater
- )
- const Keys = settings.redis.documentupdater.key_schema
- const DocumentUpdaterManager = {
- getDocument(project_id, doc_id, fromVersion, callback) {
- const timer = new metrics.Timer('get-document')
- const url = `${settings.apis.documentupdater.url}/project/${project_id}/doc/${doc_id}?fromVersion=${fromVersion}`
- logger.log(
- { project_id, doc_id, fromVersion },
- 'getting doc from document updater'
- )
- request.get(url, function (err, res, body) {
- timer.done()
- if (err) {
- logger.error(
- { err, url, project_id, doc_id },
- 'error getting doc from doc updater'
- )
- return callback(err)
- }
- if (res.statusCode >= 200 && res.statusCode < 300) {
- logger.log(
- { project_id, doc_id },
- 'got doc from document document updater'
- )
- try {
- body = JSON.parse(body)
- } catch (error) {
- return callback(error)
- }
- body = body || {}
- callback(null, body.lines, body.version, body.ranges, body.ops)
- } else if ([404, 422].includes(res.statusCode)) {
- err = new Error('doc updater could not load requested ops')
- err.statusCode = res.statusCode
- logger.warn(
- { err, project_id, doc_id, url, fromVersion },
- 'doc updater could not load requested ops'
- )
- callback(err)
- } else {
- err = new Error(
- `doc updater returned a non-success status code: ${res.statusCode}`
- )
- err.statusCode = res.statusCode
- logger.error(
- { err, project_id, doc_id, url },
- `doc updater returned a non-success status code: ${res.statusCode}`
- )
- callback(err)
- }
- })
- },
- checkDocument(project_id, doc_id, callback) {
- // in this call fromVersion = -1 means get document without docOps
- DocumentUpdaterManager.getDocument(project_id, doc_id, -1, callback)
- },
- flushProjectToMongoAndDelete(project_id, callback) {
- // this method is called when the last connected user leaves the project
- logger.log({ project_id }, 'deleting project from document updater')
- const timer = new metrics.Timer('delete.mongo.project')
- // flush the project in the background when all users have left
- const url =
- `${settings.apis.documentupdater.url}/project/${project_id}?background=true` +
- (settings.shutDownInProgress ? '&shutdown=true' : '')
- request.del(url, function (err, res) {
- timer.done()
- if (err) {
- logger.error(
- { err, project_id },
- 'error deleting project from document updater'
- )
- callback(err)
- } else if (res.statusCode >= 200 && res.statusCode < 300) {
- logger.log({ project_id }, 'deleted project from document updater')
- callback(null)
- } else {
- err = new Error(
- `document updater returned a failure status code: ${res.statusCode}`
- )
- err.statusCode = res.statusCode
- logger.error(
- { err, project_id },
- `document updater returned failure status code: ${res.statusCode}`
- )
- callback(err)
- }
- })
- },
- queueChange(project_id, doc_id, change, callback) {
- const allowedKeys = [
- 'doc',
- 'op',
- 'v',
- 'dupIfSource',
- 'meta',
- 'lastV',
- 'hash'
- ]
- change = _.pick(change, allowedKeys)
- const jsonChange = JSON.stringify(change)
- if (jsonChange.indexOf('\u0000') !== -1) {
- // memory corruption check
- const error = new Error('null bytes found in op')
- logger.error(
- { err: error, project_id, doc_id, jsonChange },
- error.message
- )
- return callback(error)
- }
- const updateSize = jsonChange.length
- if (updateSize > settings.maxUpdateSize) {
- const error = new Error('update is too large')
- error.updateSize = updateSize
- return callback(error)
- }
- // record metric for each update added to queue
- metrics.summary('redis.pendingUpdates', updateSize, { status: 'push' })
- const doc_key = `${project_id}:${doc_id}`
- // Push onto pendingUpdates for doc_id first, because once the doc updater
- // gets an entry on pending-updates-list, it starts processing.
- rclient.rpush(Keys.pendingUpdates({ doc_id }), jsonChange, function (
- error
- ) {
- if (error) {
- return callback(error)
- }
- rclient.rpush('pending-updates-list', doc_key, callback)
- })
- }
- }
- module.exports = DocumentUpdaterManager
|