diff --git a/packages/forest_admin_agent/lib/forest_admin_agent/builder/agent_factory.rb b/packages/forest_admin_agent/lib/forest_admin_agent/builder/agent_factory.rb index fb2051f99..1983a38ef 100644 --- a/packages/forest_admin_agent/lib/forest_admin_agent/builder/agent_factory.rb +++ b/packages/forest_admin_agent/lib/forest_admin_agent/builder/agent_factory.rb @@ -13,6 +13,11 @@ class AgentFactory attr_reader :customizer, :container, :has_env_secret attr_accessor :schema_only_mode + def initialize + super + @reloading = false + end + def setup(options) @options = options @has_env_secret = options.to_h.key?(:env_secret) @@ -75,6 +80,14 @@ def generate_schema_only end def reload! + if @reloading + @logger.log('Info', 'Agent is already reloading. Do nothing.') + return + end + + @reloading = true + @logger.log('Info', 'Agent is reloading...') + begin @customizer.reload!(logger: @logger) rescue StandardError => e @@ -89,6 +102,8 @@ def reload! @logger.log('Info', 'route cache cleared due to agent reload') send_schema + ensure + @reloading = false end def send_schema(force: false) diff --git a/packages/forest_admin_agent/spec/lib/forest_admin_agent/builder/agent_factory_spec.rb b/packages/forest_admin_agent/spec/lib/forest_admin_agent/builder/agent_factory_spec.rb index 9ed4626aa..342e65b3a 100644 --- a/packages/forest_admin_agent/spec/lib/forest_admin_agent/builder/agent_factory_spec.rb +++ b/packages/forest_admin_agent/spec/lib/forest_admin_agent/builder/agent_factory_spec.rb @@ -160,6 +160,64 @@ module Builder expect(instance.container).not_to have_received(:register) expect(instance).not_to have_received(:send_schema) end + + it 'skips reload if another reload is already in progress' do + instance = described_class.instance + + logger = instance_spy(Services::LoggerService) + instance.instance_variable_set(:@logger, logger) + + # Simulate a long-running reload by blocking inside customizer.reload! + reload_started = Queue.new + proceed = Queue.new + + allow(instance.customizer).to receive(:reload!) do + reload_started << true + proceed.pop + end + allow(instance).to receive(:send_schema) + + first_thread = Thread.new { instance.reload! } + reload_started.pop # wait until the first reload is inside customizer.reload! + + # Second reload should be skipped + instance.reload! + + expect(logger).to have_received(:log).with('Info', 'Agent is already reloading. Do nothing.') + + proceed << true # unblock first reload + first_thread.join + end + + it 'allows a new reload after the previous one completes' do + instance = described_class.instance + + allow(instance.customizer).to receive(:reload!) + allow(instance).to receive(:send_schema) + + instance.reload! + instance.reload! + + expect(instance.customizer).to have_received(:reload!).twice + end + + it 'resets the reloading flag even when an error occurs' do + instance = described_class.instance + + logger = instance_spy(Services::LoggerService) + instance.instance_variable_set(:@logger, logger) + + allow(instance.customizer).to receive(:reload!).and_raise(StandardError.new('Boom')) + allow(instance).to receive(:send_schema) + + instance.reload! + + # Should allow a subsequent reload (flag was reset despite the error) + allow(instance.customizer).to receive(:reload!) + instance.reload! + + expect(instance.customizer).to have_received(:reload!).twice + end end describe 'send_schema' do