diff --git a/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb b/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb index 4cb8839..2fe0f76 100644 --- a/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +++ b/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb @@ -36,7 +36,7 @@ def post(request) end # Depsolve! - solved = depsolve(request, desired_versions.keys, desired_versions, environment_constraints) + solved, cookbook_obj_cache = depsolve(request, desired_versions.keys, desired_versions, environment_constraints) unless solved if @last_missing_dep && !cookbook_names.include?(@last_missing_dep) return raise RestErrorResponse.new(412, "No such cookbook: #{@last_missing_dep}") @@ -50,23 +50,31 @@ def post(request) result = {} solved.each_pair do |name, versions| - cookbook = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ["cookbooks", name, versions[0]])) + if cookbook_obj_cache.dig(name, versions[0]) + cookbook = cookbook_obj_cache[name][versions[0]] + else + cookbook = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ["cookbooks", name, versions[0]])) + end result[name] = ChefData::DataNormalizer.normalize_cookbook(self, request.rest_path[0..1], cookbook, name, versions[0], request.base_uri, "MIN", false, api_version: request.api_version) end json_response(200, result) end - def depsolve(request, unsolved, desired_versions, environment_constraints) + def depsolve(request, unsolved, desired_versions, environment_constraints, cookbook_obj_cache = nil) desired_versions.each do |cb, ver| if ver.empty? @last_constraint_failure = cb - return nil + return [nil, nil] end end # If everything is already solve_for = unsolved[0] - return desired_versions unless solve_for + return [desired_versions, cookbook_obj_cache] unless solve_for + + # Cache the get_data calls - this saves the need to run the get_data call a second time in #post + # Conceptually: cache[cookbook][version] = cookbook_object + cookbook_obj_cache ||= {} # Go through each desired version of this cookbook, starting with the latest, # until we find one we can solve successfully with @@ -77,6 +85,8 @@ def depsolve(request, unsolved, desired_versions, environment_constraints) # Pick this cookbook, and add dependencies cookbook_obj = FFI_Yajl::Parser.parse(get_data(request, request.rest_path[0..1] + ["cookbooks", solve_for, desired_version])) + cookbook_obj_cache[solve_for] ||= {} + cookbook_obj_cache[solve_for][desired_version] = cookbook_obj cookbook_metadata = cookbook_obj["metadata"] || {} cookbook_dependencies = cookbook_metadata["dependencies"] || {} dep_not_found = false @@ -100,10 +110,10 @@ def depsolve(request, unsolved, desired_versions, environment_constraints) next if dep_not_found # Depsolve children with this desired version! First solution wins. - result = depsolve(request, new_unsolved, new_desired_versions, environment_constraints) - return result if result + result, cookbook_obj_cache = depsolve(request, new_unsolved, new_desired_versions, environment_constraints, cookbook_obj_cache) + return [result, cookbook_obj_cache] if result end - nil + [nil, nil] end def sort_versions(versions)