Bug 1398945 - Unpacking tree in Red Hat Repositories sequentially contacts CDN for each and every CDN path
Summary: Unpacking tree in Red Hat Repositories sequentially contacts CDN for each and...
Keywords:
Status: CLOSED ERRATA
Alias: None
Product: Red Hat Satellite
Classification: Red Hat
Component: Repositories
Version: 6.2.5
Hardware: Unspecified
OS: Unspecified
medium
medium
Target Milestone: Unspecified
Assignee: Justin Sherrill
QA Contact: Jonathon Turel
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2016-11-27 14:00 UTC by Pavel Moravec
Modified: 2019-09-25 21:26 UTC (History)
7 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2018-02-21 16:51:07 UTC
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Foreman Issue Tracker 17564 0 None None None 2016-12-02 16:30:36 UTC

Description Pavel Moravec 2016-11-27 14:00:16 UTC
Description of problem:
User scenario: I want to enable some CDN repo. Going to WebUI -> Content -> Red Hat Repositories. Once that is loaded, navigating / unwrapping any "tree leaf" (that already shows particular repos, not sub-tree), this step takes tens of seconds.

The reason is evident when enabling debug logs:

Rails.logger.debug "CDN: Requesting path #{used_url}" in /opt/theforeman/tfm/root/usr/share/gems/gems/katello-3.0.0.82/app/lib/katello/resources/cdn.rb is logged for each and every path that katello scans on CDN. Unwrapping " Red Hat Enterprise Linux 6 Server (RPMs) " logs

2016-11-27 14:57:15 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/listing
2016-11-27 14:57:17 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/6.1/listing
2016-11-27 14:57:18 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/6.2/listing
2016-11-27 14:57:20 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/6.3/listing
2016-11-27 14:57:22 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/6.4/listing
2016-11-27 14:57:24 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/6.5/listing
2016-11-27 14:57:26 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/6.6/listing
2016-11-27 14:57:28 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/6.7/listing
2016-11-27 14:57:30 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/6.8/listing
2016-11-27 14:57:32 [app] [D] CDN: Requesting path https://cdn.redhat.com:443/content/dist/rhel/server/6/6Server/listing


See the time spent between individual log entries. And also total time spent to process the user action.


Version-Release number of selected component (if applicable):
Sat6.2.4


How reproducible:
100%


Steps to Reproduce:
1. Have some manifest granting access to RHEL6 base repos.
2. In WebUI, click to Content -> Red Hat Repositories
3. Click to "Red Hat Enterprise Linux"
4. Measure how much time it takes to unwrap " Red Hat Enterprise Linux 6 Server (RPMs) "
5. Optionally enable debugs to see the "CDN: Requesting path .." logs.


Actual results:
30 seconds or so


Expected results:
below 10 seconds, if possible


Additional info:

Comment 2 Pavel Moravec 2016-11-27 14:17:28 UTC
Technically, the reason is twofold:

1) Adding two logs to /opt/theforeman/tfm/root/usr/share/gems/gems/katello-3.0.0.82/app/lib/katello/util/cdn_var_substitutor.rb:

      def get_substitutions_from(base_path)
        Rails.logger.info("get_substitutions_from CDN: #{Time.now.strftime('%s.%N')} before get")
        ret = @resource.get(File.join(base_path, "listing")).split("\n")
        Rails.logger.info("get_substitutions_from CDN: #{Time.now.strftime('%s.%N')} after get")
        @good_listings << base_path
        ret
      rescue Errors::NotFound => e # some of listing file points to not existing content
        @bad_listings << base_path
        @resource.log :error, e.message
        [] # return no substitution for unreachable listings
      end

clearly shows the @resource.get call (i.e. contacting CDN over SSL and parsing output) consumes the most of the time - almost whole time between two consequtive "CDN: Requesting path .." calls. We can hardly improve this substantially.


2) The GET requests to CDN are called sequentially, due to "until paths_with_vars.empty?" cycle in the same file:

      def substitute_vars_in_prefix(prefix_with_vars)
        paths_with_vars = { {} => prefix_with_vars}
        prefixes_without_vars = @substitutions[prefix_with_vars]

        unless prefixes_without_vars
          prefixes_without_vars = {}
          until paths_with_vars.empty?
            substitutions, path = paths_with_vars.shift

            if substituable? path
              for_each_substitute_of_next_var substitutions, path do |new_substitution, new_path|
                begin
                  paths_with_vars[new_substitution] = new_path
                rescue Errors::SecurityViolation
                  # Some paths may not be accessible
                  @resource.log :warn, "#{new_path} is not accessible, ignoring"
                end
              end
            else
              prefixes_without_vars[substitutions] = path
            end
          end
          @substitutions[prefix_with_vars] = prefixes_without_vars
        end
        return prefixes_without_vars
      end


My suggestion is therefore to make the "until paths_with_vars.empty? { substitutions, path = paths_with_vars.shift .. } calls in parallel. Pseudocode (ignore my syntax errors):


until paths_with_vars.empty?
  for substitutions, paths in paths_with_vars do
    fork do
      (do all the stuff and append (new_substitution,new_path) key/value pair in a thread-safe way to new variable new_paths_with_vars instead of updating paths_with_vars)
    end
  end
  paths_with_vars = new_paths_with_vars
end


Eric, as github blames the code on you :) does the patch skeleton sound reasonable? Esp. forking bits?

If so I can try to propose a full patch.

Comment 3 Justin Sherrill 2016-12-02 16:29:47 UTC
Pavel, 

I think ehelms just moved the code around :)

I think its a valid strategy and I've implemented it with what looks like good results.  

-Justin

Comment 4 Justin Sherrill 2016-12-02 16:30:33 UTC
Created redmine issue http://projects.theforeman.org/issues/17564 from this bug

Comment 5 Satellite Program 2017-02-20 15:10:44 UTC
Moving this bug to POST for triage into Satellite 6 since the upstream issue http://projects.theforeman.org/issues/17564 has been resolved.

Comment 8 Jonathon Turel 2017-08-02 20:41:49 UTC
I was able to expand several repository sets at the same time and most of them took less than three seconds. Good result!

Verified against Sat 6.3 Snap 9 => satellite-6.3.0-16.0.beta.el7sat.noarch

Comment 9 Satellite Program 2018-02-21 16:51:07 UTC
Since the problem described in this bug report should be resolved in a recent advisory, it has been closed with a resolution of ERRATA.
> 
> For information on the advisory, and where to find the updated files, follow the link below.
> 
> If the solution does not work for you, open a new bug report.
> 
> https://access.redhat.com/errata/RHSA-2018:0336


Note You need to log in before you can comment on or make changes to this bug.