DocumentUpdaterHandler.coffee 8.2 KB

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