2014-02-12 05:40:42 -05:00
|
|
|
RedisManager = require "./RedisManager"
|
2018-03-09 09:14:14 -05:00
|
|
|
ProjectHistoryRedisManager = require "./ProjectHistoryRedisManager"
|
2014-02-12 05:40:42 -05:00
|
|
|
PersistenceManager = require "./PersistenceManager"
|
|
|
|
DiffCodec = require "./DiffCodec"
|
|
|
|
logger = require "logger-sharelatex"
|
|
|
|
Metrics = require "./Metrics"
|
2016-11-28 05:14:42 -05:00
|
|
|
HistoryManager = require "./HistoryManager"
|
2017-05-02 10:38:33 -04:00
|
|
|
RealTimeRedisManager = require "./RealTimeRedisManager"
|
2017-01-09 08:41:18 -05:00
|
|
|
Errors = require "./Errors"
|
|
|
|
RangesManager = require "./RangesManager"
|
2017-11-01 15:16:49 -04:00
|
|
|
async = require "async"
|
2014-02-12 05:40:42 -05:00
|
|
|
|
2017-10-06 07:23:23 -04:00
|
|
|
MAX_UNFLUSHED_AGE = 300 * 1000 # 5 mins, document should be flushed to mongo this time after a change
|
|
|
|
|
2014-02-12 05:40:42 -05:00
|
|
|
module.exports = DocumentManager =
|
2018-04-23 07:08:04 -04:00
|
|
|
getDoc: (project_id, doc_id, _callback = (error, lines, version, ranges, pathname, projectHistoryId, unflushedTime, alreadyLoaded) ->) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
timer = new Metrics.Timer("docManager.getDoc")
|
|
|
|
callback = (args...) ->
|
|
|
|
timer.done()
|
|
|
|
_callback(args...)
|
|
|
|
|
2018-04-23 07:08:04 -04:00
|
|
|
RedisManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId, unflushedTime) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
return callback(error) if error?
|
|
|
|
if !lines? or !version?
|
2016-12-02 06:04:21 -05:00
|
|
|
logger.log {project_id, doc_id}, "doc not in redis so getting from persistence API"
|
2019-11-15 11:53:16 -05:00
|
|
|
PersistenceManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId, projectHistoryType) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
return callback(error) if error?
|
2019-11-15 11:53:16 -05:00
|
|
|
logger.log {project_id, doc_id, lines, version, pathname, projectHistoryId, projectHistoryType}, "got doc from persistence API"
|
2018-04-23 07:08:04 -04:00
|
|
|
RedisManager.putDocInMemory project_id, doc_id, lines, version, ranges, pathname, projectHistoryId, (error) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
return callback(error) if error?
|
2019-11-15 11:53:16 -05:00
|
|
|
RedisManager.setHistoryType doc_id, projectHistoryType, (error) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
callback null, lines, version, ranges, pathname, projectHistoryId, null, false
|
2014-02-12 05:40:42 -05:00
|
|
|
else
|
2018-04-23 07:08:04 -04:00
|
|
|
callback null, lines, version, ranges, pathname, projectHistoryId, unflushedTime, true
|
2014-02-12 05:40:42 -05:00
|
|
|
|
2018-04-23 07:08:04 -04:00
|
|
|
getDocAndRecentOps: (project_id, doc_id, fromVersion, _callback = (error, lines, version, ops, ranges, pathname, projectHistoryId) ->) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
timer = new Metrics.Timer("docManager.getDocAndRecentOps")
|
|
|
|
callback = (args...) ->
|
|
|
|
timer.done()
|
|
|
|
_callback(args...)
|
2017-09-29 06:06:20 -04:00
|
|
|
|
2018-04-23 07:08:04 -04:00
|
|
|
DocumentManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
return callback(error) if error?
|
|
|
|
if fromVersion == -1
|
2018-04-23 07:08:04 -04:00
|
|
|
callback null, lines, version, [], ranges, pathname, projectHistoryId
|
2014-02-12 05:40:42 -05:00
|
|
|
else
|
2016-08-23 11:00:46 -04:00
|
|
|
RedisManager.getPreviousDocOps doc_id, fromVersion, version, (error, ops) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
return callback(error) if error?
|
2018-04-23 07:08:04 -04:00
|
|
|
callback null, lines, version, ops, ranges, pathname, projectHistoryId
|
2014-02-12 05:40:42 -05:00
|
|
|
|
2017-03-21 07:20:38 -04:00
|
|
|
setDoc: (project_id, doc_id, newLines, source, user_id, undoing, _callback = (error) ->) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
timer = new Metrics.Timer("docManager.setDoc")
|
|
|
|
callback = (args...) ->
|
|
|
|
timer.done()
|
|
|
|
_callback(args...)
|
|
|
|
|
|
|
|
if !newLines?
|
|
|
|
return callback(new Error("No lines were provided to setDoc"))
|
|
|
|
|
|
|
|
UpdateManager = require "./UpdateManager"
|
2018-04-23 07:08:04 -04:00
|
|
|
DocumentManager.getDoc project_id, doc_id, (error, oldLines, version, ranges, pathname, projectHistoryId, unflushedTime, alreadyLoaded) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
return callback(error) if error?
|
2017-09-29 06:06:20 -04:00
|
|
|
|
2014-02-12 05:40:42 -05:00
|
|
|
if oldLines? and oldLines.length > 0 and oldLines[0].text?
|
|
|
|
logger.log doc_id: doc_id, project_id: project_id, oldLines: oldLines, newLines: newLines, "document is JSON so not updating"
|
|
|
|
return callback(null)
|
|
|
|
|
|
|
|
logger.log doc_id: doc_id, project_id: project_id, oldLines: oldLines, newLines: newLines, "setting a document via http"
|
|
|
|
DiffCodec.diffAsShareJsOp oldLines, newLines, (error, op) ->
|
|
|
|
return callback(error) if error?
|
2017-03-21 07:20:38 -04:00
|
|
|
if undoing
|
|
|
|
for o in op or []
|
|
|
|
o.u = true # Turn on undo flag for each op for track changes
|
2014-02-12 05:40:42 -05:00
|
|
|
update =
|
|
|
|
doc: doc_id
|
|
|
|
op: op
|
|
|
|
v: version
|
|
|
|
meta:
|
|
|
|
type: "external"
|
2014-03-11 08:47:26 -04:00
|
|
|
source: source
|
|
|
|
user_id: user_id
|
2016-09-09 06:01:14 -04:00
|
|
|
UpdateManager.applyUpdate project_id, doc_id, update, (error) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
return callback(error) if error?
|
2016-01-20 09:31:25 -05:00
|
|
|
# If the document was loaded already, then someone has it open
|
|
|
|
# in a project, and the usual flushing mechanism will happen.
|
|
|
|
# Otherwise we should remove it immediately since nothing else
|
|
|
|
# is using it.
|
|
|
|
if alreadyLoaded
|
|
|
|
DocumentManager.flushDocIfLoaded project_id, doc_id, (error) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
callback null
|
|
|
|
else
|
|
|
|
DocumentManager.flushAndDeleteDoc project_id, doc_id, (error) ->
|
2018-02-22 05:16:29 -05:00
|
|
|
# There is no harm in flushing project history if the previous
|
|
|
|
# call failed and sometimes it is required
|
2018-02-22 05:01:05 -05:00
|
|
|
HistoryManager.flushProjectChangesAsync project_id
|
|
|
|
|
2016-01-20 09:31:25 -05:00
|
|
|
return callback(error) if error?
|
|
|
|
callback null
|
2014-02-12 05:40:42 -05:00
|
|
|
|
|
|
|
flushDocIfLoaded: (project_id, doc_id, _callback = (error) ->) ->
|
|
|
|
timer = new Metrics.Timer("docManager.flushDocIfLoaded")
|
|
|
|
callback = (args...) ->
|
|
|
|
timer.done()
|
|
|
|
_callback(args...)
|
2019-04-22 20:02:48 -04:00
|
|
|
RedisManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId, unflushedTime, lastUpdatedAt, lastUpdatedBy) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
return callback(error) if error?
|
|
|
|
if !lines? or !version?
|
|
|
|
logger.log project_id: project_id, doc_id: doc_id, "doc is not loaded so not flushing"
|
2016-09-02 09:47:41 -04:00
|
|
|
callback null # TODO: return a flag to bail out, as we go on to remove doc from memory?
|
2014-02-12 05:40:42 -05:00
|
|
|
else
|
2014-02-10 10:17:08 -05:00
|
|
|
logger.log project_id: project_id, doc_id: doc_id, version: version, "flushing doc"
|
2019-04-22 20:02:48 -04:00
|
|
|
PersistenceManager.setDoc project_id, doc_id, lines, version, ranges, lastUpdatedAt, lastUpdatedBy, (error) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
return callback(error) if error?
|
2017-10-06 07:23:23 -04:00
|
|
|
RedisManager.clearUnflushedTime doc_id, callback
|
2014-02-12 05:40:42 -05:00
|
|
|
|
|
|
|
flushAndDeleteDoc: (project_id, doc_id, _callback = (error) ->) ->
|
|
|
|
timer = new Metrics.Timer("docManager.flushAndDeleteDoc")
|
|
|
|
callback = (args...) ->
|
|
|
|
timer.done()
|
|
|
|
_callback(args...)
|
|
|
|
|
|
|
|
DocumentManager.flushDocIfLoaded project_id, doc_id, (error) ->
|
|
|
|
return callback(error) if error?
|
2017-09-29 06:06:20 -04:00
|
|
|
|
2017-10-05 08:45:29 -04:00
|
|
|
# Flush in the background since it requires a http request
|
2018-02-22 05:01:05 -05:00
|
|
|
HistoryManager.flushDocChangesAsync project_id, doc_id
|
2016-01-20 10:05:31 -05:00
|
|
|
|
2014-02-12 05:40:42 -05:00
|
|
|
RedisManager.removeDocFromMemory project_id, doc_id, (error) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
callback null
|
2017-01-09 08:41:18 -05:00
|
|
|
|
2017-05-05 10:12:06 -04:00
|
|
|
acceptChanges: (project_id, doc_id, change_ids = [], _callback = (error) ->) ->
|
|
|
|
timer = new Metrics.Timer("docManager.acceptChanges")
|
2017-01-09 08:41:18 -05:00
|
|
|
callback = (args...) ->
|
|
|
|
timer.done()
|
|
|
|
_callback(args...)
|
|
|
|
|
|
|
|
DocumentManager.getDoc project_id, doc_id, (error, lines, version, ranges) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
if !lines? or !version?
|
|
|
|
return callback(new Errors.NotFoundError("document not found: #{doc_id}"))
|
2017-05-05 10:12:06 -04:00
|
|
|
RangesManager.acceptChanges change_ids, ranges, (error, new_ranges) ->
|
2017-01-09 08:41:18 -05:00
|
|
|
return callback(error) if error?
|
2019-04-22 20:02:48 -04:00
|
|
|
RedisManager.updateDocument project_id, doc_id, lines, version, [], new_ranges, {}, (error) ->
|
2017-01-09 08:41:18 -05:00
|
|
|
return callback(error) if error?
|
|
|
|
callback()
|
2017-09-29 06:06:20 -04:00
|
|
|
|
2017-01-24 09:57:11 -05:00
|
|
|
deleteComment: (project_id, doc_id, comment_id, _callback = (error) ->) ->
|
|
|
|
timer = new Metrics.Timer("docManager.deleteComment")
|
|
|
|
callback = (args...) ->
|
|
|
|
timer.done()
|
|
|
|
_callback(args...)
|
|
|
|
|
|
|
|
DocumentManager.getDoc project_id, doc_id, (error, lines, version, ranges) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
if !lines? or !version?
|
|
|
|
return callback(new Errors.NotFoundError("document not found: #{doc_id}"))
|
|
|
|
RangesManager.deleteComment comment_id, ranges, (error, new_ranges) ->
|
|
|
|
return callback(error) if error?
|
2019-04-22 20:02:48 -04:00
|
|
|
RedisManager.updateDocument project_id, doc_id, lines, version, [], new_ranges, {}, (error) ->
|
2017-01-24 09:57:11 -05:00
|
|
|
return callback(error) if error?
|
|
|
|
callback()
|
2014-02-12 05:40:42 -05:00
|
|
|
|
2018-04-23 07:08:04 -04:00
|
|
|
renameDoc: (project_id, doc_id, user_id, update, projectHistoryId, _callback = (error) ->) ->
|
2017-11-01 15:16:49 -04:00
|
|
|
timer = new Metrics.Timer("docManager.updateProject")
|
|
|
|
callback = (args...) ->
|
|
|
|
timer.done()
|
|
|
|
_callback(args...)
|
|
|
|
|
2018-04-23 07:08:04 -04:00
|
|
|
RedisManager.renameDoc project_id, doc_id, user_id, update, projectHistoryId, callback
|
2017-11-01 15:16:49 -04:00
|
|
|
|
2017-10-06 07:23:23 -04:00
|
|
|
getDocAndFlushIfOld: (project_id, doc_id, callback = (error, doc) ->) ->
|
2018-04-23 07:08:04 -04:00
|
|
|
DocumentManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId, unflushedTime, alreadyLoaded) ->
|
2017-10-06 07:23:23 -04:00
|
|
|
return callback(error) if error?
|
|
|
|
# if doc was already loaded see if it needs to be flushed
|
|
|
|
if alreadyLoaded and unflushedTime? and (Date.now() - unflushedTime) > MAX_UNFLUSHED_AGE
|
|
|
|
DocumentManager.flushDocIfLoaded project_id, doc_id, (error) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
callback(null, lines, version)
|
|
|
|
else
|
|
|
|
callback(null, lines, version)
|
|
|
|
|
2018-03-02 10:02:27 -05:00
|
|
|
resyncDocContents: (project_id, doc_id, callback) ->
|
2018-08-16 06:14:11 -04:00
|
|
|
logger.log {project_id: project_id, doc_id: doc_id}, "start resyncing doc contents"
|
2018-04-23 07:08:04 -04:00
|
|
|
RedisManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId) ->
|
2018-03-02 10:02:27 -05:00
|
|
|
return callback(error) if error?
|
|
|
|
|
|
|
|
if !lines? or !version?
|
2018-08-16 06:14:11 -04:00
|
|
|
logger.log {project_id: project_id, doc_id: doc_id}, "resyncing doc contents - not found in redis - retrieving from web"
|
2018-04-23 07:08:04 -04:00
|
|
|
PersistenceManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId) ->
|
2018-08-16 06:14:11 -04:00
|
|
|
if error?
|
|
|
|
logger.error {project_id: project_id, doc_id: doc_id, getDocError: error}, "resyncing doc contents - error retrieving from web"
|
|
|
|
return callback(error)
|
2018-04-23 07:08:04 -04:00
|
|
|
ProjectHistoryRedisManager.queueResyncDocContent project_id, projectHistoryId, doc_id, lines, version, pathname, callback
|
2018-03-02 10:02:27 -05:00
|
|
|
else
|
2018-08-16 06:14:11 -04:00
|
|
|
logger.log {project_id: project_id, doc_id: doc_id}, "resyncing doc contents - doc in redis - will queue in redis"
|
2018-04-23 07:08:04 -04:00
|
|
|
ProjectHistoryRedisManager.queueResyncDocContent project_id, projectHistoryId, doc_id, lines, version, pathname, callback
|
2018-03-02 10:02:27 -05:00
|
|
|
|
2014-02-12 05:40:42 -05:00
|
|
|
getDocWithLock: (project_id, doc_id, callback = (error, lines, version) ->) ->
|
|
|
|
UpdateManager = require "./UpdateManager"
|
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.getDoc, project_id, doc_id, callback
|
2017-09-29 06:06:20 -04:00
|
|
|
|
2018-04-23 07:08:04 -04:00
|
|
|
getDocAndRecentOpsWithLock: (project_id, doc_id, fromVersion, callback = (error, lines, version, ops, ranges, pathname, projectHistoryId) ->) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
UpdateManager = require "./UpdateManager"
|
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.getDocAndRecentOps, project_id, doc_id, fromVersion, callback
|
2017-10-06 07:23:23 -04:00
|
|
|
|
|
|
|
getDocAndFlushIfOldWithLock: (project_id, doc_id, callback = (error, doc) ->) ->
|
|
|
|
UpdateManager = require "./UpdateManager"
|
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.getDocAndFlushIfOld, project_id, doc_id, callback
|
|
|
|
|
2017-03-21 07:20:38 -04:00
|
|
|
setDocWithLock: (project_id, doc_id, lines, source, user_id, undoing, callback = (error) ->) ->
|
2014-02-12 05:40:42 -05:00
|
|
|
UpdateManager = require "./UpdateManager"
|
2017-03-21 07:20:38 -04:00
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.setDoc, project_id, doc_id, lines, source, user_id, undoing, callback
|
2017-09-29 06:06:20 -04:00
|
|
|
|
2014-02-12 05:40:42 -05:00
|
|
|
flushDocIfLoadedWithLock: (project_id, doc_id, callback = (error) ->) ->
|
|
|
|
UpdateManager = require "./UpdateManager"
|
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.flushDocIfLoaded, project_id, doc_id, callback
|
|
|
|
|
|
|
|
flushAndDeleteDocWithLock: (project_id, doc_id, callback = (error) ->) ->
|
|
|
|
UpdateManager = require "./UpdateManager"
|
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.flushAndDeleteDoc, project_id, doc_id, callback
|
2017-01-09 08:41:18 -05:00
|
|
|
|
2020-03-07 08:11:18 -05:00
|
|
|
deleteDocWithLock: (project_id, doc_id, callback) ->
|
|
|
|
UpdateManager = require "./UpdateManager"
|
|
|
|
UpdateManager.lockUpdatesAndDo RedisManager.removeDocFromMemory, project_id, doc_id, callback
|
|
|
|
|
2017-05-05 10:12:06 -04:00
|
|
|
acceptChangesWithLock: (project_id, doc_id, change_ids, callback = (error) ->) ->
|
2017-01-09 08:41:18 -05:00
|
|
|
UpdateManager = require "./UpdateManager"
|
2017-05-05 10:12:06 -04:00
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.acceptChanges, project_id, doc_id, change_ids, callback
|
2017-01-24 09:57:11 -05:00
|
|
|
|
|
|
|
deleteCommentWithLock: (project_id, doc_id, thread_id, callback = (error) ->) ->
|
|
|
|
UpdateManager = require "./UpdateManager"
|
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.deleteComment, project_id, doc_id, thread_id, callback
|
2017-11-01 15:16:49 -04:00
|
|
|
|
2018-04-23 07:08:04 -04:00
|
|
|
renameDocWithLock: (project_id, doc_id, user_id, update, projectHistoryId, callback = (error) ->) ->
|
2017-11-01 15:16:49 -04:00
|
|
|
UpdateManager = require "./UpdateManager"
|
2018-04-23 07:08:04 -04:00
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.renameDoc, project_id, doc_id, user_id, update, projectHistoryId, callback
|
2018-03-02 10:02:27 -05:00
|
|
|
|
|
|
|
resyncDocContentsWithLock: (project_id, doc_id, callback = (error) ->) ->
|
|
|
|
UpdateManager = require "./UpdateManager"
|
|
|
|
UpdateManager.lockUpdatesAndDo DocumentManager.resyncDocContents, project_id, doc_id, callback
|