ShareJsUpdateManager.coffee 2.3 KB

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