QueueWorkers.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import Features from './Features.mjs'
  2. import Queues from './Queues.mjs'
  3. import UserOnboardingEmailManager from '../Features/User/UserOnboardingEmailManager.mjs'
  4. import UserPostRegistrationAnalyticsManager from '../Features/User/UserPostRegistrationAnalyticsManager.mjs'
  5. import FeaturesUpdater from '../Features/Subscription/FeaturesUpdater.mjs'
  6. import {
  7. addOptionalCleanupHandlerBeforeStoppingTraffic,
  8. addRequiredCleanupHandlerBeforeDrainingConnections,
  9. } from './GracefulShutdown.mjs'
  10. import EmailHandler from '../Features/Email/EmailHandler.mjs'
  11. import logger from '@overleaf/logger'
  12. import OError from '@overleaf/o-error'
  13. import Modules from './Modules.mjs'
  14. /**
  15. * @typedef {{
  16. * data: {queueName: string,name?: string,data?: any},
  17. * }} BullJob
  18. */
  19. /**
  20. * @param {string} queueName
  21. * @param {(job: BullJob) => Promise<void>} handler
  22. */
  23. function registerQueue(queueName, handler) {
  24. if (process.env.QUEUE_PROCESSING_ENABLED === 'true') {
  25. const queue = Queues.getQueue(queueName)
  26. queue.process(handler)
  27. registerCleanup(queue)
  28. }
  29. }
  30. function start() {
  31. if (!Features.hasFeature('saas')) {
  32. return
  33. }
  34. registerQueue('scheduled-jobs', async job => {
  35. const { queueName, name, data, options } = job.data
  36. const queue = Queues.getQueue(queueName)
  37. if (name) {
  38. await queue.add(name, data || {}, options || {})
  39. } else {
  40. await queue.add(data || {}, options || {})
  41. }
  42. })
  43. registerQueue('emails-onboarding', async job => {
  44. const { userId } = job.data
  45. await UserOnboardingEmailManager.sendOnboardingEmail(userId)
  46. })
  47. registerQueue('post-registration-analytics', async job => {
  48. const { userId } = job.data
  49. await UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(userId)
  50. })
  51. registerQueue('refresh-features', async job => {
  52. const { userId, reason } = job.data
  53. await FeaturesUpdater.promises.refreshFeatures(userId, reason)
  54. })
  55. registerQueue('deferred-emails', async job => {
  56. const { emailType, opts } = job.data
  57. try {
  58. await EmailHandler.promises.sendEmail(emailType, opts)
  59. } catch (e) {
  60. const error = OError.tag(e, 'failed to send deferred email')
  61. logger.warn({ error, emailType }, error.message)
  62. throw error
  63. }
  64. })
  65. registerQueue('group-sso-reminder', async job => {
  66. const { userId, subscriptionId } = job.data
  67. try {
  68. await Modules.promises.hooks.fire(
  69. 'sendGroupSSOReminder',
  70. userId,
  71. subscriptionId
  72. )
  73. } catch (e) {
  74. const error = OError.tag(
  75. e,
  76. 'failed to send scheduled Group SSO account linking reminder'
  77. )
  78. logger.warn({ error, userId, subscriptionId }, error.message)
  79. throw error
  80. }
  81. })
  82. registerQueue('deferred-subscription-webhook-event', async job => {
  83. const { eventId, eventType, serviceId } = job.data
  84. try {
  85. await Modules.promises.hooks.fire(
  86. 'handleDeferredSubscriptionWebhookEvent',
  87. job.data
  88. )
  89. } catch (e) {
  90. const error = OError.tag(
  91. e,
  92. 'failed to handle deferred subscription webhook event'
  93. )
  94. logger.warn({ error, eventId, eventType, serviceId }, error.message)
  95. throw error
  96. }
  97. })
  98. registerQueue('project-notification', async job => {
  99. const { projectId, timestamp } = job.data
  100. try {
  101. await Modules.promises.hooks.fire('projectModified', {
  102. projectId,
  103. timestamp,
  104. })
  105. } catch (e) {
  106. const error = OError.tag(e, 'failed to process project notification')
  107. logger.warn({ error, projectId }, error.message)
  108. throw error
  109. }
  110. })
  111. }
  112. function registerCleanup(queue) {
  113. const label = `bull queue ${queue.name}`
  114. // Stop accepting new jobs.
  115. addOptionalCleanupHandlerBeforeStoppingTraffic(label, async () => {
  116. const justThisWorker = true
  117. await queue.pause(justThisWorker)
  118. })
  119. // Wait for all jobs to process before shutting down connections.
  120. addRequiredCleanupHandlerBeforeDrainingConnections(label, async () => {
  121. await queue.close()
  122. })
  123. // Disconnect from redis is scheduled in queue setup.
  124. }
  125. export default { start, registerQueue }