ShareJsUpdateManager.coffee 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. ShareJsModel = require "./sharejs/server/model"
  2. ShareJsDB = require "./ShareJsDB"
  3. async = require "async"
  4. logger = require "logger-sharelatex"
  5. Settings = require('settings-sharelatex')
  6. Keys = require "./UpdateKeys"
  7. {EventEmitter} = require "events"
  8. util = require "util"
  9. WebRedisManager = require "./WebRedisManager"
  10. ShareJsModel:: = {}
  11. util.inherits ShareJsModel, EventEmitter
  12. module.exports = ShareJsUpdateManager =
  13. getNewShareJsModel: (project_id, doc_id, lines, version) ->
  14. db = new ShareJsDB(project_id, doc_id, lines, version)
  15. model = new ShareJsModel(db, maxDocLength: Settings.max_doc_length)
  16. model.db = db
  17. return model
  18. applyUpdate: (project_id, doc_id, update, lines, version, callback = (error, updatedDocLines) ->) ->
  19. logger.log project_id: project_id, doc_id: doc_id, update: update, "applying sharejs updates"
  20. jobs = []
  21. # We could use a global model for all docs, but we're hitting issues with the
  22. # internal state of ShareJS not being accessible for clearing caches, and
  23. # getting stuck due to queued callbacks (line 260 of sharejs/server/model.coffee)
  24. # This adds a small but hopefully acceptable overhead (~12ms per 1000 updates on
  25. # my 2009 MBP).
  26. model = @getNewShareJsModel(project_id, doc_id, lines, version)
  27. @_listenForOps(model)
  28. doc_key = Keys.combineProjectIdAndDocId(project_id, doc_id)
  29. model.applyOp doc_key, update, (error) ->
  30. if error?
  31. if error == "Op already submitted"
  32. logger.warn {project_id, doc_id, update}, "op has already been submitted"
  33. update.dup = true
  34. ShareJsUpdateManager._sendOp(project_id, doc_id, update)
  35. else
  36. ShareJsUpdateManager._sendError(project_id, doc_id, error)
  37. return callback(error)
  38. logger.log project_id: project_id, doc_id: doc_id, error: error, "applied update"
  39. model.getSnapshot doc_key, (error, data) =>
  40. if error?
  41. ShareJsUpdateManager._sendError(project_id, doc_id, error)
  42. return callback(error)
  43. docLines = data.snapshot.split(/\r\n|\n|\r/)
  44. callback(null, docLines, data.v, model.db.appliedOps[doc_key] or [])
  45. _listenForOps: (model) ->
  46. model.on "applyOp", (doc_key, opData) ->
  47. [project_id, doc_id] = Keys.splitProjectIdAndDocId(doc_key)
  48. ShareJsUpdateManager._sendOp(project_id, doc_id, opData)
  49. _sendOp: (project_id, doc_id, op) ->
  50. WebRedisManager.sendData {project_id, doc_id, op}
  51. _sendError: (project_id, doc_id, error) ->
  52. WebRedisManager.sendData {project_id, doc_id, error: error.message || error}