Skip to content

Commit d8befba

Browse files
martinemdedeivid-rodriguez
authored andcommitted
Merge pull request #6931 from rubygems/martinemde/fetch_names_never_overrides
Improve Bundle::Index efficiency by removing unnecessary creation and dups (cherry picked from commit 0e54343)
1 parent 106f8a2 commit d8befba

File tree

2 files changed

+55
-30
lines changed

2 files changed

+55
-30
lines changed

bundler/lib/bundler/index.rb

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ def self.build
1010
i
1111
end
1212

13-
attr_reader :specs, :all_specs, :sources
14-
protected :specs, :all_specs
13+
attr_reader :specs, :duplicates, :sources
14+
protected :specs, :duplicates
1515

1616
RUBY = "ruby"
1717
NULL = "\0"
@@ -20,20 +20,20 @@ def initialize
2020
@sources = []
2121
@cache = {}
2222
@specs = Hash.new {|h, k| h[k] = {} }
23-
@all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
23+
@duplicates = {}
2424
end
2525

2626
def initialize_copy(o)
2727
@sources = o.sources.dup
2828
@cache = {}
2929
@specs = Hash.new {|h, k| h[k] = {} }
30-
@all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
30+
@duplicates = {}
3131

3232
o.specs.each do |name, hash|
3333
@specs[name] = hash.dup
3434
end
35-
o.all_specs.each do |name, array|
36-
@all_specs[name] = array.dup
35+
o.duplicates.each do |name, array|
36+
@duplicates[name] = array.dup
3737
end
3838
end
3939

@@ -47,7 +47,9 @@ def empty?
4747
end
4848

4949
def search_all(name)
50-
all_matches = local_search(name) + @all_specs[name]
50+
all_matches = specs_by_name(name) # always returns a new Array
51+
dupes = @duplicates[name]
52+
all_matches.concat(dupes) if dupes
5153
@sources.each do |source|
5254
all_matches.concat(source.search_all(name))
5355
end
@@ -78,10 +80,11 @@ def local_search(query)
7880

7981
alias_method :[], :search
8082

81-
def <<(spec)
83+
def add(spec)
8284
@specs[spec.name][spec.full_name] = spec
8385
spec
8486
end
87+
alias_method :<<, :add
8588

8689
def each(&blk)
8790
return enum_for(:each) unless blk
@@ -115,15 +118,25 @@ def dependency_names
115118
names.uniq
116119
end
117120

118-
def use(other, override_dupes = false)
121+
# Combines indexes proritizing existing specs, like `Hash#reverse_merge!`
122+
# Duplicate specs found in `other` are stored in `@duplicates`.
123+
def use(other)
119124
return unless other
120-
other.each do |s|
121-
if (dupes = search_by_spec(s)) && !dupes.empty?
122-
# safe to << since it's a new array when it has contents
123-
@all_specs[s.name] = dupes << s
124-
next unless override_dupes
125+
other.each do |spec|
126+
exist?(spec) ? add_duplicate(spec) : add(spec)
127+
end
128+
self
129+
end
130+
131+
# Combines indexes proritizing specs from `other`, like `Hash#merge!`
132+
# Duplicate specs found in `self` are saved in `@duplicates`.
133+
def merge!(other)
134+
return unless other
135+
other.each do |spec|
136+
if existing = find_by_spec(spec)
137+
add_duplicate(existing)
125138
end
126-
self << s
139+
add spec
127140
end
128141
self
129142
end
@@ -157,6 +170,10 @@ def add_source(index)
157170

158171
private
159172

173+
def add_duplicate(spec)
174+
(@duplicates[spec.name] ||= []) << spec
175+
end
176+
160177
def specs_by_name_and_version(name, version)
161178
specs_by_name(name).select {|spec| spec.version == version }
162179
end
@@ -168,8 +185,16 @@ def specs_by_name(name)
168185
EMPTY_SEARCH = [].freeze
169186

170187
def search_by_spec(spec)
171-
spec = @specs[spec.name][spec.full_name]
188+
spec = find_by_spec(spec)
172189
spec ? [spec] : EMPTY_SEARCH
173190
end
191+
192+
def find_by_spec(spec)
193+
@specs[spec.name][spec.full_name]
194+
end
195+
196+
def exist?(spec)
197+
@specs[spec.name].key?(spec.full_name)
198+
end
174199
end
175200
end

bundler/lib/bundler/source/rubygems.rb

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,12 @@ def identifier
128128
def specs
129129
@specs ||= begin
130130
# remote_specs usually generates a way larger Index than the other
131-
# sources, and large_idx.use small_idx is way faster than
132-
# small_idx.use large_idx.
133-
idx = @allow_remote ? remote_specs.dup : Index.new
134-
idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
135-
idx.use(installed_specs, :override_dupes) if @allow_local
136-
idx
131+
# sources, and large_idx.merge! small_idx is way faster than
132+
# small_idx.merge! large_idx.
133+
index = @allow_remote ? remote_specs.dup : Index.new
134+
index.merge!(cached_specs) if @allow_cached || @allow_remote
135+
index.merge!(installed_specs) if @allow_local
136+
index
137137
end
138138
end
139139

@@ -274,9 +274,9 @@ def double_check_for(unmet_dependency_names)
274274

275275
Bundler.ui.debug "Double checking for #{unmet_dependency_names || "all specs (due to the size of the request)"} in #{self}"
276276

277-
fetch_names(api_fetchers, unmet_dependency_names, remote_specs, false)
277+
fetch_names(api_fetchers, unmet_dependency_names, remote_specs)
278278

279-
specs.use(remote_specs, false)
279+
specs.use remote_specs
280280
end
281281

282282
def dependency_names_to_double_check
@@ -380,7 +380,7 @@ def installed_specs
380380

381381
def cached_specs
382382
@cached_specs ||= begin
383-
idx = @allow_local ? installed_specs.dup : Index.new
383+
idx = Index.new
384384

385385
Dir["#{cache_path}/*.gem"].each do |gemfile|
386386
s ||= Bundler.rubygems.spec_from_gem(gemfile)
@@ -401,22 +401,22 @@ def remote_specs
401401
index_fetchers = fetchers - api_fetchers
402402

403403
if index_fetchers.empty?
404-
fetch_names(api_fetchers, dependency_names, idx, false)
404+
fetch_names(api_fetchers, dependency_names, idx)
405405
else
406-
fetch_names(fetchers, nil, idx, false)
406+
fetch_names(fetchers, nil, idx)
407407
end
408408
end
409409
end
410410

411-
def fetch_names(fetchers, dependency_names, index, override_dupes)
411+
def fetch_names(fetchers, dependency_names, index)
412412
fetchers.each do |f|
413413
if dependency_names
414414
Bundler.ui.info "Fetching gem metadata from #{URICredentialsFilter.credential_filtered_uri(f.uri)}", Bundler.ui.debug?
415-
index.use f.specs_with_retry(dependency_names, self), override_dupes
415+
index.use f.specs_with_retry(dependency_names, self)
416416
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
417417
else
418418
Bundler.ui.info "Fetching source index from #{URICredentialsFilter.credential_filtered_uri(f.uri)}"
419-
index.use f.specs_with_retry(nil, self), override_dupes
419+
index.use f.specs_with_retry(nil, self)
420420
end
421421
end
422422
end

0 commit comments

Comments
 (0)