ShareJsUpdateManager.coffee 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  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 "./RedisKeyBuilder"
  7. {EventEmitter} = require "events"
  8. util = require "util"
  9. redis = require("redis-sharelatex")
  10. rclient = redis.createClient(Settings.redis.web)
  11. ShareJsModel:: = {}
  12. util.inherits ShareJsModel, EventEmitter
  13. module.exports = ShareJsUpdateManager =
  14. getNewShareJsModel: () -> new ShareJsModel(ShareJsDB)
  15. applyUpdates: (project_id, doc_id, updates, callback = (error, updatedDocLines) ->) ->
  16. logger.log project_id: project_id, doc_id: doc_id, updates: updates, "applying sharejs updates"
  17. jobs = []
  18. # We could use a global model for all docs, but we're hitting issues with the
  19. # internal state of ShareJS not being accessible for clearing caches, and
  20. # getting stuck due to queued callbacks (line 260 of sharejs/server/model.coffee)
  21. # This adds a small but hopefully acceptable overhead (~12ms per 1000 updates on
  22. # my 2009 MBP).
  23. model = @getNewShareJsModel()
  24. @_listenForOps(model)
  25. doc_key = Keys.combineProjectIdAndDocId(project_id, doc_id)
  26. for update in updates
  27. do (update) =>
  28. jobs.push (callback) =>
  29. model.applyOp doc_key, update, callback
  30. async.series jobs, (error) =>
  31. logger.log project_id: project_id, doc_id: doc_id, error: error, "applied updates"
  32. if error?
  33. @_sendError(project_id, doc_id, error)
  34. return callback(error)
  35. model.getSnapshot doc_key, (error, data) =>
  36. if error?
  37. @_sendError(project_id, doc_id, error)
  38. return callback(error)
  39. docLines = data.snapshot.split(/\r\n|\n|\r/)
  40. callback(null, docLines, data.v)
  41. _listenForOps: (model) ->
  42. model.on "applyOp", (doc_key, opData) ->
  43. [project_id, doc_id] = Keys.splitProjectIdAndDocId(doc_key)
  44. data = JSON.stringify
  45. project_id: project_id
  46. doc_id: doc_id
  47. op: opData
  48. rclient.publish "applied-ops", data
  49. _sendError: (project_id, doc_id, error) ->
  50. data = JSON.stringify
  51. project_id: project_id
  52. doc_id: doc_id
  53. error: error.message || error
  54. rclient.publish "applied-ops", data