From 1d024f77a614177bf2cdaa18f7518b35eb5423fb Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 16 Dec 2020 09:43:39 +0000 Subject: [PATCH] FEATURE: Allow plugins to register demon processes (#11493) This allows plugins to call `register_demon_process` with a Class inheriting from Demon::Base. The unicorn master process will take care of spawning, monitoring and restarting the process. This API should be used with extreme caution, but it is significantly cleaner than spawning processes/threads in an `after_initialize` block. This commit also cleans up the demon spawning logging so that it uses the same format as unicorn worker logging. It also switches to the block form of `fork` to ensure that Demons exit after running, rather than returning execution to where the fork took place. --- config/unicorn.conf.rb | 13 +++++++++++-- lib/demon/base.rb | 14 ++++++-------- lib/discourse_plugin_registry.rb | 1 + lib/plugin/instance.rb | 9 +++++++++ 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/config/unicorn.conf.rb b/config/unicorn.conf.rb index 373e235b3fa..3271f82466d 100644 --- a/config/unicorn.conf.rb +++ b/config/unicorn.conf.rb @@ -82,7 +82,7 @@ before_fork do |server, worker| sidekiqs = ENV['UNICORN_SIDEKIQS'].to_i if sidekiqs > 0 - puts "Starting up #{sidekiqs} supervised sidekiqs" + server.logger.info "starting #{sidekiqs} supervised sidekiqs" require 'demon/sidekiq' Demon::Sidekiq.after_fork do @@ -105,7 +105,7 @@ before_fork do |server, worker| end if ENV['DISCOURSE_ENABLE_EMAIL_SYNC_DEMON'] == 'true' - puts "Starting up EmailSync demon" + server.logger.info "starting up EmailSync demon" Demon::EmailSync.start Signal.trap("SIGTSTP") do STDERR.puts "#{Time.now}: Issuing stop to EmailSync" @@ -113,6 +113,11 @@ before_fork do |server, worker| end end + DiscoursePluginRegistry.demon_processes.each do |demon_class| + server.logger.info "starting #{demon_class.prefix} demon" + demon_class.start + end + class ::Unicorn::HttpServer alias :master_sleep_orig :master_sleep @@ -230,6 +235,10 @@ before_fork do |server, worker| check_email_sync_heartbeat end + DiscoursePluginRegistry.demon_processes.each do |demon_class| + demon_class.ensure_running + end + master_sleep_orig(sec) end end diff --git a/lib/demon/base.rb b/lib/demon/base.rb index 008a17d07c7..2d6d46e2955 100644 --- a/lib/demon/base.rb +++ b/lib/demon/base.rb @@ -141,15 +141,13 @@ class Demon::Base end def run - if @pid = fork - write_pid_file - return + @pid = fork do + Process.setproctitle("discourse #{self.class.prefix}") + monitor_parent + establish_app + after_fork end - - Process.setproctitle("discourse #{self.class.prefix}") - monitor_parent - establish_app - after_fork + write_pid_file end def already_running? diff --git a/lib/discourse_plugin_registry.rb b/lib/discourse_plugin_registry.rb index 890165a3158..3970591c88f 100644 --- a/lib/discourse_plugin_registry.rb +++ b/lib/discourse_plugin_registry.rb @@ -68,6 +68,7 @@ class DiscoursePluginRegistry define_register :vendored_pretty_text, Set define_register :vendored_core_pretty_text, Set define_register :seedfu_filter, Set + define_register :demon_processes, Set define_filtered_register :staff_user_custom_fields define_filtered_register :public_user_custom_fields diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb index f449a19593e..5af08b24e53 100644 --- a/lib/plugin/instance.rb +++ b/lib/plugin/instance.rb @@ -861,6 +861,15 @@ class Plugin::Instance ), self) end + # Register a new demon process to be forked by the Unicorn master. + # The demon_class should inherit from Demon::Base. + # With great power comes great responsibility - this method should + # be used with extreme caution. See `config/unicorn.conf.rb`. + def register_demon_process(demon_class) + raise "Not a demon class" if !demon_class.ancestors.include?(Demon::Base) + DiscoursePluginRegistry.demon_processes << demon_class + end + protected def self.js_path