UpdateManager.coffee 4.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. LockManager = require "./LockManager"
  2. RedisManager = require "./RedisManager"
  3. WebRedisManager = require "./WebRedisManager"
  4. ShareJsUpdateManager = require "./ShareJsUpdateManager"
  5. Settings = require('settings-sharelatex')
  6. async = require("async")
  7. logger = require('logger-sharelatex')
  8. Metrics = require "./Metrics"
  9. module.exports = UpdateManager =
  10. processOutstandingUpdates: (project_id, doc_id, callback = (error) ->) ->
  11. timer = new Metrics.Timer("updateManager.processOutstandingUpdates")
  12. UpdateManager.fetchAndApplyUpdates project_id, doc_id, (error) ->
  13. timer.done()
  14. return callback(error) if error?
  15. callback()
  16. processOutstandingUpdatesWithLock: (project_id, doc_id, callback = (error) ->) ->
  17. LockManager.tryLock doc_id, (error, gotLock, lockValue) =>
  18. return callback(error) if error?
  19. return callback() if !gotLock
  20. UpdateManager.processOutstandingUpdates project_id, doc_id, (error) ->
  21. return UpdateManager._handleErrorInsideLock(doc_id, lockValue, error, callback) if error?
  22. LockManager.releaseLock doc_id, lockValue, (error) =>
  23. return callback(error) if error?
  24. UpdateManager.continueProcessingUpdatesWithLock project_id, doc_id, callback
  25. continueProcessingUpdatesWithLock: (project_id, doc_id, callback = (error) ->) ->
  26. WebRedisManager.getUpdatesLength doc_id, (error, length) =>
  27. return callback(error) if error?
  28. if length > 0
  29. UpdateManager.processOutstandingUpdatesWithLock project_id, doc_id, callback
  30. else
  31. callback()
  32. fetchAndApplyUpdates: (project_id, doc_id, callback = (error) ->) ->
  33. WebRedisManager.getPendingUpdatesForDoc doc_id, (error, updates) =>
  34. return callback(error) if error?
  35. if updates.length == 0
  36. return callback()
  37. UpdateManager.applyUpdates project_id, doc_id, updates, callback
  38. applyUpdates: (project_id, doc_id, updates, callback = (error) ->) ->
  39. for update in updates or []
  40. UpdateManager._sanitizeUpdate update
  41. ShareJsUpdateManager.applyUpdates project_id, doc_id, updates, (error, updatedDocLines, version) ->
  42. return callback(error) if error?
  43. logger.log doc_id: doc_id, version: version, "updating doc via sharejs"
  44. RedisManager.setDocument doc_id, updatedDocLines, version, callback
  45. lockUpdatesAndDo: (method, project_id, doc_id, args..., callback) ->
  46. LockManager.getLock doc_id, (error, lockValue) ->
  47. return callback(error) if error?
  48. UpdateManager.processOutstandingUpdates project_id, doc_id, (error) ->
  49. return UpdateManager._handleErrorInsideLock(doc_id, lockValue, error, callback) if error?
  50. method project_id, doc_id, args..., (error, response_args...) ->
  51. return UpdateManager._handleErrorInsideLock(doc_id, lockValue, error, callback) if error?
  52. LockManager.releaseLock doc_id, lockValue, (error) ->
  53. return callback(error) if error?
  54. callback null, response_args...
  55. # We held the lock for a while so updates might have queued up
  56. UpdateManager.continueProcessingUpdatesWithLock project_id, doc_id
  57. _handleErrorInsideLock: (doc_id, lockValue, original_error, callback = (error) ->) ->
  58. LockManager.releaseLock doc_id, lockValue, (lock_error) ->
  59. callback(original_error)
  60. _sanitizeUpdate: (update) ->
  61. # In Javascript, characters are 16-bits wide. It does not understand surrogates as characters.
  62. #
  63. # From Wikipedia (http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane):
  64. # "The High Surrogates (U+D800–U+DBFF) and Low Surrogate (U+DC00–U+DFFF) codes are reserved
  65. # for encoding non-BMP characters in UTF-16 by using a pair of 16-bit codes: one High Surrogate
  66. # and one Low Surrogate. A single surrogate code point will never be assigned a character.""
  67. #
  68. # The main offender seems to be \uD835 as a stand alone character, which would be the first
  69. # 16-bit character of a blackboard bold character (http://www.fileformat.info/info/unicode/char/1d400/index.htm).
  70. # Something must be going on client side that is screwing up the encoding and splitting the
  71. # two 16-bit characters so that \uD835 is standalone.
  72. for op in update.op or []
  73. if op.i?
  74. # Replace high and low surrogate characters with 'replacement character' (\uFFFD)
  75. op.i = op.i.replace(/[\uD800-\uDFFF]/g, "\uFFFD")
  76. return update