From b98f5d1a01e6579a6a4c09a701c7826049f33834 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Mon, 4 Sep 2023 15:31:20 +1000 Subject: [PATCH] feat: optimise matrix query when selectors with pacticipant names only are used --- .../matrix/matrix_row_dataset_module.rb | 46 ++++++++++++++-- .../matrix_row_verification_dataset_module.rb | 53 +++++++++++++++++-- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/lib/pact_broker/matrix/matrix_row_dataset_module.rb b/lib/pact_broker/matrix/matrix_row_dataset_module.rb index 4b4961629..1983ef777 100644 --- a/lib/pact_broker/matrix/matrix_row_dataset_module.rb +++ b/lib/pact_broker/matrix/matrix_row_dataset_module.rb @@ -99,16 +99,52 @@ def matching_only_selectors_joining_verifications(resolved_selectors) .where(consumer_id: specified_pacticipant_ids).or(provider_id: specified_pacticipant_ids) end - # Return pact publications where the consumer version is described by any of the resolved_selectors, AND the provider is described by any of the resolved selectors. + # Return pact publications where the consumer/consumer version is described by any of the resolved_selectors, AND the provider is described by any of the resolved selectors. # @private # @param [Array] resolved_selectors # @return [Sequel::Dataset] def matching_only_selectors_as_consumer(resolved_selectors) - pacticipant_ids = resolved_selectors.collect(&:pacticipant_id).uniq + [ + matching_only_selectors_as_consumer_where_only_pacticipant_name_in_selector(resolved_selectors), + matching_only_selectors_as_consumer_where_not_only_pacticipant_name_in_selector(resolved_selectors), + ].compact.reduce(&:union) + end - select_pact_columns_with_aliases - .inner_join_versions_for_selectors_as_consumer(resolved_selectors) - .where(provider_id: pacticipant_ids) + + # Return pact publications where the consumer is described by any of the resolved_selectors *that only specify the pacticipant NAME*, AND the provider is described by any of the resolved selectors. + # If the original selector only specified the pacticipant name, we don't need to join to the versions table to identify the required pact_publications. + # Return nil if there are no resolved selectors where only the pacticipant name is specified. + # @private + # @param [Array] resolved_selectors + # @return [Sequel::Dataset, nil] + def matching_only_selectors_as_consumer_where_only_pacticipant_name_in_selector(resolved_selectors) + all_pacticipant_ids = resolved_selectors.collect(&:pacticipant_id).uniq + pacticipant_ids_for_pacticipant_only_selectors = resolved_selectors.select(&:only_pacticipant_name_specified?).collect(&:pacticipant_id).uniq + + if pacticipant_ids_for_pacticipant_only_selectors.any? + select_pact_columns_with_aliases + .where(consumer_id: pacticipant_ids_for_pacticipant_only_selectors) + .where(provider_id: all_pacticipant_ids) + end + end + + # Return pact publications where the consumer *version* is described by any of the resolved_selectors + # *that specify more than just the pacticipant name*, + # AND the provider is described by any of the resolved selectors. + # If the selector uses any of the tag/branch/environment/latest attributes, we need to join to the versions table to identify the required pact_publications. + # Return nil if there are no resolved selectors where anything other than the pacticipant name is specified. + # @private + # @param [Array] resolved_selectors + # @return [Sequel::Dataset, nil] + def matching_only_selectors_as_consumer_where_not_only_pacticipant_name_in_selector(resolved_selectors) + all_pacticipant_ids = resolved_selectors.collect(&:pacticipant_id).uniq + resolved_selectors_with_versions_specified = resolved_selectors.reject(&:only_pacticipant_name_specified?) + + if resolved_selectors_with_versions_specified.any? + select_pact_columns_with_aliases + .inner_join_versions_for_selectors_as_consumer(resolved_selectors_with_versions_specified) + .where(provider_id: all_pacticipant_ids) + end end # @private diff --git a/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb b/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb index d58284eea..f1cd4e587 100644 --- a/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb +++ b/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb @@ -11,19 +11,62 @@ module MatrixRowVerificationDatasetModule # @param [Array] selectors # @return [Sequel::Dataset] def matching_only_selectors_as_provider(resolved_selectors) - # get the UnresolvedSelector objects back out of the resolved_selectors because the Version.for_selector() method uses the UnresolvedSelector - pacticipant_ids = resolved_selectors.collect(&:pacticipant_id).uniq - select_verification_columns_with_aliases - .inner_join_versions_for_selectors_as_provider(resolved_selectors) - .where(consumer_id: pacticipant_ids) + [ + matching_only_selectors_as_provider_where_only_pacticipant_name_in_selector(resolved_selectors), + matching_only_selectors_as_provider_where_not_only_pacticipant_name_in_selector(resolved_selectors) + ].compact.reduce(&:union) end + # @public + # @return [Sequel::Dataset, nil] def matching_selectors_as_provider_for_any_consumer(resolved_selectors) select_verification_columns_with_aliases .inner_join_versions_for_selectors_as_provider(resolved_selectors) end + # Return verifications where the provider is described by any of the resolved_selectors *that only specify the pacticipant NAME*, + # AND the consumer is described by any of the resolved selectors. + # If the original selector only specified the pacticipant name, we don't need to join to the versions table to identify the required verifications. + # Return nil if there are no resolved selectors where only the pacticipant name is specified. + # @private + # @param [Array] resolved_selectors + # @return [Sequel::Dataset, nil] + def matching_only_selectors_as_provider_where_only_pacticipant_name_in_selector(resolved_selectors) + all_pacticipant_ids = resolved_selectors.collect(&:pacticipant_id).uniq + pacticipant_ids_for_pacticipant_only_selectors = resolved_selectors.select(&:only_pacticipant_name_specified?).collect(&:pacticipant_id).uniq + + if pacticipant_ids_for_pacticipant_only_selectors.any? + select_verification_columns_with_aliases + .where(provider_id: pacticipant_ids_for_pacticipant_only_selectors) + .where(consumer_id: all_pacticipant_ids) + end + end + + # Return verifications where the provider *version* is described by any of the resolved_selectors + # *that specify more than just the pacticipant name*, + # AND the consumer is described by any of the resolved selectors. + # If the selector uses any of the tag/branch/environment/latest attributes, we need to join to the versions table to identify the required verifications. + # Return nil if there are no resolved selectors where anything other than the pacticipant name is specified. + # @private + # @param [Array] resolved_selectors + # @return [Sequel::Dataset, nil] + def matching_only_selectors_as_provider_where_not_only_pacticipant_name_in_selector(resolved_selectors) + # get the UnresolvedSelector objects back out of the resolved_selectors because the Version.for_selector() method uses the UnresolvedSelector + all_pacticipant_ids = resolved_selectors.collect(&:pacticipant_id).uniq + resolved_selectors_with_versions_specified = resolved_selectors.reject(&:only_pacticipant_name_specified?) + + if resolved_selectors_with_versions_specified.any? + select_verification_columns_with_aliases + .inner_join_versions_for_selectors_as_provider(resolved_selectors_with_versions_specified) + .where(consumer_id: all_pacticipant_ids) + end + end + + # Don't think it's worth splitting this into 2 different queries for selectors with only pacticipant name/selectors with version properties, + # as it's unlikely for there ever to be a query through the UI or CLI that results in 1 selector which only has a pacticipant name in it. # @private + # @param [Array] resolved_selectors + # @return [Sequel::Dataset] def inner_join_versions_for_selectors_as_provider(resolved_selectors) # get the UnresolvedSelector objects back out of the resolved_selectors because the Version.for_selector() method uses the UnresolvedSelector unresolved_selectors = resolved_selectors.collect(&:original_selector).uniq