2014-11-12 15:54:55 +00:00
|
|
|
request = require "request"
|
2019-05-24 09:19:02 +00:00
|
|
|
_ = require "underscore"
|
2014-11-12 15:54:55 +00:00
|
|
|
logger = require "logger-sharelatex"
|
|
|
|
settings = require "settings-sharelatex"
|
2015-08-29 07:26:01 +00:00
|
|
|
metrics = require("metrics-sharelatex")
|
2014-11-12 15:54:55 +00:00
|
|
|
|
2017-05-02 14:51:17 +00:00
|
|
|
rclient = require("redis-sharelatex").createClient(settings.redis.documentupdater)
|
|
|
|
Keys = settings.redis.documentupdater.key_schema
|
2014-11-13 17:07:05 +00:00
|
|
|
|
2014-11-12 15:54:55 +00:00
|
|
|
module.exports = DocumentUpdaterManager =
|
|
|
|
getDocument: (project_id, doc_id, fromVersion, callback = (error, exists, doclines, version) ->) ->
|
2015-08-29 07:22:43 +00:00
|
|
|
timer = new metrics.Timer("get-document")
|
2014-11-12 15:54:55 +00:00
|
|
|
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, (err, res, body) ->
|
2015-08-29 07:22:43 +00:00
|
|
|
timer.done()
|
2014-11-12 15:54:55 +00:00
|
|
|
if err?
|
|
|
|
logger.error {err, url, project_id, doc_id}, "error getting doc from doc updater"
|
|
|
|
return callback(err)
|
|
|
|
if 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)
|
2016-12-08 11:37:31 +00:00
|
|
|
callback null, body?.lines, body?.version, body?.ranges, body?.ops
|
2019-05-31 21:27:05 +00:00
|
|
|
else if res.statusCode in [404, 422]
|
2016-05-31 13:21:23 +00:00
|
|
|
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
|
2014-11-12 15:54:55 +00:00
|
|
|
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}"
|
2014-11-13 17:07:05 +00:00
|
|
|
callback err
|
2014-11-14 15:53:59 +00:00
|
|
|
|
|
|
|
flushProjectToMongoAndDelete: (project_id, callback = ()->) ->
|
2019-06-03 08:42:12 +00:00
|
|
|
# this method is called when the last connected user leaves the project
|
2014-11-14 15:53:59 +00:00
|
|
|
logger.log project_id:project_id, "deleting project from document updater"
|
2015-08-29 07:26:01 +00:00
|
|
|
timer = new metrics.Timer("delete.mongo.project")
|
2019-06-03 08:42:12 +00:00
|
|
|
# flush the project in the background when all users have left
|
2019-08-13 16:36:53 +00:00
|
|
|
url = "#{settings.apis.documentupdater.url}/project/#{project_id}?background=true" +
|
|
|
|
(if settings.shutDownInProgress then "&shutdown=true" else "")
|
2014-11-14 15:53:59 +00:00
|
|
|
request.del url, (err, res, body)->
|
2015-08-29 07:26:01 +00:00
|
|
|
timer.done()
|
2014-11-14 15:53:59 +00:00
|
|
|
if err?
|
|
|
|
logger.error {err, project_id}, "error deleting project from document updater"
|
|
|
|
return callback(err)
|
|
|
|
else if 200 <= res.statusCode < 300
|
|
|
|
logger.log {project_id}, "deleted project from document updater"
|
|
|
|
return 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}"
|
|
|
|
return callback(err)
|
2019-05-31 21:27:05 +00:00
|
|
|
|
2014-11-13 17:07:05 +00:00
|
|
|
queueChange: (project_id, doc_id, change, callback = ()->)->
|
2019-05-24 09:19:02 +00:00
|
|
|
allowedKeys = [ 'doc', 'op', 'v', 'dupIfSource', 'meta', 'lastV', 'hash']
|
|
|
|
change = _.pick change, allowedKeys
|
2014-11-13 17:07:05 +00:00
|
|
|
jsonChange = JSON.stringify change
|
2017-06-01 10:27:56 +00:00
|
|
|
if jsonChange.indexOf("\u0000") != -1
|
|
|
|
error = new Error("null bytes found in op")
|
|
|
|
logger.error err: error, project_id: project_id, doc_id: doc_id, jsonChange: jsonChange, error.message
|
|
|
|
return callback(error)
|
2014-11-13 17:07:05 +00:00
|
|
|
doc_key = "#{project_id}:#{doc_id}"
|
2017-05-11 16:27:28 +00:00
|
|
|
# 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, (error) ->
|
2014-11-13 17:07:05 +00:00
|
|
|
return callback(error) if error?
|
2017-05-11 16:27:28 +00:00
|
|
|
rclient.rpush "pending-updates-list", doc_key, callback
|