RateLimitManager.js 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. /* eslint-disable
  2. no-unused-vars,
  3. */
  4. // TODO: This file was created by bulk-decaffeinate.
  5. // Fix any style issues and re-enable lint.
  6. /*
  7. * decaffeinate suggestions:
  8. * DS102: Remove unnecessary code created because of implicit returns
  9. * DS207: Consider shorter variations of null checks
  10. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  11. */
  12. let RateLimiter
  13. const Settings = require('@overleaf/settings')
  14. const logger = require('@overleaf/logger')
  15. const Metrics = require('./Metrics')
  16. module.exports = RateLimiter = class RateLimiter {
  17. constructor(number) {
  18. if (number == null) {
  19. number = 10
  20. }
  21. this.ActiveWorkerCount = 0
  22. this.CurrentWorkerLimit = number
  23. this.BaseWorkerCount = number
  24. }
  25. _adjustLimitUp() {
  26. this.CurrentWorkerLimit += 0.1 // allow target worker limit to increase gradually
  27. return Metrics.gauge('currentLimit', Math.ceil(this.CurrentWorkerLimit))
  28. }
  29. _adjustLimitDown() {
  30. this.CurrentWorkerLimit = Math.max(
  31. this.BaseWorkerCount,
  32. this.CurrentWorkerLimit * 0.9
  33. )
  34. logger.debug(
  35. { currentLimit: Math.ceil(this.CurrentWorkerLimit) },
  36. 'reducing rate limit'
  37. )
  38. return Metrics.gauge('currentLimit', Math.ceil(this.CurrentWorkerLimit))
  39. }
  40. _trackAndRun(task, callback) {
  41. if (callback == null) {
  42. callback = function () {}
  43. }
  44. this.ActiveWorkerCount++
  45. Metrics.gauge('processingUpdates', this.ActiveWorkerCount)
  46. return task(err => {
  47. this.ActiveWorkerCount--
  48. Metrics.gauge('processingUpdates', this.ActiveWorkerCount)
  49. return callback(err)
  50. })
  51. }
  52. run(task, callback) {
  53. if (this.ActiveWorkerCount < this.CurrentWorkerLimit) {
  54. // below the limit, just put the task in the background
  55. this._trackAndRun(task, err => {
  56. if (err) {
  57. logger.error({ err }, 'error in background task')
  58. }
  59. })
  60. callback() // return immediately
  61. if (this.CurrentWorkerLimit > this.BaseWorkerCount) {
  62. return this._adjustLimitDown()
  63. }
  64. } else {
  65. logger.debug(
  66. {
  67. active: this.ActiveWorkerCount,
  68. currentLimit: Math.ceil(this.CurrentWorkerLimit),
  69. },
  70. 'hit rate limit'
  71. )
  72. return this._trackAndRun(task, err => {
  73. if (err == null) {
  74. this._adjustLimitUp()
  75. } // don't increment rate limit if there was an error
  76. return callback(err)
  77. }) // only return after task completes
  78. }
  79. }
  80. }