DispatchManager.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS101: Remove unnecessary use of Array.from
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * DS202: Simplify dynamic range loops
  13. * DS205: Consider reworking code to avoid use of IIFEs
  14. * DS207: Consider shorter variations of null checks
  15. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  16. */
  17. let DispatchManager
  18. const Settings = require('settings-sharelatex')
  19. const logger = require('logger-sharelatex')
  20. const Keys = require('./UpdateKeys')
  21. const redis = require('@overleaf/redis-wrapper')
  22. const Errors = require('./Errors')
  23. const UpdateManager = require('./UpdateManager')
  24. const Metrics = require('./Metrics')
  25. const RateLimitManager = require('./RateLimitManager')
  26. module.exports = DispatchManager = {
  27. createDispatcher(RateLimiter) {
  28. const client = redis.createClient(Settings.redis.documentupdater)
  29. var worker = {
  30. client,
  31. _waitForUpdateThenDispatchWorker(callback) {
  32. if (callback == null) {
  33. callback = function (error) {}
  34. }
  35. const timer = new Metrics.Timer('worker.waiting')
  36. return worker.client.blpop('pending-updates-list', 0, function (
  37. error,
  38. result
  39. ) {
  40. logger.log('getting pending-updates-list', error, result)
  41. timer.done()
  42. if (error != null) {
  43. return callback(error)
  44. }
  45. if (result == null) {
  46. return callback()
  47. }
  48. const [list_name, doc_key] = Array.from(result)
  49. const [project_id, doc_id] = Array.from(
  50. Keys.splitProjectIdAndDocId(doc_key)
  51. )
  52. // Dispatch this in the background
  53. const backgroundTask = (cb) =>
  54. UpdateManager.processOutstandingUpdatesWithLock(
  55. project_id,
  56. doc_id,
  57. function (error) {
  58. // log everything except OpRangeNotAvailable errors, these are normal
  59. if (error != null) {
  60. // downgrade OpRangeNotAvailable and "Delete component" errors so they are not sent to sentry
  61. const logAsWarning =
  62. error instanceof Errors.OpRangeNotAvailableError ||
  63. error instanceof Errors.DeleteMismatchError
  64. if (logAsWarning) {
  65. logger.warn(
  66. { err: error, project_id, doc_id },
  67. 'error processing update'
  68. )
  69. } else {
  70. logger.error(
  71. { err: error, project_id, doc_id },
  72. 'error processing update'
  73. )
  74. }
  75. }
  76. return cb()
  77. }
  78. )
  79. return RateLimiter.run(backgroundTask, callback)
  80. })
  81. },
  82. run() {
  83. if (Settings.shuttingDown) {
  84. return
  85. }
  86. return worker._waitForUpdateThenDispatchWorker((error) => {
  87. if (error != null) {
  88. logger.error({ err: error }, 'Error in worker process')
  89. throw error
  90. } else {
  91. return worker.run()
  92. }
  93. })
  94. }
  95. }
  96. return worker
  97. },
  98. createAndStartDispatchers(number) {
  99. const RateLimiter = new RateLimitManager(number)
  100. return (() => {
  101. const result = []
  102. for (
  103. let i = 1, end = number, asc = end >= 1;
  104. asc ? i <= end : i >= end;
  105. asc ? i++ : i--
  106. ) {
  107. const worker = DispatchManager.createDispatcher(RateLimiter)
  108. result.push(worker.run())
  109. }
  110. return result
  111. })()
  112. }
  113. }