From 42ffda3f9bd84501fe31619d157128924bd3b404 Mon Sep 17 00:00:00 2001 From: Justin Kenyon Date: Fri, 13 Feb 2026 15:42:04 -0500 Subject: [PATCH 1/2] Prefetch all collection items globally before tests Instead of making 3 GraphQL API calls per collection (~300 total for 100 collections), collect all repo and user references across all collections upfront, deduplicate them, and batch the GraphQL queries. This reduces API round-trips from ~300 to ~10-15 (depending on total unique items), significantly improving test suite performance while maintaining the same validation coverage. --- test/collections_test.rb | 6 ++--- test/collections_test_helper.rb | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/test/collections_test.rb b/test/collections_test.rb index 6610ec218a8..0e31cd8cc3e 100644 --- a/test/collections_test.rb +++ b/test/collections_test.rb @@ -137,6 +137,8 @@ end it "fails if a user, organization, or repository has been renamed or removed" do + prefetch_all_collection_items! + errors = [] repos_to_check = [] users_to_check = [] @@ -151,10 +153,6 @@ end end - cache_repos_exist_check!(repos_to_check) - cache_users_exist_check!(users_to_check) - cache_orgs_exist_check!(users_not_found_from(users_to_check)) - repos_to_check.each do |repo| repo_result = client.repository(repo) current_name_with_owner = repo_result&.full_name diff --git a/test/collections_test_helper.rb b/test/collections_test_helper.rb index e7b50164b6d..230e0da2a21 100644 --- a/test/collections_test_helper.rb +++ b/test/collections_test_helper.rb @@ -106,3 +106,43 @@ def annotate_collection_item_error(collection, string, error_message) def possible_image_file_names_for_collection(collection) COLLECTION_IMAGE_EXTENSIONS.map { |ext| "#{collection}#{ext}" } end + +GRAPHQL_BATCH_SIZE = 100 + +def prefetch_all_collection_items! + return if @_prefetched + + all_repos = [] + all_users = [] + + collections.each do |collection| + items_for_collection(collection)&.each do |item| + if item.match?(USERNAME_AND_REPO_REGEX) + all_repos << item + elsif item.match?(USERNAME_REGEX) + all_users << item + end + end + end + + all_repos.uniq! + all_users.uniq! + + # Batch repos in chunks to stay within GraphQL query limits + all_repos.each_slice(GRAPHQL_BATCH_SIZE) do |batch| + cache_repos_exist_check!(batch) + end + + # Batch users in chunks + all_users.each_slice(GRAPHQL_BATCH_SIZE) do |batch| + cache_users_exist_check!(batch) + end + + # Check orgs for users not found + not_found_users = users_not_found_from(all_users) + not_found_users.each_slice(GRAPHQL_BATCH_SIZE) do |batch| + cache_orgs_exist_check!(batch) + end + + @_prefetched = true +end From 44f56efd0d78845d8ad699c26ffc2d6b0c897e20 Mon Sep 17 00:00:00 2001 From: Justin Kenyon Date: Fri, 13 Feb 2026 16:37:07 -0500 Subject: [PATCH 2/2] Use class-level prefetch flag and verify all batches succeed --- test/collections_test_helper.rb | 35 +++++++++++++++++++-------------- test/test_helper.rb | 9 +++++++++ 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/test/collections_test_helper.rb b/test/collections_test_helper.rb index 230e0da2a21..59048a782dd 100644 --- a/test/collections_test_helper.rb +++ b/test/collections_test_helper.rb @@ -110,7 +110,7 @@ def possible_image_file_names_for_collection(collection) GRAPHQL_BATCH_SIZE = 100 def prefetch_all_collection_items! - return if @_prefetched + return if NewOctokit.global_prefetch_done? all_repos = [] all_users = [] @@ -128,21 +128,26 @@ def prefetch_all_collection_items! all_repos.uniq! all_users.uniq! - # Batch repos in chunks to stay within GraphQL query limits - all_repos.each_slice(GRAPHQL_BATCH_SIZE) do |batch| - cache_repos_exist_check!(batch) - end + begin + # Batch repos in chunks to stay within GraphQL query limits + all_repos.each_slice(GRAPHQL_BATCH_SIZE) do |batch| + cache_repos_exist_check!(batch) + end - # Batch users in chunks - all_users.each_slice(GRAPHQL_BATCH_SIZE) do |batch| - cache_users_exist_check!(batch) - end + # Batch users in chunks + all_users.each_slice(GRAPHQL_BATCH_SIZE) do |batch| + cache_users_exist_check!(batch) + end - # Check orgs for users not found - not_found_users = users_not_found_from(all_users) - not_found_users.each_slice(GRAPHQL_BATCH_SIZE) do |batch| - cache_orgs_exist_check!(batch) - end + # Check orgs for users not found + not_found_users = users_not_found_from(all_users) + not_found_users.each_slice(GRAPHQL_BATCH_SIZE) do |batch| + cache_orgs_exist_check!(batch) + end - @_prefetched = true + # Only mark as done after ALL batch calls completed successfully + NewOctokit.global_prefetch_done! + rescue StandardError => error + warn "Global prefetch failed, falling back to per-test caching: #{error.message}" + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index c878c64d302..c81dde88da6 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -59,6 +59,7 @@ class NewOctokit < Octokit::Client @@repo_request_count = 0 unless defined? @@repo_request_count @@user_request_count = 0 unless defined? @@user_request_count @@messages = [] unless defined? @@messages + @@global_prefetch_done = false unless defined? @@global_prefetch_done def repos @@repos @@ -122,6 +123,14 @@ def self.messages @@messages end + def self.global_prefetch_done? + @@global_prefetch_done + end + + def self.global_prefetch_done! + @@global_prefetch_done = true + end + # rubocop:enable Style/ClassVars end