| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- RedisManager = require "./RedisManager"
- ProjectHistoryRedisManager = require "./ProjectHistoryRedisManager"
- PersistenceManager = require "./PersistenceManager"
- DiffCodec = require "./DiffCodec"
- logger = require "logger-sharelatex"
- Metrics = require "./Metrics"
- HistoryManager = require "./HistoryManager"
- RealTimeRedisManager = require "./RealTimeRedisManager"
- Errors = require "./Errors"
- RangesManager = require "./RangesManager"
- async = require "async"
- MAX_UNFLUSHED_AGE = 300 * 1000 # 5 mins, document should be flushed to mongo this time after a change
- module.exports = DocumentManager =
- getDoc: (project_id, doc_id, _callback = (error, lines, version, ranges, pathname, projectHistoryId, unflushedTime, alreadyLoaded) ->) ->
- timer = new Metrics.Timer("docManager.getDoc")
- callback = (args...) ->
- timer.done()
- _callback(args...)
- RedisManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId, unflushedTime) ->
- return callback(error) if error?
- if !lines? or !version?
- logger.log {project_id, doc_id}, "doc not in redis so getting from persistence API"
- PersistenceManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId) ->
- return callback(error) if error?
- logger.log {project_id, doc_id, lines, version, pathname, projectHistoryId}, "got doc from persistence API"
- RedisManager.putDocInMemory project_id, doc_id, lines, version, ranges, pathname, projectHistoryId, (error) ->
- return callback(error) if error?
- callback null, lines, version, ranges, pathname, projectHistoryId, null, false
- else
- callback null, lines, version, ranges, pathname, projectHistoryId, unflushedTime, true
- getDocAndRecentOps: (project_id, doc_id, fromVersion, _callback = (error, lines, version, ops, ranges, pathname, projectHistoryId) ->) ->
- timer = new Metrics.Timer("docManager.getDocAndRecentOps")
- callback = (args...) ->
- timer.done()
- _callback(args...)
- DocumentManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId) ->
- return callback(error) if error?
- if fromVersion == -1
- callback null, lines, version, [], ranges, pathname, projectHistoryId
- else
- RedisManager.getPreviousDocOps doc_id, fromVersion, version, (error, ops) ->
- return callback(error) if error?
- callback null, lines, version, ops, ranges, pathname, projectHistoryId
- setDoc: (project_id, doc_id, newLines, source, user_id, undoing, _callback = (error) ->) ->
- 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"
- DocumentManager.getDoc project_id, doc_id, (error, oldLines, version, ranges, pathname, projectHistoryId, unflushedTime, alreadyLoaded) ->
- return callback(error) if error?
- 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?
- if undoing
- for o in op or []
- o.u = true # Turn on undo flag for each op for track changes
- update =
- doc: doc_id
- op: op
- v: version
- meta:
- type: "external"
- source: source
- user_id: user_id
- UpdateManager.applyUpdate project_id, doc_id, update, (error) ->
- return callback(error) if error?
- # 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) ->
- # There is no harm in flushing project history if the previous
- # call failed and sometimes it is required
- HistoryManager.flushProjectChangesAsync project_id
- return callback(error) if error?
- callback null
- flushDocIfLoaded: (project_id, doc_id, _callback = (error) ->) ->
- timer = new Metrics.Timer("docManager.flushDocIfLoaded")
- callback = (args...) ->
- timer.done()
- _callback(args...)
- RedisManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId, unflushedTime, lastUpdatedAt, lastUpdatedBy) ->
- 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"
- callback null # TODO: return a flag to bail out, as we go on to remove doc from memory?
- else
- logger.log project_id: project_id, doc_id: doc_id, version: version, "flushing doc"
- PersistenceManager.setDoc project_id, doc_id, lines, version, ranges, lastUpdatedAt, lastUpdatedBy, (error) ->
- return callback(error) if error?
- RedisManager.clearUnflushedTime doc_id, callback
- 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?
- # Flush in the background since it requires a http request
- HistoryManager.flushDocChangesAsync project_id, doc_id
- RedisManager.removeDocFromMemory project_id, doc_id, (error) ->
- return callback(error) if error?
- callback null
- acceptChanges: (project_id, doc_id, change_ids = [], _callback = (error) ->) ->
- timer = new Metrics.Timer("docManager.acceptChanges")
- 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.acceptChanges change_ids, ranges, (error, new_ranges) ->
- return callback(error) if error?
- RedisManager.updateDocument project_id, doc_id, lines, version, [], new_ranges, {}, (error) ->
- return callback(error) if error?
- callback()
- 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?
- RedisManager.updateDocument project_id, doc_id, lines, version, [], new_ranges, {}, (error) ->
- return callback(error) if error?
- callback()
- renameDoc: (project_id, doc_id, user_id, update, projectHistoryId, _callback = (error) ->) ->
- timer = new Metrics.Timer("docManager.updateProject")
- callback = (args...) ->
- timer.done()
- _callback(args...)
- RedisManager.renameDoc project_id, doc_id, user_id, update, projectHistoryId, callback
- getDocAndFlushIfOld: (project_id, doc_id, callback = (error, doc) ->) ->
- DocumentManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId, unflushedTime, alreadyLoaded) ->
- 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)
- resyncDocContents: (project_id, doc_id, callback) ->
- logger.log {project_id: project_id, doc_id: doc_id}, "start resyncing doc contents"
- RedisManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId) ->
- return callback(error) if error?
- if !lines? or !version?
- logger.log {project_id: project_id, doc_id: doc_id}, "resyncing doc contents - not found in redis - retrieving from web"
- PersistenceManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, projectHistoryId) ->
- if error?
- logger.error {project_id: project_id, doc_id: doc_id, getDocError: error}, "resyncing doc contents - error retrieving from web"
- return callback(error)
- ProjectHistoryRedisManager.queueResyncDocContent project_id, projectHistoryId, doc_id, lines, version, pathname, callback
- else
- logger.log {project_id: project_id, doc_id: doc_id}, "resyncing doc contents - doc in redis - will queue in redis"
- ProjectHistoryRedisManager.queueResyncDocContent project_id, projectHistoryId, doc_id, lines, version, pathname, callback
- getDocWithLock: (project_id, doc_id, callback = (error, lines, version) ->) ->
- UpdateManager = require "./UpdateManager"
- UpdateManager.lockUpdatesAndDo DocumentManager.getDoc, project_id, doc_id, callback
- getDocAndRecentOpsWithLock: (project_id, doc_id, fromVersion, callback = (error, lines, version, ops, ranges, pathname, projectHistoryId) ->) ->
- UpdateManager = require "./UpdateManager"
- UpdateManager.lockUpdatesAndDo DocumentManager.getDocAndRecentOps, project_id, doc_id, fromVersion, callback
- getDocAndFlushIfOldWithLock: (project_id, doc_id, callback = (error, doc) ->) ->
- UpdateManager = require "./UpdateManager"
- UpdateManager.lockUpdatesAndDo DocumentManager.getDocAndFlushIfOld, project_id, doc_id, callback
- setDocWithLock: (project_id, doc_id, lines, source, user_id, undoing, callback = (error) ->) ->
- UpdateManager = require "./UpdateManager"
- UpdateManager.lockUpdatesAndDo DocumentManager.setDoc, project_id, doc_id, lines, source, user_id, undoing, callback
- 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
- acceptChangesWithLock: (project_id, doc_id, change_ids, callback = (error) ->) ->
- UpdateManager = require "./UpdateManager"
- UpdateManager.lockUpdatesAndDo DocumentManager.acceptChanges, project_id, doc_id, change_ids, callback
- deleteCommentWithLock: (project_id, doc_id, thread_id, callback = (error) ->) ->
- UpdateManager = require "./UpdateManager"
- UpdateManager.lockUpdatesAndDo DocumentManager.deleteComment, project_id, doc_id, thread_id, callback
- renameDocWithLock: (project_id, doc_id, user_id, update, projectHistoryId, callback = (error) ->) ->
- UpdateManager = require "./UpdateManager"
- UpdateManager.lockUpdatesAndDo DocumentManager.renameDoc, project_id, doc_id, user_id, update, projectHistoryId, callback
- resyncDocContentsWithLock: (project_id, doc_id, callback = (error) ->) ->
- UpdateManager = require "./UpdateManager"
- UpdateManager.lockUpdatesAndDo DocumentManager.resyncDocContents, project_id, doc_id, callback
|