diff --git a/lib/datadog/statsd/sender.rb b/lib/datadog/statsd/sender.rb index 9f50cc8..b2aa7bb 100644 --- a/lib/datadog/statsd/sender.rb +++ b/lib/datadog/statsd/sender.rb @@ -51,7 +51,7 @@ def rendez_vous # could happen if #start hasn't be called return unless message_queue - # Initialize and get the thread's sync queue + # initialize and get the thread's sync queue queue = (@thread_class.current[:statsd_sync_queue] ||= @queue_class.new) # tell sender-thread to notify us in the current # thread's queue @@ -104,6 +104,9 @@ def start # start background thread @sender_thread = @thread_class.new(&method(:send_loop)) @sender_thread.name = "Statsd Sender" unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3') + # advise multi-threaded app servers to ignore this thread for the purposes of fork safety warnings + # see Puma's implementation for `:fork_safe`: https://github.com/puma/puma/blob/v7.2.0/lib/puma/cluster.rb#L374 + @sender_thread.thread_variable_set(:fork_safe, true) rescue ThreadError => e @logger.debug { "Statsd: Failed to start sender thread: #{e.message}" } if @logger @mx.synchronize { @done = true } diff --git a/spec/statsd/sender_spec.rb b/spec/statsd/sender_spec.rb index 95c1f01..b0bf85d 100644 --- a/spec/statsd/sender_spec.rb +++ b/spec/statsd/sender_spec.rb @@ -48,6 +48,13 @@ |thds| thds.any? { |t| t.name == "Statsd Sender" } } end + + it 'marks the sender thread as fork safe' do + subject.start + expect(Thread.list).to satisfy { + |thds| thds.any? { |t| t.name == "Statsd Sender" && t.thread_variable_get(:fork_safe) } + } + end end context 'when the sender is started' do @@ -204,9 +211,9 @@ let(:thread_class) do if Thread.instance_methods.include?(:name=) - fake_thread = instance_double(Thread, { "alive?" => true, "name=" => true, "join" => true }) + fake_thread = instance_double(Thread, { "alive?" => true, "name=" => true, "join" => true, "thread_variable_set" => true }) else - fake_thread = instance_double(Thread, { "alive?" => true, "join" => true }) + fake_thread = instance_double(Thread, { "alive?" => true, "join" => true, "thread_variable_set" => true }) end class_double(Thread, new: fake_thread) end