Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bundler/lib/bundler/fetcher/gem_remote_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
module Bundler
class Fetcher
class GemRemoteFetcher < Gem::RemoteFetcher
def initialize(*)
super

@pool_size = 5
end

def request(*args)
super do |req|
req.delete("User-Agent") if headers["User-Agent"]
Expand Down
60 changes: 60 additions & 0 deletions bundler/spec/bundler/fetcher/gem_remote_fetcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

require "rubygems/remote_fetcher"
require "bundler/fetcher/gem_remote_fetcher"
require_relative "../../support/artifice/helpers/artifice"
require "bundler/vendored_persistent.rb"

RSpec.describe Bundler::Fetcher::GemRemoteFetcher do
describe "Parallel download" do
it "download using multiple connections from the pool" do
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this patch, this test would deadlock

unless Bundler.rubygems.provides?(">= 4.0.0.dev")
skip "This example can only run when RubyGems supports multiple http connection pool"
end

require_relative "../../support/artifice/helpers/endpoint"
concurrent_ruby_path = Dir[scoped_base_system_gem_path.join("gems/concurrent-ruby-*/lib/concurrent-ruby")].first
$LOAD_PATH.unshift(concurrent_ruby_path)
require "concurrent-ruby"

require_rack_test
responses = []

latch1 = Concurrent::CountDownLatch.new
latch2 = Concurrent::CountDownLatch.new
previous_client = Gem::Request::ConnectionPools.client
dummy_endpoint = Class.new(Endpoint) do
get "/foo" do
latch2.count_down
latch1.wait

responses << "foo"
end

get "/bar" do
responses << "bar"

latch1.count_down
end
end

Artifice.activate_with(dummy_endpoint)
Gem::Request::ConnectionPools.client = Gem::Net::HTTP

first_request = Thread.new do
subject.fetch_path("https://example.org/foo")
end
second_request = Thread.new do
latch2.wait
subject.fetch_path("https://example.org/bar")
end

[first_request, second_request].each(&:join)

expect(responses).to eq(["bar", "foo"])
ensure
Artifice.deactivate
Gem::Request::ConnectionPools.client = previous_client
end
end
end
3 changes: 2 additions & 1 deletion lib/rubygems/remote_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def initialize(proxy = nil, dns = nil, headers = {})
@proxy = proxy
@pools = {}
@pool_lock = Thread::Mutex.new
@pool_size = 1
@cert_files = Gem::Request.get_cert_files

@headers = headers
Expand Down Expand Up @@ -338,7 +339,7 @@ def proxy_for(proxy, uri)

def pools_for(proxy)
@pool_lock.synchronize do
@pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files
@pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files, @pool_size
end
end
end
7 changes: 4 additions & 3 deletions lib/rubygems/request/connection_pools.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ class << self
attr_accessor :client
end

def initialize(proxy_uri, cert_files)
def initialize(proxy_uri, cert_files, pool_size = 1)
@proxy_uri = proxy_uri
@cert_files = cert_files
@pools = {}
@pool_mutex = Thread::Mutex.new
@pool_size = pool_size
end

def pool_for(uri)
Expand All @@ -20,9 +21,9 @@ def pool_for(uri)
@pool_mutex.synchronize do
@pools[key] ||=
if https? uri
Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri)
Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri, @pool_size)
else
Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri)
Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri, @pool_size)
end
end
end
Expand Down
15 changes: 11 additions & 4 deletions lib/rubygems/request/http_pool.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
class Gem::Request::HTTPPool # :nodoc:
attr_reader :cert_files, :proxy_uri

def initialize(http_args, cert_files, proxy_uri)
def initialize(http_args, cert_files, proxy_uri, pool_size)
@http_args = http_args
@cert_files = cert_files
@proxy_uri = proxy_uri
@queue = Thread::SizedQueue.new 1
@queue << nil
@pool_size = pool_size

@queue = Thread::SizedQueue.new @pool_size
setup_queue
end

def checkout
Expand All @@ -31,7 +33,8 @@ def close_all
connection.finish
end
end
@queue.push(nil)

setup_queue
end

private
Expand All @@ -44,4 +47,8 @@ def setup_connection(connection)
connection.start
connection
end

def setup_queue
@pool_size.times { @queue.push(nil) }
end
end
12 changes: 12 additions & 0 deletions test/rubygems/test_gem_request_connection_pools.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,16 @@ def test_thread_waits_for_connection
end
end.join
end

def test_checkouts_multiple_connections_from_the_pool
uri = Gem::URI.parse("http://example/some_endpoint")
pools = Gem::Request::ConnectionPools.new nil, [], 2
pool = pools.pool_for uri

pool.checkout

Thread.new do
assert_not_nil(pool.checkout)
end.join
end
end
1 change: 1 addition & 0 deletions tool/bundler/test_gems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
gem "rb_sys"
gem "fiddle"
gem "rubygems-generate_index", "~> 1.1"
gem "concurrent-ruby"
gem "psych"
gem "etc", platforms: [:ruby, :windows]
gem "open3"
Expand Down
3 changes: 3 additions & 0 deletions tool/bundler/test_gems.rb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ GEM
base64 (0.3.0)
builder (3.3.0)
compact_index (0.15.0)
concurrent-ruby (1.3.5)
date (3.5.0)
date (3.5.0-java)
etc (1.4.6)
Expand Down Expand Up @@ -59,6 +60,7 @@ PLATFORMS
DEPENDENCIES
builder (~> 3.2)
compact_index (~> 0.15.0)
concurrent-ruby
etc
fiddle
open3
Expand All @@ -75,6 +77,7 @@ CHECKSUMS
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
compact_index (0.15.0) sha256=5c6c404afca8928a7d9f4dde9524f6e1610db17e675330803055db282da84a8b
concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6
date (3.5.0) sha256=5e74fd6c04b0e65d97ad4f3bb5cb2d8efb37f386cc848f46310b4593ffc46ee5
date (3.5.0-java) sha256=d6876651299185b935e1b834a353e3a1d1db054be478967e8104e30a9a8f1127
etc (1.4.6) sha256=0f7e9e7842ea5e3c3bd9bc81746ebb8c65ea29e4c42a93520a0d638129c7de01
Expand Down