DocumentUpdaterHandler.coffee 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. request = require 'request'
  2. request = request.defaults()
  3. async = require 'async'
  4. settings = require 'settings-sharelatex'
  5. _ = require 'underscore'
  6. async = require 'async'
  7. logger = require('logger-sharelatex')
  8. metrics = require('../../infrastructure/Metrics')
  9. slReqIdHelper = require('soa-req-id')
  10. redis = require("redis-sharelatex")
  11. rclient = redis.createClient(settings.redis.web)
  12. Project = require("../../models/Project").Project
  13. ProjectLocator = require('../../Features/Project/ProjectLocator')
  14. module.exports = DocumentUpdaterHandler =
  15. queueChange : (project_id, doc_id, change, sl_req_id, callback = ()->)->
  16. {callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
  17. jsonChange = JSON.stringify change
  18. doc_key = keys.combineProjectIdAndDocId(project_id, doc_id)
  19. multi = rclient.multi()
  20. multi.rpush keys.pendingUpdates(doc_id:doc_id), jsonChange
  21. multi.sadd keys.docsWithPendingUpdates, doc_key
  22. multi.rpush "pending-updates-list", doc_key
  23. multi.exec (error) ->
  24. return callback(error) if error?
  25. callback()
  26. flushProjectToMongo: (project_id, sl_req_id, callback = (error) ->)->
  27. {callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
  28. logger.log project_id:project_id, sl_req_id:sl_req_id, "flushing project from document updater"
  29. timer = new metrics.Timer("flushing.mongo.project")
  30. url = "#{settings.apis.documentupdater.url}/project/#{project_id}/flush"
  31. request.post url, (error, res, body)->
  32. if error?
  33. logger.error err: error, project_id: project_id, sl_req_id: sl_req_id, "error flushing project from document updater"
  34. return callback(error)
  35. else if res.statusCode >= 200 and res.statusCode < 300
  36. logger.log project_id: project_id, sl_req_id: sl_req_id, "flushed project from document updater"
  37. return callback(null)
  38. else
  39. error = new Error("document updater returned a failure status code: #{res.statusCode}")
  40. logger.error err: error, project_id: project_id, sl_req_id: sl_req_id, "document updater returned failure status code: #{res.statusCode}"
  41. return callback(error)
  42. flushMultipleProjectsToMongo: (project_ids, callback = (error) ->) ->
  43. jobs = []
  44. for project_id in project_ids
  45. do (project_id) ->
  46. jobs.push (callback) ->
  47. DocumentUpdaterHandler.flushProjectToMongo project_id, callback
  48. async.series jobs, callback
  49. flushProjectToMongoAndDelete: (project_id, sl_req_id, callback = ()->) ->
  50. {callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
  51. logger.log project_id:project_id, sl_req_id:sl_req_id, "deleting project from document updater"
  52. timer = new metrics.Timer("delete.mongo.project")
  53. url = "#{settings.apis.documentupdater.url}/project/#{project_id}"
  54. request.del url, (error, res, body)->
  55. if error?
  56. logger.error err: error, project_id: project_id, sl_req_id: sl_req_id, "error deleting project from document updater"
  57. return callback(error)
  58. else if res.statusCode >= 200 and res.statusCode < 300
  59. logger.log project_id: project_id, sl_req_id: sl_req_id, "deleted project from document updater"
  60. return callback(null)
  61. else
  62. error = new Error("document updater returned a failure status code: #{res.statusCode}")
  63. logger.error err: error, project_id: project_id, sl_req_id: sl_req_id, "document updater returned failure status code: #{res.statusCode}"
  64. return callback(error)
  65. flushDocToMongo: (project_id, doc_id, callback = (error) ->) ->
  66. logger.log project_id:project_id, doc_id: doc_id, "flushing doc from document updater"
  67. timer = new metrics.Timer("flushing.mongo.doc")
  68. url = "#{settings.apis.documentupdater.url}/project/#{project_id}/doc/#{doc_id}/flush"
  69. request.post url, (error, res, body)->
  70. if error?
  71. logger.error err: error, project_id: project_id, doc_id: doc_id, "error flushing doc from document updater"
  72. return callback(error)
  73. else if res.statusCode >= 200 and res.statusCode < 300
  74. logger.log project_id: project_id, doc_id: doc_id, "flushed doc from document updater"
  75. return callback(null)
  76. else
  77. error = new Error("document updater returned a failure status code: #{res.statusCode}")
  78. logger.error err: error, project_id: project_id, doc_id: doc_id, "document updater returned failure status code: #{res.statusCode}"
  79. return callback(error)
  80. deleteDoc : (project_id, doc_id, sl_req_id, callback = ()->)->
  81. {callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
  82. logger.log project_id:project_id, doc_id: doc_id, sl_req_id:sl_req_id, "deleting doc from document updater"
  83. timer = new metrics.Timer("delete.mongo.doc")
  84. url = "#{settings.apis.documentupdater.url}/project/#{project_id}/doc/#{doc_id}"
  85. request.del url, (error, res, body)->
  86. if error?
  87. logger.error err: error, project_id: project_id, doc_id: doc_id, sl_req_id: sl_req_id, "error deleting doc from document updater"
  88. return callback(error)
  89. else if res.statusCode >= 200 and res.statusCode < 300
  90. logger.log project_id: project_id, doc_id: doc_id, sl_req_id: sl_req_id, "deleted doc from document updater"
  91. return callback(null)
  92. else
  93. error = new Error("document updater returned a failure status code: #{res.statusCode}")
  94. logger.error err: error, project_id: project_id, doc_id: doc_id, sl_req_id: sl_req_id, "document updater returned failure status code: #{res.statusCode}"
  95. return callback(error)
  96. getDocument: (project_id, doc_id, fromVersion, sl_req_id, callback = (error, exists, doclines, version) ->) ->
  97. {callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
  98. timer = new metrics.Timer("get-document")
  99. url = "#{settings.apis.documentupdater.url}/project/#{project_id}/doc/#{doc_id}?fromVersion=#{fromVersion}"
  100. logger.log project_id:project_id, doc_id: doc_id, sl_req_id:sl_req_id, "getting doc from document updater"
  101. request.get url, (error, res, body)->
  102. timer.done()
  103. if error?
  104. logger.error err:error, url:url, project_id:project_id, doc_id:doc_id, "error getting doc from doc updater"
  105. return callback(error)
  106. if res.statusCode >= 200 and res.statusCode < 300
  107. logger.log project_id:project_id, doc_id:doc_id, "got doc from document document updater"
  108. try
  109. body = JSON.parse(body)
  110. catch error
  111. return callback(error)
  112. callback null, body.lines, body.version, body.ops
  113. else
  114. logger.error project_id:project_id, doc_id:doc_id, url: url, "doc updater returned a non-success status code: #{res.statusCode}"
  115. callback new Error("doc updater returned a non-success status code: #{res.statusCode}")
  116. setDocument : (project_id, doc_id, docLines, sl_req_id, callback = (error) ->)->
  117. {callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
  118. timer = new metrics.Timer("set-document")
  119. url = "#{settings.apis.documentupdater.url}/project/#{project_id}/doc/#{doc_id}"
  120. body =
  121. url: url
  122. json:
  123. lines: docLines
  124. logger.log project_id:project_id, doc_id: doc_id, sl_req_id:sl_req_id, "setting doc in document updater"
  125. request.post body, (error, res, body)->
  126. timer.done()
  127. if error?
  128. logger.error err:error, url:url, project_id:project_id, doc_id:doc_id, "error setting doc in doc updater"
  129. return callback(error)
  130. if res.statusCode >= 200 and res.statusCode < 300
  131. logger.log project_id: project_id, doc_id: doc_id, sl_req_id: sl_req_id, "set doc in document updater"
  132. return callback(null)
  133. else
  134. logger.error project_id:project_id, doc_id:doc_id, url: url, "doc updater returned a non-success status code: #{res.statusCode}"
  135. callback new Error("doc updater returned a non-success status code: #{res.statusCode}")
  136. getNumberOfDocsInMemory : (callback)->
  137. request.get "#{settings.apis.documentupdater.url}/total", (err, req, body)->
  138. try
  139. body = JSON.parse body
  140. catch err
  141. logger.err err:err, "error parsing response from doc updater about the total number of docs"
  142. callback(err, body?.total)
  143. PENDINGUPDATESKEY = "PendingUpdates"
  144. DOCLINESKEY = "doclines"
  145. DOCIDSWITHPENDINGUPDATES = "DocsWithPendingUpdates"
  146. keys =
  147. pendingUpdates : (op) -> "#{PENDINGUPDATESKEY}:#{op.doc_id}"
  148. docsWithPendingUpdates: DOCIDSWITHPENDINGUPDATES
  149. docLines : (op) -> "#{DOCLINESKEY}:#{op.doc_id}"
  150. combineProjectIdAndDocId: (project_id, doc_id) -> "#{project_id}:#{doc_id}"