Queues.mjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import Queue from 'bull'
  2. import Settings from '@overleaf/settings'
  3. import Features from '../infrastructure/Features.mjs'
  4. import { addConnectionDrainer } from './GracefulShutdown.mjs'
  5. // Bull will keep a fixed number of the most recently completed jobs. This is
  6. // useful to inspect recently completed jobs. The bull prometheus exporter also
  7. // uses the completed job records to report on job duration.
  8. const MAX_COMPLETED_JOBS_RETAINED = 10000
  9. const MAX_FAILED_JOBS_RETAINED = 50000
  10. const MAX_FAILED_JOBS_RETAINED_ANALYTICS = 3000000
  11. const QUEUES_JOB_OPTIONS = {
  12. 'analytics-events': {
  13. removeOnFail: MAX_FAILED_JOBS_RETAINED_ANALYTICS,
  14. },
  15. 'analytics-editing-sessions': {
  16. removeOnFail: MAX_FAILED_JOBS_RETAINED_ANALYTICS,
  17. },
  18. 'analytics-account-mapping': {
  19. removeOnFail: MAX_FAILED_JOBS_RETAINED_ANALYTICS,
  20. },
  21. 'analytics-user-properties': {
  22. removeOnFail: MAX_FAILED_JOBS_RETAINED_ANALYTICS,
  23. },
  24. 'refresh-features': {
  25. removeOnFail: MAX_FAILED_JOBS_RETAINED,
  26. attempts: 3,
  27. },
  28. 'analytics-email-change': {
  29. removeOnFail: MAX_FAILED_JOBS_RETAINED_ANALYTICS,
  30. },
  31. 'analytics-package-usage': {
  32. removeOnFail: MAX_FAILED_JOBS_RETAINED_ANALYTICS,
  33. },
  34. 'emails-onboarding': {
  35. removeOnFail: MAX_FAILED_JOBS_RETAINED,
  36. },
  37. 'post-registration-analytics': {
  38. removeOnFail: MAX_FAILED_JOBS_RETAINED_ANALYTICS,
  39. },
  40. 'scheduled-jobs': {
  41. removeOnFail: MAX_FAILED_JOBS_RETAINED,
  42. attempts: 1,
  43. },
  44. 'confirm-institution-domain': {
  45. removeOnFail: MAX_FAILED_JOBS_RETAINED,
  46. attempts: 3,
  47. },
  48. 'group-sso-reminder': {
  49. removeOnFail: MAX_FAILED_JOBS_RETAINED,
  50. attempts: 3,
  51. },
  52. 'deferred-subscription-webhook-event': {
  53. removeOnFail: MAX_FAILED_JOBS_RETAINED,
  54. attempts: 3,
  55. },
  56. 'project-notification': {
  57. removeOnFail: MAX_FAILED_JOBS_RETAINED,
  58. attempts: 3,
  59. },
  60. }
  61. const QUEUE_OPTIONS = {
  62. 'confirm-institution-domain': {
  63. limiter: {
  64. max: 1,
  65. duration: 60 * 1000,
  66. },
  67. },
  68. }
  69. const ANALYTICS_QUEUES = [
  70. 'analytics-account-mapping',
  71. 'analytics-email-change',
  72. 'analytics-events',
  73. 'analytics-editing-sessions',
  74. 'analytics-package-usage',
  75. 'analytics-user-properties',
  76. 'analytics-user-exports',
  77. 'post-registration-analytics',
  78. ]
  79. const queues = {}
  80. function getQueue(queueName) {
  81. if (!Features.hasFeature('saas')) {
  82. // Disable bull queue handling for Server Pro/CE by providing a stub interface.
  83. return {
  84. async add() {},
  85. process() {},
  86. }
  87. }
  88. if (!queues[queueName]) {
  89. const redisOptions = ANALYTICS_QUEUES.includes(queueName)
  90. ? Settings.redis.analyticsQueues
  91. : Settings.redis.queues
  92. const queueOptions = QUEUE_OPTIONS[queueName] || {}
  93. const jobOptions = QUEUES_JOB_OPTIONS[queueName] || {}
  94. queues[queueName] = new Queue(queueName, {
  95. // this configuration is duplicated in /services/analytics/app/js/Queues.js
  96. // and needs to be manually kept in sync whenever modified
  97. redis: redisOptions,
  98. ...queueOptions,
  99. defaultJobOptions: {
  100. removeOnComplete: MAX_COMPLETED_JOBS_RETAINED,
  101. attempts: 11,
  102. backoff: {
  103. type: 'exponential',
  104. delay: 3000,
  105. },
  106. ...jobOptions,
  107. },
  108. })
  109. // Disconnect from redis eventually.
  110. addConnectionDrainer(`bull queue ${queueName}`, async () => {
  111. await queues[queueName].disconnect()
  112. })
  113. }
  114. return queues[queueName]
  115. }
  116. async function createScheduledJob(queueName, { name, data, options }, delay) {
  117. await getQueue('scheduled-jobs').add(
  118. { queueName, name, data, options },
  119. {
  120. delay,
  121. }
  122. )
  123. }
  124. export default {
  125. getQueue,
  126. createScheduledJob,
  127. }