UpdateManager.coffee 4.1 KB

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