From 627c0006409d12d489e283e3200c553e261de3b7 Mon Sep 17 00:00:00 2001 From: Sean Millichamp Date: Wed, 3 Apr 2024 00:12:51 +0000 Subject: [PATCH] Optimize for exact-match target name lookups Performing wildcard target name resolution is fairly costly, having to search through all group names, target names, and target aliases, calling File.fnmatch on each one in order to find matches. In the case of the targets being non-wildcard strings much faster exact matching can be performed resulting in a significant performance improvement. !feature * **Optimize get_targets performance for exact-match cases** Attempt exact target name matches for strings passed to get_targets and only attempt the slower wildcard match if the string contains a valid glob wildcard character. --- lib/bolt/inventory/inventory.rb | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/bolt/inventory/inventory.rb b/lib/bolt/inventory/inventory.rb index 54d67b387..b10cface3 100644 --- a/lib/bolt/inventory/inventory.rb +++ b/lib/bolt/inventory/inventory.rb @@ -14,6 +14,10 @@ class Inventory EXTENDED_TARGET_REGEX = /[[:space:],]+(?=[^\]}]*(?:[\[{]|$))/.freeze TARGET_REGEX = /[[:space:],]+/.freeze + # Pattern which looks for indicators that glob-based target name matching + # should be used. + GLOB_MATCH_REGEX = /[*?\[\]{}]/.freeze + class WildcardError < Bolt::Error def initialize(target) super("Found 0 targets matching wildcard pattern #{target}", 'bolt.inventory/wildcard-error') @@ -125,12 +129,18 @@ def match_wildcard?(wildcard, target_name, ext_glob: false) # If target is a group name, expand it to the members of that group. # Else match against groups and targets in inventory by name or alias. - # If a wildcard string, error if no matches are found. + # Attempt exact matches for groups, targets, and aliases first for speed. + # If no exact match and the string contains wildcard characters, then try + # for a wildcard match and error if no matches are found. # Else fall back to [target] if no matches are found. def resolve_name(target, ext_glob: false) if (group = group_lookup[target]) group.all_targets.to_a - else + elsif @targets.key?(target) + [target] + elsif (real_target = groups.target_aliases[target]) + [real_target] + elsif GLOB_MATCH_REGEX.match?(target) targets = [] # Find groups that match the glob @@ -147,12 +157,11 @@ def resolve_name(target, ext_glob: false) .select { |tgt_alias, _| match_wildcard?(target, tgt_alias, ext_glob: ext_glob) } .values - if targets.empty? - raise(WildcardError, target) if target.include?('*') - [target] - else - targets.uniq - end + raise(WildcardError, target) if targets.empty? + + targets.uniq + else # rubocop:disable Lint/DuplicateBranch + [target] end end private :resolve_name