From 5be3611ff3540990013fb54d27482ccc344a557f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 9 Apr 2019 13:20:26 -0700 Subject: [PATCH] fix memory leak introduced by connection pooling 5bae22e8a3626ab3b14743c00796d4f176580ad8 introduced a memory leak when it switched to connection pooling. We're setting a thread local but then never clearing the local. If the pool is used in the main thread, it will never die and the locals will stick around for the life of the process. Here is the program I used to test: ``` require 'net/http/persistent' def run2 1000.times do uri = URI 'http://localhost:5000' http = Net::HTTP::Persistent.new name: 'my_app_name' # perform a GET response = http.request uri # if you are done making http requests, or won't make requests for several # minutes http.shutdown end end p Thread.current.keys run2 p Thread.current.keys ``` --- lib/net/http/persistent.rb | 4 +--- lib/net/http/persistent/pool.rb | 12 ++++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/net/http/persistent.rb b/lib/net/http/persistent.rb index e1fadbc..ad66842 100644 --- a/lib/net/http/persistent.rb +++ b/lib/net/http/persistent.rb @@ -1048,9 +1048,7 @@ def request_setup req_or_uri # :nodoc: # #shutdown when you are completely done making requests! def shutdown - @pool.available.shutdown do |http| - http.finish - end + @pool.shutdown { |http| http.finish } end ## diff --git a/lib/net/http/persistent/pool.rb b/lib/net/http/persistent/pool.rb index b88dd59..90fd92b 100644 --- a/lib/net/http/persistent/pool.rb +++ b/lib/net/http/persistent/pool.rb @@ -7,11 +7,11 @@ def initialize(options = {}, &block) super @available = Net::HTTP::Persistent::TimedStackMulti.new(@size, &block) - @key = :"current-#{@available.object_id}" + @key = "current-#{@available.object_id}" end def checkin net_http_args - stack = Thread.current[@key][net_http_args] + stack = Thread.current[@key][net_http_args] ||= [] raise ConnectionPool::Error, 'no connections are checked out' if stack.empty? @@ -26,8 +26,8 @@ def checkin net_http_args end def checkout net_http_args - stacks = Thread.current[@key] ||= Hash.new { |h, k| h[k] = [] } - stack = stacks[net_http_args] + stacks = Thread.current[@key] ||= {} + stack = stacks[net_http_args] ||= [] if stack.empty? then conn = @available.pop connection_args: net_http_args @@ -40,6 +40,10 @@ def checkout net_http_args conn end + def shutdown + Thread.current[@key] = nil + super + end end require 'net/http/persistent/timed_stack_multi'