background_services.rb 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class BackgroundServices
  3. def self.available_services
  4. BackgroundServices::Service.descendants
  5. end
  6. attr_reader :config
  7. def initialize(config)
  8. @config = Array(config)
  9. end
  10. def run
  11. Rails.logger.debug 'Starting BackgroundServices...'
  12. config.each do |service_config|
  13. run_service service_config
  14. end
  15. Process.waitall
  16. loop do
  17. sleep 1
  18. end
  19. rescue Interrupt
  20. nil
  21. ensure
  22. Rails.logger.debug('Stopping BackgroundServices.')
  23. end
  24. private
  25. def run_service(service_config)
  26. if !service_config.enabled?
  27. Rails.logger.debug { "Skipping disabled service #{service_config.service.service_name}." }
  28. return
  29. end
  30. service_config.service.pre_run
  31. case service_config.start_as
  32. when :fork
  33. start_as_forks(service_config.service, service_config.workers)
  34. when :thread
  35. start_as_thread(service_config.service)
  36. end
  37. end
  38. def start_as_forks(service, forks)
  39. (1..forks).map do
  40. Process.fork do
  41. Rails.logger.debug { "Starting process ##{Process.pid} for service #{service.service_name}." }
  42. service.new.run
  43. rescue Interrupt
  44. nil
  45. end
  46. end
  47. end
  48. def start_as_thread(service)
  49. Thread.new do
  50. Thread.current.abort_on_exception = true
  51. Rails.logger.debug { "Starting thread for service #{service.service_name} in the main process." }
  52. service.new.run
  53. # BackgroundServices rspec test is using Timeout.timeout to stop background services.
  54. # It was fine for a long time, but started throwing following error in Rails 7.2.
  55. # This seems to affect that test case only.
  56. # Unfortunately, since it's running on a separate thread, that error has to be rescued here.
  57. # That said, this should be handled by improving services loops to support graceful exiting.
  58. rescue ActiveRecord::ActiveRecordError => e
  59. raise e if Rails.env.test? && e.message != 'Cannot expire connection, it is not currently leased.' # rubocop:disable Zammad/DetectTranslatableString
  60. end
  61. end
  62. end