diff --git a/Gemfile b/Gemfile index 63ab2e4885d291f0ee4c20a8deb99cbac45eed62..d3a05e3eeb10531cc8d98fcd7730e03972a04246 100644 --- a/Gemfile +++ b/Gemfile @@ -57,7 +57,11 @@ group :monorepo do end gem 'gitlab-backup-cli', path: 'gems/gitlab-backup-cli', require: 'gitlab/backup/cli', feature_category: :backup_restore - +# load_balancing is manually required at the top of lib/gitlab/database.rb +# Otherwise the module Gitlab::Database::LoadBalancing would make an empty Gitlab::Database module and +# confuse the autoloader into skipping loading lib/gitlab/database.rb +gem 'gitlab-database-load_balancing', path: 'gems/gitlab-database-load_balancing', require: false, + feature_category: :database gem 'gitlab-secret_detection', '< 1.0', feature_category: :secret_detection # Responders respond_to and respond_with diff --git a/Gemfile.lock b/Gemfile.lock index 18ccdea1a726daca379a1a01ae59a833f7446686..646959f19b275876e321927c3717c00e2352b3fb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -67,6 +67,14 @@ PATH rexml (~> 3.4.0) thor (~> 1.3) +PATH + remote: gems/gitlab-database-load_balancing + specs: + gitlab-database-load_balancing (0.1.0) + gitlab-net-dns (~> 0.12) + pg (~> 1.5.6) + rails (>= 7.1) + PATH remote: gems/gitlab-housekeeper specs: @@ -2151,6 +2159,7 @@ DEPENDENCIES gitlab-cloud-connector (~> 1.21) gitlab-crystalball (~> 1.1.0) gitlab-dangerfiles (~> 4.9.0) + gitlab-database-load_balancing! gitlab-duo-workflow-service-client (~> 0.2)! gitlab-experiment (~> 0.9.1) gitlab-fog-azure-rm (~> 2.2.0) diff --git a/Gemfile.next.lock b/Gemfile.next.lock index 792875ddcf02f87bf8e9c3fd09a12b1c1d8f2a94..ec024424f3555f8eec8e178eb9a9070d487c4785 100644 --- a/Gemfile.next.lock +++ b/Gemfile.next.lock @@ -67,6 +67,14 @@ PATH rexml (~> 3.4.0) thor (~> 1.3) +PATH + remote: gems/gitlab-database-load_balancing + specs: + gitlab-database-load_balancing (0.1.0) + gitlab-net-dns (~> 0.12) + pg (~> 1.5.6) + rails (>= 7.1) + PATH remote: gems/gitlab-housekeeper specs: @@ -2146,6 +2154,7 @@ DEPENDENCIES gitlab-cloud-connector (~> 1.21) gitlab-crystalball (~> 1.1.0) gitlab-dangerfiles (~> 4.9.0) + gitlab-database-load_balancing! gitlab-duo-workflow-service-client (~> 0.2)! gitlab-experiment (~> 0.9.1) gitlab-fog-azure-rm (~> 2.2.0) diff --git a/config/initializers/load_balancing.rb b/config/initializers/load_balancing.rb index 32b8e6e5a738d9317916891099c88743a9d58062..2b397af691170f5c54ff8181b5cf3637ba014f5f 100644 --- a/config/initializers/load_balancing.rb +++ b/config/initializers/load_balancing.rb @@ -1,5 +1,38 @@ # frozen_string_literal: true +# Configure Load Balancing +Gitlab::Database::LoadBalancing.configure! do |load_balancer| + load_balancer.enabled = !Gitlab::Runtime.rake? + load_balancer.default_pool_size = Gitlab::Database.default_pool_size + load_balancer.base_models = ::Gitlab::Database.database_base_models_using_load_balancing.values.freeze + load_balancer.all_database_names = ::Gitlab::Database.all_database_names.freeze + + Gitlab::Cluster::LifecycleEvents.on_worker_start do + load_balancer.enabled = !Gitlab::Runtime.rake? + load_balancer.default_pool_size = Gitlab::Database.default_pool_size + end +end + +# Configure Load Balancing interface callbacks +Gitlab::Database::LoadBalancing::Callbacks.configure! do |callbacks| + hosts_gauge = Gitlab::Metrics.gauge(:db_load_balancing_hosts, 'Current number of load balancing hosts') + + callbacks.logger_proc = -> { Gitlab::Database::LoadBalancing::Logger.build } + callbacks.metrics_host_gauge_proc = ->(labels, value) { hosts_gauge.set(labels, value) } + callbacks.track_exception_proc = ->(exception) { Gitlab::ErrorTracking.track_exception(exception) } + callbacks.get_wal_for_proc = ->(key) { Gitlab::Redis::DbLoadBalancing.with { |redis| redis.get(key) } } + callbacks.del_wal_for_proc = ->(key) { Gitlab::Redis::DbLoadBalancing.with { |redis| redis.del(key) } } + callbacks.set_wal_for_proc = ->(key, value, ex:) do + Gitlab::Redis::DbLoadBalancing.with { |redis| redis.set(key, value, ex: ex) } + end + + callbacks.check_feature_flag = ->(key, thing, type: nil) do + # rubocop:disable Gitlab/FeatureFlagKeyDynamic -- passing through literal feature flag calls from gem + Feature.enabled?(key, thing, type: type) + # rubocop:enable Gitlab/FeatureFlagKeyDynamic + end +end + Gitlab::Application.configure do |config| config.middleware.use(Gitlab::Database::LoadBalancing::RackMiddleware) diff --git a/gems/gitlab-database-load_balancing/.gitlab-ci.yml b/gems/gitlab-database-load_balancing/.gitlab-ci.yml index 6816d641291d6d85ddb53f82d1e02fb1835d37aa..c798e8d2e5b2b42dd0bd776d8210afe768cbbfc8 100644 --- a/gems/gitlab-database-load_balancing/.gitlab-ci.yml +++ b/gems/gitlab-database-load_balancing/.gitlab-ci.yml @@ -2,3 +2,13 @@ include: - local: gems/gem-pg.gitlab-ci.yml inputs: gem_name: "gitlab-database-load_balancing" + +variables: + BUNDLE_GEMFILE: 'Gemfile' + +workflow: + rules: + - if: '$CI_COMMIT_BRANCH == "rails-next" || $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-with-rails-next/' + variables: + BUNDLE_GEMFILE: 'Gemfile.next' + - when: always diff --git a/gems/gitlab-database-load_balancing/.rubocop.yml b/gems/gitlab-database-load_balancing/.rubocop.yml index 583fa8227eec03099bb349d080ecc17feceb32ea..03e87fb1632f1159997e3618110ac6d55407c7ac 100644 --- a/gems/gitlab-database-load_balancing/.rubocop.yml +++ b/gems/gitlab-database-load_balancing/.rubocop.yml @@ -1,6 +1,56 @@ inherit_from: + - .rubocop_todo.yml - ../config/rubocop.yml Gemfile/MissingFeatureCategory: Exclude: - 'Gemfile' + +Database/MultipleDatabases: + Exclude: + - 'lib/gitlab/database/load_balancing/load_balancer.rb' + - 'spec/gitlab/database/load_balancing_spec.rb' + - 'spec/gitlab/database/load_balancing/host_list_spec.rb' + - 'spec/gitlab/database/load_balancing/host_spec.rb' + - 'spec/gitlab/database/load_balancing/load_balancer_spec.rb' + - 'spec/gitlab/database/load_balancing/setup_spec.rb' + - 'spec/gitlab/database/load_balancing/sticking_spec.rb' + +Graphql/ResolverType: + Exclude: + - 'lib/gitlab/database/load_balancing/resolver.rb' + - 'lib/gitlab/database/load_balancing/srv_resolver.rb' + +Rails/SkipsModelValidations: + Exclude: + - 'spec/gitlab/database/load_balancing/connection_proxy_spec.rb' + - 'spec/gitlab/database/load_balancing_spec.rb' + +RSpec/ExpectInHook: + Exclude: + - 'spec/gitlab/database/load_balancing/host_spec.rb' + - 'spec/gitlab/database/load_balancing/load_balancer_spec.rb' + +RSpec/ImplicitSubject: + Exclude: + - 'spec/gitlab/database/load_balancing/configuration_spec.rb' + +# Configuration parameters: AllowSubject. +RSpec/MultipleMemoizedHelpers: + Max: 25 + AllowSubject: true + +RSpec/VerifiedDoubles: + Exclude: + - 'spec/gitlab/database/load_balancing/configuration_spec.rb' + - 'spec/gitlab/database/load_balancing/connection_proxy_spec.rb' + - 'spec/gitlab/database/load_balancing/host_list_spec.rb' + - 'spec/gitlab/database/load_balancing/host_spec.rb' + - 'spec/gitlab/database/load_balancing/load_balancer_spec.rb' + - 'spec/gitlab/database/load_balancing/resolver_spec.rb' + - 'spec/gitlab/database/load_balancing/service_discovery_spec.rb' + - 'spec/gitlab/database/load_balancing/setup_spec.rb' + - 'spec/gitlab/database/load_balancing_spec.rb' + +Layout/LineLength: + Max: 129 diff --git a/gems/gitlab-database-load_balancing/.rubocop_todo.yml b/gems/gitlab-database-load_balancing/.rubocop_todo.yml new file mode 100644 index 0000000000000000000000000000000000000000..94be7317466be827329501d3cb252391fc9d204f --- /dev/null +++ b/gems/gitlab-database-load_balancing/.rubocop_todo.yml @@ -0,0 +1,42 @@ +Lint/AssignmentInCondition: + Exclude: + - 'lib/gitlab/database/load_balancing/configuration.rb' + - 'lib/gitlab/database/load_balancing/host.rb' + - 'lib/gitlab/database/load_balancing/load_balancer.rb' + +Lint/DuplicateBranch: + Exclude: + - 'lib/gitlab/database/load_balancing/load_balancer.rb' + +Lint/EmptyBlock: + Exclude: + - 'spec/gitlab/database/load_balancing/load_balancer_spec.rb' + +Lint/RedundantCopDisableDirective: + Exclude: + - 'lib/gitlab/database/load_balancing.rb' + +Lint/UnusedMethodArgument: + Exclude: + - 'lib/gitlab/database/load_balancing/connection_proxy.rb' + - 'lib/gitlab/database/load_balancing/primary_host.rb' + +Naming/RescuedExceptionsVariableName: + Exclude: + - 'lib/gitlab/database/load_balancing/load_balancer.rb' + - 'lib/gitlab/database/load_balancing/service_discovery.rb' + - 'spec/gitlab/database/load_balancing/host_spec.rb' + - 'spec/gitlab/database/load_balancing/load_balancer_spec.rb' + +Style/GuardClause: + Exclude: + - 'lib/gitlab/database/load_balancing/load_balancer.rb' + +Style/Lambda: + Exclude: + - 'lib/gitlab/database/load_balancing/action_cable_callbacks.rb' + +Style/RedundantSelf: + Exclude: + - 'spec/gitlab/database/load_balancing_spec.rb' + - 'lib/gitlab/database/load_balancing/service_discovery.rb' diff --git a/gems/gitlab-database-load_balancing/Gemfile b/gems/gitlab-database-load_balancing/Gemfile index 05b508b1333f6020af64dea09ec004a51151a153..8e1f07650a181fe352615e125dbd1e0e06236c9a 100644 --- a/gems/gitlab-database-load_balancing/Gemfile +++ b/gems/gitlab-database-load_balancing/Gemfile @@ -1,5 +1,9 @@ # frozen_string_literal: true +def next? + File.basename(__FILE__) == "Gemfile.next" +end + source "https://rubygems.org" # Specify your gem's dependencies in gitlab-safe_request_store.gemspec @@ -12,3 +16,9 @@ end gem 'activerecord-gitlab', path: '../activerecord-gitlab' gem 'gitlab-utils', path: '../gitlab-utils' gem 'gitlab-safe_request_store', path: '../gitlab-safe_request_store' + +if next? + gem 'rails', '~> 7.2' +else + gem 'rails', '~> 7.1.5.1' +end diff --git a/gems/gitlab-database-load_balancing/Gemfile.lock b/gems/gitlab-database-load_balancing/Gemfile.lock index 6fe6df1c82e89db433111bdfdac4569fdbac57c7..f9ae8f9022d1c5fc150598294e950679cb035a59 100644 --- a/gems/gitlab-database-load_balancing/Gemfile.lock +++ b/gems/gitlab-database-load_balancing/Gemfile.lock @@ -32,103 +32,132 @@ PATH remote: . specs: gitlab-database-load_balancing (0.1.0) - gitlab-net-dns (~> 0.9.2) - pg (~> 1.5.4) - rails (>= 7) + gitlab-net-dns (~> 0.12) + pg (~> 1.5.6) + rails (>= 7.1) GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8.7) - actionpack (= 7.0.8.7) - activesupport (= 7.0.8.7) + actioncable (7.1.5.1) + actionpack (= 7.1.5.1) + activesupport (= 7.1.5.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8.7) - actionpack (= 7.0.8.7) - activejob (= 7.0.8.7) - activerecord (= 7.0.8.7) - activestorage (= 7.0.8.7) - activesupport (= 7.0.8.7) + zeitwerk (~> 2.6) + actionmailbox (7.1.5.1) + actionpack (= 7.1.5.1) + activejob (= 7.1.5.1) + activerecord (= 7.1.5.1) + activestorage (= 7.1.5.1) + activesupport (= 7.1.5.1) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.8.7) - actionpack (= 7.0.8.7) - actionview (= 7.0.8.7) - activejob (= 7.0.8.7) - activesupport (= 7.0.8.7) + actionmailer (7.1.5.1) + actionpack (= 7.1.5.1) + actionview (= 7.1.5.1) + activejob (= 7.1.5.1) + activesupport (= 7.1.5.1) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp - rails-dom-testing (~> 2.0) - actionpack (7.0.8.7) - actionview (= 7.0.8.7) - activesupport (= 7.0.8.7) - rack (~> 2.0, >= 2.2.4) + rails-dom-testing (~> 2.2) + actionpack (7.1.5.1) + actionview (= 7.1.5.1) + activesupport (= 7.1.5.1) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.8.7) - actionpack (= 7.0.8.7) - activerecord (= 7.0.8.7) - activestorage (= 7.0.8.7) - activesupport (= 7.0.8.7) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.1.5.1) + actionpack (= 7.1.5.1) + activerecord (= 7.1.5.1) + activestorage (= 7.1.5.1) + activesupport (= 7.1.5.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8.7) - activesupport (= 7.0.8.7) + actionview (7.1.5.1) + activesupport (= 7.1.5.1) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.8.7) - activesupport (= 7.0.8.7) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.1.5.1) + activesupport (= 7.1.5.1) globalid (>= 0.3.6) - activemodel (7.0.8.7) - activesupport (= 7.0.8.7) - activerecord (7.0.8.7) - activemodel (= 7.0.8.7) - activesupport (= 7.0.8.7) - activestorage (7.0.8.7) - actionpack (= 7.0.8.7) - activejob (= 7.0.8.7) - activerecord (= 7.0.8.7) - activesupport (= 7.0.8.7) + activemodel (7.1.5.1) + activesupport (= 7.1.5.1) + activerecord (7.1.5.1) + activemodel (= 7.1.5.1) + activesupport (= 7.1.5.1) + timeout (>= 0.4.0) + activestorage (7.1.5.1) + actionpack (= 7.1.5.1) + activejob (= 7.1.5.1) + activerecord (= 7.1.5.1) + activesupport (= 7.1.5.1) marcel (~> 1.0) - mini_mime (>= 1.1.0) - activesupport (7.0.8.7) + activesupport (7.1.5.1) + base64 + benchmark (>= 0.3) + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) + mutex_m + securerandom (>= 0.3) tzinfo (~> 2.0) addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) + base64 (0.3.0) + benchmark (0.4.1) + bigdecimal (3.2.2) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) builder (3.2.4) coderay (1.1.3) concurrent-ruby (1.2.2) + connection_pool (2.5.3) crass (1.0.6) date (3.3.3) debug_inspector (1.1.0) diff-lcs (1.5.0) + drb (2.2.3) + erb (5.0.2) erubi (1.12.0) - gitlab-net-dns (0.9.2) - gitlab-styles (10.1.0) - rubocop (~> 1.50.2) - rubocop-graphql (~> 0.18) - rubocop-performance (~> 1.15) - rubocop-rails (~> 2.17) - rubocop-rspec (~> 2.22) + gitlab-net-dns (0.15.0) + logger + gitlab-styles (13.1.0) + rubocop (= 1.71.1) + rubocop-capybara (~> 2.21.0) + rubocop-factory_bot (~> 2.26.1) + rubocop-graphql (~> 1.5.4) + rubocop-performance (~> 1.21.1) + rubocop-rails (~> 2.26.0) + rubocop-rspec (~> 3.0.4) + rubocop-rspec_rails (~> 2.30.0) globalid (1.2.1) activesupport (>= 6.1) i18n (1.12.0) concurrent-ruby (~> 1.0) + io-console (0.8.1) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) json (2.6.3) + language_server-protocol (3.17.0.5) + logger (1.7.0) loofah (2.21.4) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -142,6 +171,7 @@ GEM mini_mime (1.1.5) mini_portile2 (2.8.7) minitest (5.17.0) + mutex_m (0.3.0) net-imap (0.4.4) date net-protocol @@ -168,10 +198,14 @@ GEM nokogiri (1.16.8-x86_64-linux) racc (~> 1.4) parallel (1.22.1) - parser (3.2.2.3) + parser (3.3.8.0) ast (~> 2.4.1) racc - pg (1.5.4) + pg (1.5.9) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + prism (1.4.0) proc_to_ast (0.1.0) coderay parser @@ -179,25 +213,33 @@ GEM pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) + psych (5.2.6) + date + stringio public_suffix (5.0.3) racc (1.8.1) rack (2.2.17) + rack-session (1.0.2) + rack (< 3) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.8.7) - actioncable (= 7.0.8.7) - actionmailbox (= 7.0.8.7) - actionmailer (= 7.0.8.7) - actionpack (= 7.0.8.7) - actiontext (= 7.0.8.7) - actionview (= 7.0.8.7) - activejob (= 7.0.8.7) - activemodel (= 7.0.8.7) - activerecord (= 7.0.8.7) - activestorage (= 7.0.8.7) - activesupport (= 7.0.8.7) + rackup (1.0.1) + rack (< 3) + webrick + rails (7.1.5.1) + actioncable (= 7.1.5.1) + actionmailbox (= 7.1.5.1) + actionmailer (= 7.1.5.1) + actionpack (= 7.1.5.1) + actiontext (= 7.1.5.1) + actionview (= 7.1.5.1) + activejob (= 7.1.5.1) + activemodel (= 7.1.5.1) + activerecord (= 7.1.5.1) + activestorage (= 7.1.5.1) + activesupport (= 7.1.5.1) bundler (>= 1.15.0) - railties (= 7.0.8.7) + railties (= 7.1.5.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -205,31 +247,36 @@ GEM rails-html-sanitizer (1.6.1) loofah (~> 2.21) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.0.8.7) - actionpack (= 7.0.8.7) - activesupport (= 7.0.8.7) - method_source + railties (7.1.5.1) + actionpack (= 7.1.5.1) + activesupport (= 7.1.5.1) + irb + rackup (>= 1.0.0) rake (>= 12.2) - thor (~> 1.0) - zeitwerk (~> 2.5) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.1.0) - regexp_parser (2.7.0) + rdoc (6.14.2) + erb + psych (>= 4.0.0) + regexp_parser (2.10.0) + reline (0.6.2) + io-console (~> 0.5) request_store (1.5.1) rack (>= 1.4) - rexml (3.3.9) - rspec (3.12.0) - rspec-core (~> 3.12.0) - rspec-expectations (~> 3.12.0) - rspec-mocks (~> 3.12.0) - rspec-core (3.12.1) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.2) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.3) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) + rspec-support (~> 3.13.0) rspec-parameterized (1.0.0) rspec-parameterized-core (< 2) rspec-parameterized-table_syntax (< 2) @@ -249,37 +296,42 @@ GEM rspec-expectations (~> 3.12) rspec-mocks (~> 3.12) rspec-support (~> 3.12) - rspec-support (3.12.0) - rubocop (1.50.2) + rspec-support (3.13.4) + rubocop (1.71.1) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.0.0) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.0, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.38.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.29.0) - parser (>= 3.2.1.0) - rubocop-capybara (2.18.0) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.46.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-capybara (2.21.0) rubocop (~> 1.41) - rubocop-factory_bot (2.23.1) - rubocop (~> 1.33) - rubocop-graphql (0.19.0) - rubocop (>= 0.87, < 2) - rubocop-performance (1.18.0) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) - rubocop-rails (2.20.2) + rubocop-factory_bot (2.26.1) + rubocop (~> 1.61) + rubocop-graphql (1.5.4) + rubocop (>= 1.50, < 2) + rubocop-performance (1.21.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.26.2) activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) - rubocop-rspec (2.22.0) - rubocop (~> 1.33) - rubocop-capybara (~> 2.17) - rubocop-factory_bot (~> 2.22) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rspec (3.0.5) + rubocop (~> 1.61) + rubocop-rspec_rails (2.30.0) + rubocop (~> 1.61) + rubocop-rspec (~> 3, >= 3.0.1) ruby-progressbar (1.11.0) + securerandom (0.4.1) + stringio (3.1.7) thor (1.3.0) timeout (0.4.0) tzinfo (2.0.6) @@ -288,6 +340,7 @@ GEM unparser (0.6.8) diff-lcs (~> 1.3) parser (>= 3.2.0) + webrick (1.9.1) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -307,49 +360,60 @@ DEPENDENCIES gitlab-database-load_balancing! gitlab-rspec! gitlab-safe_request_store! - gitlab-styles (~> 10.1.0) + gitlab-styles (~> 13.1.0) gitlab-utils! - pg (~> 1.5.4) + pg (~> 1.5.6) pry - rspec (~> 3.0) + rails (~> 7.1.5.1) + rspec (~> 3.13) rspec-parameterized (~> 1.0) rspec-rails (~> 6.0.1) - rubocop (~> 1.50) - rubocop-rspec (~> 2.22) + rubocop (~> 1.71.1) + rubocop-rspec (~> 3.0.4) CHECKSUMS - actioncable (7.0.8.7) sha256=4034513841df2fd09dbbf38f37c1a00fc6c841122a8714e5d6916b8d6ce2f162 - actionmailbox (7.0.8.7) sha256=940eeaa3d8e85dcd9fc6069e39571e13c5a4bdb0db52c7ab96d14da81d6ac1c2 - actionmailer (7.0.8.7) sha256=8be8f9a2f8774af89822bc92e1ab6df10b3a2be59c75486a34e86a1f10d88d14 - actionpack (7.0.8.7) sha256=40e6b1d687904a4fd2285d1fa3aad3d9a9d9ba8fd8858dd0faa9f4673c3f5e2c - actiontext (7.0.8.7) sha256=cb75d2db97d5b2c8caccdc0f643541df36c2c53f076a2d49b226f971d8d528a0 - actionview (7.0.8.7) sha256=be975bc9c61903fe5da80a97c345271159033bcbba63988c7f27b6b8b98f7fed - activejob (7.0.8.7) sha256=eff4db3aeaee34863a47570089d11d5577ed0ea42b1475dc9be6a413be182a20 - activemodel (7.0.8.7) sha256=f13b04bb055c1e85b965ce40b0a2e671b8d97835083597bc7fbc04cde0f40a83 - activerecord (7.0.8.7) sha256=f94fc8510e58a18e462c5ee8862c9be75e2bfad0688e8d022b86a6e05df2a45a + actioncable (7.1.5.1) sha256=764637b5b2d97b94e412d562c177bfd16b0fd769d55c98846362f5263e8aaa0d + actionmailbox (7.1.5.1) sha256=c3c20589fe43e6fa88bba2d76a6f9805ffdd02531f4a9a4af8197d59f5a5360a + actionmailer (7.1.5.1) sha256=b213d6d880b23b093ccfef3b4f87a3d27e4666442f71b5b634b2d19e19a49759 + actionpack (7.1.5.1) sha256=2bc263d9f43f16cc3b3360f59659ab11f140577602f371f1a968e2672b38d718 + actiontext (7.1.5.1) sha256=b8e261cfad5bc6a78b3f15be5e7c7f32190041b3dc6f027a3a353b4392d2f7ec + actionview (7.1.5.1) sha256=8c559a213501798e29b50b5341a643a70bbf6fa0aa2abaf571d0efc59dc4f6aa + activejob (7.1.5.1) sha256=7633376c857f4c491d06b5a7f5d86d9f07afc595398354a3f1abe80eb7e35767 + activemodel (7.1.5.1) sha256=74727466854a7fbdfe8f2702ca3112b23877500d4926bf7e02e921ad542191f1 + activerecord (7.1.5.1) sha256=f40ad1609bf33b9ba5bdc4e16d80a77b1517153234ceb413d31d635d7b91f1e3 activerecord-gitlab (0.2.0) - activestorage (7.0.8.7) sha256=ca411e73733a50983f44b0945bfd0612313beb3a8f914cd3a88e4fcd99399ef5 - activesupport (7.0.8.7) sha256=df4702375de924aae81709c831605317c5417f0bd9e502a0373ff84a067204ff + activestorage (7.1.5.1) sha256=ae6b8b076858c666eaad6f896d786b67654235e861e24a83f61f1cc97b43ff63 + activesupport (7.1.5.1) sha256=9f0c482e473b9868cb3dfe3e9db549a3bd2302c02e4f595a5caac144a8c7cfb8 addressable (2.8.5) sha256=63f0fbcde42edf116d6da98a9437f19dd1692152f1efa3fcc4741e443c772117 ast (2.4.2) sha256=1e280232e6a33754cde542bc5ef85520b74db2aac73ec14acef453784447cc12 + base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b + benchmark (0.4.1) sha256=d4ef40037bba27f03b28013e219b950b82bace296549ec15a78016552f8d2cce + bigdecimal (3.2.2) sha256=39085f76b495eb39a79ce07af716f3a6829bc35eb44f2195e2753749f2fa5adc binding_of_caller (1.0.0) sha256=3aad25d1d538fc6e7972978f9bf512ccd992784009947c81633bea776713161d builder (3.2.4) sha256=99caf08af60c8d7f3a6b004029c4c3c0bdaebced6c949165fe98f1db27fbbc10 coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b concurrent-ruby (1.2.2) sha256=3879119b8b75e3b62616acc256c64a134d0b0a7a9a3fcba5a233025bcde22c4f + connection_pool (2.5.3) sha256=cfd74a82b9b094d1ce30c4f1a346da23ee19dc8a062a16a85f58eab1ced4305b crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d date (3.3.3) sha256=819792019d5712b748fb15f6dfaaedef14b0328723ef23583ea35f186774530f debug_inspector (1.1.0) sha256=eaa5a2d0195e1d65fb4164e8e7e466cca2e7eb53bc5e608cf12b8bf02c3a8606 diff-lcs (1.5.0) sha256=49b934001c8c6aedb37ba19daec5c634da27b318a7a3c654ae979d6ba1929b67 + drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 + erb (5.0.2) sha256=d30f258143d4300fb4ecf430042ac12970c9bb4b33c974a545b8f58c1ec26c0f erubi (1.12.0) sha256=27bedb74dfb1e04ff60674975e182d8ca787f2224f2e8143268c7696f42e4723 gitlab-database-load_balancing (0.1.0) - gitlab-net-dns (0.9.2) sha256=f726d978479d43810819f12a45c0906d775a07e34df111bbe693fffbbef3059d + gitlab-net-dns (0.15.0) sha256=d229aae205055b86b2ad166981257eb589ce6d6a146aa79b3ea2b1e5d9741f46 gitlab-rspec (0.1.0) gitlab-safe_request_store (0.1.0) - gitlab-styles (10.1.0) sha256=f42745f5397d042fe24cf2d0eb56c995b37f9f43d8fb79b834d197a1cafdc84a + gitlab-styles (13.1.0) sha256=46c7c5729616355868b7b40a4ffcd052b36346076042abe8cafaee1688cbf2c1 gitlab-utils (0.1.0) globalid (1.2.1) sha256=70bf76711871f843dbba72beb8613229a49429d1866828476f9c9d6ccc327ce9 i18n (1.12.0) sha256=91e3cc1b97616d308707eedee413d82ee021d751c918661fb82152793e64aced + io-console (0.8.1) sha256=1e15440a6b2f67b6ea496df7c474ed62c860ad11237f29b3bd187f054b925fcb + irb (1.15.2) sha256=222f32952e278da34b58ffe45e8634bf4afc2dc7aa9da23fed67e581aa50fdba json (2.6.3) sha256=86aaea16adf346a2b22743d88f8dcceeb1038843989ab93cda44b5176c845459 + language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 loofah (2.21.4) sha256=2c18cd5db9fd92a48a270344e57a5b84cf22d668b78e1481f10e4f2cde4eb734 mail (2.8.1) sha256=ec3b9fadcf2b3755c78785cb17bc9a0ca9ee9857108a64b6f5cfc9c0b5bfc9ad marcel (1.0.2) sha256=a013b677ef46cbcb49fd5c59b3d35803d2ee04dd75d8bfdc43533fc5a31f7e4e @@ -357,6 +421,7 @@ CHECKSUMS mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef mini_portile2 (2.8.7) sha256=13eef5ab459bbfd33d61e539564ec25a9c2cf593b0a5ea6d4d7ef8c19b162ee0 minitest (5.17.0) sha256=c0dfaa3e99ed5ee3500c92bb114cf9d0d3c1e6995e162dd7b49970a9f0315ece + mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751 net-imap (0.4.4) sha256=7e61f6260343db3b49cee914f411aae81bc3ea768938112e805f9c329b59bce7 net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3 net-protocol (0.2.1) sha256=21adb19c197768899c389bd257545de9d5af64adb1928787653460c2699eac37 @@ -370,46 +435,57 @@ CHECKSUMS nokogiri (1.16.8-x86_64-darwin) sha256=6c40d7dc444f752634bf6ee8b53a55c3cfca3f9df52be46b8abcc559ccd49e47 nokogiri (1.16.8-x86_64-linux) sha256=ed7b1f80713ac968dd93fe2b96fc3df6e448b73bd02dd77d5fc89ba92a1ed6d9 parallel (1.22.1) sha256=ebdf1f0c51f182df38522f70ba770214940bef998cdb6e00f36492b29699761f - parser (3.2.2.3) sha256=10685f358ab36ffea2252dc4952e5b8fad3a297a8152a85f59adc982747b91eb - pg (1.5.4) sha256=04f7b247151c639a0b955d8e5a9a41541343f4640aa3c2bdf749a872c339d25d + parser (3.3.8.0) sha256=2476364142b307fa5a1b1ece44f260728be23858a9c71078e956131a75453c45 + pg (1.5.9) sha256=761efbdf73b66516f0c26fcbe6515dc7500c3f0aa1a1b853feae245433c64fdc + pp (0.6.2) sha256=947ec3120c6f92195f8ee8aa25a7b2c5297bb106d83b41baa02983686577b6ff + prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 + prism (1.4.0) sha256=dc0e3e00e93160213dc2a65519d9002a4a1e7b962db57d444cf1a71565bb703e proc_to_ast (0.1.0) sha256=92a73fa66e2250a83f8589f818b0751bcf227c68f85916202df7af85082f8691 pry (0.14.2) sha256=c4fe54efedaca1d351280b45b8849af363184696fcac1c72e0415f9bdac4334d + psych (5.2.6) sha256=814328aa5dcb6d604d32126a20bc1cbcf05521a5b49dbb1a8b30a07e580f316e public_suffix (5.0.3) sha256=337d475da2bd2ea1de0446751cb972ad43243b4b00aa8cf91cb904fa593d3259 racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f rack (2.2.17) sha256=5fe02a1ca80d6fb2271dba00985ee2962d6f5620b6f46dfed89f5301ac4699dd + rack-session (1.0.2) sha256=a02115e5420b4de036839b9811e3f7967d73446a554b42aa45106af335851d76 rack-test (2.1.0) sha256=0c61fc61904049d691922ea4bb99e28004ed3f43aa5cfd495024cc345f125dfb - rails (7.0.8.7) sha256=5e67ed4dd915746349bfb8c7ae2f531d3a36eb68fbe2f60ede02a0500715cded + rackup (1.0.1) sha256=ba86604a28989fe1043bff20d819b360944ca08156406812dca6742b24b3c249 + rails (7.1.5.1) sha256=05aea2ed7b6392b41ce0fc11455de118455025a431b6ea334a7ac2b101608804 rails-dom-testing (2.2.0) sha256=e515712e48df1f687a1d7c380fd7b07b8558faa26464474da64183a7426fa93b rails-html-sanitizer (1.6.1) sha256=e3d2fb10339f03b802e39c7f6cac28c54fd404d3f65ae39c31cca9d150c5cbf0 - railties (7.0.8.7) sha256=1ab985280b02bc4b176d36e1011148db600b763c646e3de88c02a665d864505f + railties (7.1.5.1) sha256=0be15562e2ded4efdc1b6c30f884b6d838c9ba49573dde042334b752b043e2fb rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a rake (13.1.0) sha256=be6a3e1aa7f66e6c65fa57555234eb75ce4cf4ada077658449207205474199c6 - regexp_parser (2.7.0) sha256=f8b8b7f34cc53c907fad6aec2b9da996a4311a0ddd92f3bfd3b999de5420c234 + rdoc (6.14.2) sha256=9fdd44df130f856ae70cc9a264dfd659b9b40de369b16581f4ab746e42439226 + regexp_parser (2.10.0) sha256=cb6f0ddde88772cd64bff1dbbf68df66d376043fe2e66a9ef77fcb1b0c548c61 + reline (0.6.2) sha256=1dad26a6008872d59c8e05244b119347c9f2ddaf4a53dce97856cd5f30a02846 request_store (1.5.1) sha256=07a204d161590789f2b1d27f9f0eadcdecd6d868cb2f03240250e1bc747df78e - rexml (3.3.9) sha256=d71875b85299f341edf47d44df0212e7658cbdf35aeb69cefdb63f57af3137c9 - rspec (3.12.0) sha256=ccc41799a43509dc0be84070e3f0410ac95cbd480ae7b6c245543eb64162399c - rspec-core (3.12.1) sha256=2e40c265f71eeb7caa4cac57106a715d2cd9caddc550bd9a4e632f4a372b4435 - rspec-expectations (3.12.2) sha256=8652db70b25ae3378b7274477a906b6ad1833a7b7cfbb001a03f49dd1c1d6a0d - rspec-mocks (3.12.3) sha256=cc0a1176707e641a2c66c71fe769486fec57d7df8ec7e34320f8957a1363026b + rspec (3.13.1) sha256=b9f9a58fa915b8d94a1d6b3195fe6dd28c4c34836a6097015142c4a9ace72140 + rspec-core (3.13.5) sha256=ab3f682897c6131c67f9a17cfee5022a597f283aebe654d329a565f9937a4fa3 + rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 + rspec-mocks (3.13.5) sha256=e4338a6f285ada9fe56f5893f5457783af8194f5d08884d17a87321d5195ea81 rspec-parameterized (1.0.0) sha256=9c07b043c72afbd23dd9a1dd48c06f46bc2fb1a6d875c6703e254932ba28b386 rspec-parameterized-core (1.0.0) sha256=287b494985e79821160af63aba4f91db8dbfa9a21cb200db34ba38f40e16ccc1 rspec-parameterized-table_syntax (1.0.1) sha256=ffead8f21f0711b3cdf8b74386f2ef7ac93b39c40b60658a5eda97072580f2fc rspec-rails (6.0.3) sha256=6d1812cfaf18dba5a08d7e30c85149b24a220fae064853a96e451376be6fd820 - rspec-support (3.12.0) sha256=dd4d44b247ff679b95b5607ac5641d197a5f9b1d33f916123cb98fc5f917c58b - rubocop (1.50.2) sha256=7cfeb0616f686ac61d049beae89f31446792d7e9f5728152657548f70aa78650 - rubocop-ast (1.29.0) sha256=d1da2ab279a074baefc81758ac430c5768a8da8c7438dd4e5819ce5984d00ba1 - rubocop-capybara (2.18.0) sha256=66b256755101f76dc455ba9694e2414bc957db5200401d204b00bc835401d605 - rubocop-factory_bot (2.23.1) sha256=c19ee30c02e591f4293c07e943e22b7999c545d5010aac4d79621ee310850c4f - rubocop-graphql (0.19.0) sha256=ba4b2fc91c9f0fda47e0870a6ae15a1e5525d6caffcb150dc88b00caaacc3e43 - rubocop-performance (1.18.0) sha256=4c9d74f1b5bfaffb5b1cdb843279364198ac804e2644ae194615834dd011e02e - rubocop-rails (2.20.2) sha256=d20cbd613900fa22bcf85a7fba78ab68b21fc4f90b1e73c97284d40674332417 - rubocop-rspec (2.22.0) sha256=2d7493222c81c78ad304ddd81aaf64b3543bcfac6d3d8706c220331921753a03 + rspec-support (3.13.4) sha256=184b1814f6a968102b57df631892c7f1990a91c9a3b9e80ef892a0fc2a71a3f7 + rubocop (1.71.1) sha256=d3dfd1e484a3a619dcf76c6a4fba694cd833921e4fd254d111845c26bcecfcfa + rubocop-ast (1.46.0) sha256=0da7f6ad5b98614f89b74f11873c191059c823eae07d6ffd40a42a3338f2232b + rubocop-capybara (2.21.0) sha256=5d264efdd8b6c7081a3d4889decf1451a1cfaaec204d81534e236bc825b280ab + rubocop-factory_bot (2.26.1) sha256=8de13cd4edcee5ca800f255188167ecef8dbfc3d1fae9f15734e9d2e755392aa + rubocop-graphql (1.5.4) sha256=2d888d40b08577daf1e74ca4623be1e3058c1a93543d5a7220818f561a254192 + rubocop-performance (1.21.1) sha256=5cf20002a544275ad6aa99abca4b945d2a2ed71be925c38fe83700360ed8734e + rubocop-rails (2.26.2) sha256=f5561a09d6afd2f54316f3f0f6057338ca55b6c24a25ba6a938d3ed0fded84ad + rubocop-rspec (3.0.5) sha256=c6a8e29fb1b00d227c32df159e92f5ebb9e0ff734e52955fb13aff5c74977e0f + rubocop-rspec_rails (2.30.0) sha256=888112e83f9d7ef7ad2397e9d69a0b9614a4bae24f072c399804a180f80c4c46 ruby-progressbar (1.11.0) sha256=cc127db3866dc414ffccbf92928a241e585b3aa2b758a5563e74a6ee0f57d50a + securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 + stringio (3.1.7) sha256=5b78b7cb242a315fb4fca61a8255d62ec438f58da2b90be66048546ade4507fa thor (1.3.0) sha256=1adc7f9e5b3655a68c71393fee8bd0ad088d14ee8e83a0b73726f23cbb3ca7c3 timeout (0.4.0) sha256=cd6d1f3e83594a90ac1f3de8235399bff87112d97fec928ee2b77de240dd2cb5 tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b unicode-display_width (2.4.2) sha256=6a10205d1a19ca790c4e53064ba93f09d9eb234bf6bd135d9deb6001c21428be unparser (0.6.8) sha256=38262636be6aed919586eca4334140eb08c06bc5978e66e4693103384733c13c + webrick (1.9.1) sha256=b42d3c94f166f3fb73d87e9b359def9b5836c426fc8beacf38f2184a21b2a989 websocket-driver (0.7.6) sha256=f69400be7bc197879726ad8e6f5869a61823147372fd8928836a53c2c741d0db websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241 zeitwerk (2.6.12) sha256=561e12975d0332fd3b62cc859aff3bab432e5f320689c8a10cd4674b5c0439be diff --git a/gems/gitlab-database-load_balancing/Gemfile.next b/gems/gitlab-database-load_balancing/Gemfile.next new file mode 120000 index 0000000000000000000000000000000000000000..6ab79009c0a374c4a9009595cb57f46476f36b38 --- /dev/null +++ b/gems/gitlab-database-load_balancing/Gemfile.next @@ -0,0 +1 @@ +Gemfile \ No newline at end of file diff --git a/gems/gitlab-database-load_balancing/Gemfile.next.lock b/gems/gitlab-database-load_balancing/Gemfile.next.lock new file mode 100644 index 0000000000000000000000000000000000000000..73660a0525d681da5145c733d7c2709016b05d4a --- /dev/null +++ b/gems/gitlab-database-load_balancing/Gemfile.next.lock @@ -0,0 +1,470 @@ +PATH + remote: ../activerecord-gitlab + specs: + activerecord-gitlab (0.2.0) + activerecord (>= 7) + +PATH + remote: ../gitlab-rspec + specs: + gitlab-rspec (0.1.0) + activerecord (>= 6.1, < 8) + activesupport (>= 6.1, < 8) + rspec (~> 3.0) + +PATH + remote: ../gitlab-safe_request_store + specs: + gitlab-safe_request_store (0.1.0) + rack (~> 2.2.8) + request_store + +PATH + remote: ../gitlab-utils + specs: + gitlab-utils (0.1.0) + actionview (>= 6.1.7.2) + activesupport (>= 6.1.7.2) + addressable (~> 2.8) + rake (~> 13.0) + +PATH + remote: . + specs: + gitlab-database-load_balancing (0.1.0) + gitlab-net-dns (~> 0.12) + pg (~> 1.5.6) + rails (>= 7.1) + +GEM + remote: https://rubygems.org/ + specs: + actioncable (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + actionmailer (7.2.2.1) + actionpack (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.2.1) + actionview (= 7.2.2.1) + activesupport (= 7.2.2.1) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.2) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.2.1) + actionpack (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.2.2.1) + activesupport (= 7.2.2.1) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.2.1) + activesupport (= 7.2.2.1) + globalid (>= 0.3.6) + activemodel (7.2.2.1) + activesupport (= 7.2.2.1) + activerecord (7.2.2.1) + activemodel (= 7.2.2.1) + activesupport (= 7.2.2.1) + timeout (>= 0.4.0) + activestorage (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activesupport (= 7.2.2.1) + marcel (~> 1.0) + activesupport (7.2.2.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + ast (2.4.3) + base64 (0.3.0) + benchmark (0.4.1) + bigdecimal (3.2.2) + binding_of_caller (1.0.1) + debug_inspector (>= 1.2.0) + builder (3.3.0) + coderay (1.1.3) + concurrent-ruby (1.3.5) + connection_pool (2.5.3) + crass (1.0.6) + date (3.4.1) + debug_inspector (1.2.0) + diff-lcs (1.6.2) + drb (2.2.3) + erb (5.0.2) + erubi (1.13.1) + gitlab-net-dns (0.15.0) + logger + gitlab-styles (13.1.0) + rubocop (= 1.71.1) + rubocop-capybara (~> 2.21.0) + rubocop-factory_bot (~> 2.26.1) + rubocop-graphql (~> 1.5.4) + rubocop-performance (~> 1.21.1) + rubocop-rails (~> 2.26.0) + rubocop-rspec (~> 3.0.4) + rubocop-rspec_rails (~> 2.30.0) + globalid (1.2.1) + activesupport (>= 6.1) + i18n (1.14.7) + concurrent-ruby (~> 1.0) + io-console (0.8.1) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + json (2.13.0) + language_server-protocol (3.17.0.5) + logger (1.7.0) + loofah (2.24.1) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) + method_source (1.1.0) + mini_mime (1.1.5) + mini_portile2 (2.8.9) + minitest (5.25.5) + net-imap (0.5.9) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.1) + net-protocol + nio4r (2.7.4) + nokogiri (1.18.9) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + parallel (1.27.0) + parser (3.3.8.0) + ast (~> 2.4.1) + racc + pg (1.5.9) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + prism (1.4.0) + proc_to_ast (0.2.0) + parser + rouge + unparser + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + psych (5.2.6) + date + stringio + public_suffix (6.0.2) + racc (1.8.1) + rack (2.2.17) + rack-session (1.0.2) + rack (< 3) + rack-test (2.2.0) + rack (>= 1.3) + rackup (1.0.1) + rack (< 3) + webrick + rails (7.2.2.1) + actioncable (= 7.2.2.1) + actionmailbox (= 7.2.2.1) + actionmailer (= 7.2.2.1) + actionpack (= 7.2.2.1) + actiontext (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activemodel (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + bundler (>= 1.15.0) + railties (= 7.2.2.1) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rainbow (3.1.1) + rake (13.3.0) + rdoc (6.14.2) + erb + psych (>= 4.0.0) + regexp_parser (2.10.0) + reline (0.6.2) + io-console (~> 0.5) + request_store (1.7.0) + rack (>= 1.4) + rouge (4.6.0) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-parameterized (1.0.2) + rspec-parameterized-core (< 2) + rspec-parameterized-table_syntax (< 2) + rspec-parameterized-core (1.0.1) + parser + proc_to_ast (>= 0.2.0) + rspec (>= 2.13, < 4) + unparser + rspec-parameterized-table_syntax (1.0.1) + binding_of_caller + rspec-parameterized-core (< 2) + rspec-rails (6.0.4) + actionpack (>= 6.1) + activesupport (>= 6.1) + railties (>= 6.1) + rspec-core (~> 3.12) + rspec-expectations (~> 3.12) + rspec-mocks (~> 3.12) + rspec-support (~> 3.12) + rspec-support (3.13.4) + rubocop (1.71.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.38.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.46.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-capybara (2.21.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.26.1) + rubocop (~> 1.61) + rubocop-graphql (1.5.4) + rubocop (>= 1.50, < 2) + rubocop-performance (1.21.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.26.2) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rspec (3.0.5) + rubocop (~> 1.61) + rubocop-rspec_rails (2.30.0) + rubocop (~> 1.61) + rubocop-rspec (~> 3, >= 3.0.1) + ruby-progressbar (1.13.0) + securerandom (0.4.1) + stringio (3.1.7) + thor (1.4.0) + timeout (0.4.3) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + unparser (0.8.0) + diff-lcs (~> 1.6) + parser (>= 3.3.0) + prism (>= 1.4) + useragent (0.16.11) + webrick (1.9.1) + websocket-driver (0.8.0) + base64 + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + zeitwerk (2.7.3) + +PLATFORMS + ruby + +DEPENDENCIES + activerecord-gitlab! + gitlab-database-load_balancing! + gitlab-rspec! + gitlab-safe_request_store! + gitlab-styles (~> 13.1.0) + gitlab-utils! + pg (~> 1.5.6) + pry + rails (~> 7.2) + rspec (~> 3.13) + rspec-parameterized (~> 1.0) + rspec-rails (~> 6.0.1) + rubocop (~> 1.71.1) + rubocop-rspec (~> 3.0.4) + +CHECKSUMS + actioncable (7.2.2.1) sha256=5b3b885075a80767d63cbf2b586cbf82466a241675b7985233f957abb01bffb4 + actionmailbox (7.2.2.1) sha256=896a47c2520f4507c75dde67c6ea1f5eec3a041fe7bfbf3568c4e0149a080e25 + actionmailer (7.2.2.1) sha256=b02ae523c32c8ad762d4db941e76f3c108c106030132247ee7a7b8c86bc7b21f + actionpack (7.2.2.1) sha256=17b2160a7bcbd5a569d06b1ae54a4bb5ccc7ba0815d73ff5768100a79dc1f734 + actiontext (7.2.2.1) sha256=f369cee41a6674b697bf9257d917a3dce575a2c89935af437b432d6737a3f0d6 + actionview (7.2.2.1) sha256=69fc880cf3d8b1baf21b048cf7bb68f1eef08760ff8104d7d60a6a1be8b359a5 + activejob (7.2.2.1) sha256=f2f95a8573b394aa4f7c24843f0c4a6065c073a5c64d6f15ecd98d98c2c23e5b + activemodel (7.2.2.1) sha256=8398861f9ee2c4671a8357ab39e9b38a045fd656f6685a3dd5890c2419dbfdaf + activerecord (7.2.2.1) sha256=79a31f71c32d5138717c2104e0ff105f5d82922247c85bdca144f2720e67fab9 + activerecord-gitlab (0.2.0) + activestorage (7.2.2.1) sha256=b4ec35ff94d4d6656ee6952ce439c3f80e249552d49fd2d3996ee53880c5525f + activesupport (7.2.2.1) sha256=842bcbf8a92977f80fb4750661a237cf5dd4fdd442066b3c35e88afb488647f5 + addressable (2.8.7) sha256=462986537cf3735ab5f3c0f557f14155d778f4b43ea4f485a9deb9c8f7c58232 + ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 + base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b + benchmark (0.4.1) sha256=d4ef40037bba27f03b28013e219b950b82bace296549ec15a78016552f8d2cce + bigdecimal (3.2.2) sha256=39085f76b495eb39a79ce07af716f3a6829bc35eb44f2195e2753749f2fa5adc + binding_of_caller (1.0.1) sha256=2b2902abff4246ddcfbc4da9b69bc4a019e22aeb300c2ff6289a173d4b90b29a + builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f + coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b + concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6 + connection_pool (2.5.3) sha256=cfd74a82b9b094d1ce30c4f1a346da23ee19dc8a062a16a85f58eab1ced4305b + crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d + date (3.4.1) sha256=bf268e14ef7158009bfeaec40b5fa3c7271906e88b196d958a89d4b408abe64f + debug_inspector (1.2.0) sha256=9bdfa02eebc3da163833e6a89b154084232f5766087e59573b70521c77ea68a2 + diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962 + drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 + erb (5.0.2) sha256=d30f258143d4300fb4ecf430042ac12970c9bb4b33c974a545b8f58c1ec26c0f + erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9 + gitlab-database-load_balancing (0.1.0) + gitlab-net-dns (0.15.0) sha256=d229aae205055b86b2ad166981257eb589ce6d6a146aa79b3ea2b1e5d9741f46 + gitlab-rspec (0.1.0) + gitlab-safe_request_store (0.1.0) + gitlab-styles (10.1.0) sha256=f42745f5397d042fe24cf2d0eb56c995b37f9f43d8fb79b834d197a1cafdc84a + gitlab-utils (0.1.0) + globalid (1.2.1) sha256=70bf76711871f843dbba72beb8613229a49429d1866828476f9c9d6ccc327ce9 + i18n (1.14.7) sha256=ceba573f8138ff2c0915427f1fc5bdf4aa3ab8ae88c8ce255eb3ecf0a11a5d0f + io-console (0.8.1) sha256=1e15440a6b2f67b6ea496df7c474ed62c860ad11237f29b3bd187f054b925fcb + irb (1.15.2) sha256=222f32952e278da34b58ffe45e8634bf4afc2dc7aa9da23fed67e581aa50fdba + json (2.12.2) sha256=ba94a48ad265605c8fa9a50a5892f3ba6a02661aa010f638211f3cb36f44abf4 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + loofah (2.24.1) sha256=655a30842b70ec476410b347ab1cd2a5b92da46a19044357bbd9f401b009a337 + mail (2.8.1) sha256=ec3b9fadcf2b3755c78785cb17bc9a0ca9ee9857108a64b6f5cfc9c0b5bfc9ad + marcel (1.0.4) sha256=0d5649feb64b8f19f3d3468b96c680bae9746335d02194270287868a661516a4 + method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5 + mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef + mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289 + minitest (5.25.5) sha256=391b6c6cb43a4802bfb7c93af1ebe2ac66a210293f4a3fb7db36f2fc7dc2c756 + net-imap (0.5.9) sha256=d95905321e1bd9f294ffc7ff8697be218eee1ec96c8504c0960964d0a0be33fc + net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3 + net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8 + net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736 + nio4r (2.7.4) sha256=d95dee68e0bb251b8ff90ac3423a511e3b784124e5db7ff5f4813a220ae73ca9 + nokogiri (1.18.8) sha256=8c7464875d9ca7f71080c24c0db7bcaa3940e8be3c6fc4bcebccf8b9a0016365 + parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 + parser (3.3.8.0) sha256=2476364142b307fa5a1b1ece44f260728be23858a9c71078e956131a75453c45 + pg (1.5.9) sha256=761efbdf73b66516f0c26fcbe6515dc7500c3f0aa1a1b853feae245433c64fdc + pp (0.6.2) sha256=947ec3120c6f92195f8ee8aa25a7b2c5297bb106d83b41baa02983686577b6ff + prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 + prism (1.4.0) sha256=dc0e3e00e93160213dc2a65519d9002a4a1e7b962db57d444cf1a71565bb703e + proc_to_ast (0.2.0) sha256=4bb446419c3878c21d8792f8a129616690168f636b9e460b5a0ed26dd6680bbe + pry (0.15.2) sha256=12d54b8640d3fa29c9211dd4ffb08f3fd8bf7a4fd9b5a73ce5b59c8709385b6b + psych (5.2.6) sha256=814328aa5dcb6d604d32126a20bc1cbcf05521a5b49dbb1a8b30a07e580f316e + public_suffix (6.0.2) sha256=bfa7cd5108066f8c9602e0d6d4114999a5df5839a63149d3e8b0f9c1d3558394 + racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f + rack (2.2.17) sha256=5fe02a1ca80d6fb2271dba00985ee2962d6f5620b6f46dfed89f5301ac4699dd + rack-session (1.0.2) sha256=a02115e5420b4de036839b9811e3f7967d73446a554b42aa45106af335851d76 + rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463 + rackup (1.0.1) sha256=ba86604a28989fe1043bff20d819b360944ca08156406812dca6742b24b3c249 + rails (7.2.2.1) sha256=aedb1604b40f4e43b5e8066e5a1aa34dae02c33aa9669b21fd4497d0f8c9bb40 + rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d + rails-html-sanitizer (1.6.2) sha256=35fce2ca8242da8775c83b6ba9c1bcaad6751d9eb73c1abaa8403475ab89a560 + railties (7.2.2.1) sha256=e3f11bf116dd6d0d874522843ccc70ec0f89fbfed3e9c2ee48a4778cd042fe1f + rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a + rake (13.3.0) sha256=96f5092d786ff412c62fde76f793cc0541bd84d2eb579caa529aa8a059934493 + rdoc (6.14.2) sha256=9fdd44df130f856ae70cc9a264dfd659b9b40de369b16581f4ab746e42439226 + regexp_parser (2.10.0) sha256=cb6f0ddde88772cd64bff1dbbf68df66d376043fe2e66a9ef77fcb1b0c548c61 + reline (0.6.1) sha256=1afcc9d7cb1029cdbe780d72f2f09251ce46d3780050f3ec39c3ccc6b60675fb + request_store (1.7.0) sha256=e1b75d5346a315f452242a68c937ef8e48b215b9453a77a6c0acdca2934c88cb + rexml (3.4.1) sha256=c74527a9a0a04b4ec31dbe0dc4ed6004b960af943d8db42e539edde3a871abca + rouge (4.5.2) sha256=034233fb8a69d0ad0e0476943184e04cb971b68e3c2239724e02f428878b68a3 + rspec (3.13.1) sha256=b9f9a58fa915b8d94a1d6b3195fe6dd28c4c34836a6097015142c4a9ace72140 + rspec-core (3.13.5) sha256=ab3f682897c6131c67f9a17cfee5022a597f283aebe654d329a565f9937a4fa3 + rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 + rspec-mocks (3.13.5) sha256=e4338a6f285ada9fe56f5893f5457783af8194f5d08884d17a87321d5195ea81 + rspec-parameterized (1.0.2) sha256=b456dec0091924175ac13963e173cdbaa2ab3e1581a405a948addc34e3f3f4c2 + rspec-parameterized-core (1.0.1) sha256=31819939c433f58b59f83d1bcd08697ca74ea7e44e2a207f7445934f88461aab + rspec-parameterized-table_syntax (1.0.1) sha256=ffead8f21f0711b3cdf8b74386f2ef7ac93b39c40b60658a5eda97072580f2fc + rspec-rails (6.0.4) sha256=60bbfd7ec06124d0cd3eedcdf1ce31b6faa58bfdd0413ccbadd9db84a168ae0d + rspec-support (3.13.4) sha256=184b1814f6a968102b57df631892c7f1990a91c9a3b9e80ef892a0fc2a71a3f7 + rubocop (1.50.2) sha256=7cfeb0616f686ac61d049beae89f31446792d7e9f5728152657548f70aa78650 + rubocop-ast (1.46.0) sha256=0da7f6ad5b98614f89b74f11873c191059c823eae07d6ffd40a42a3338f2232b + rubocop-capybara (2.21.0) sha256=5d264efdd8b6c7081a3d4889decf1451a1cfaaec204d81534e236bc825b280ab + rubocop-factory_bot (2.26.0) sha256=17593a8dd296867c906a33946d91e69232c9a3084c92933f5109d23e0451a37f + rubocop-graphql (0.19.0) sha256=ba4b2fc91c9f0fda47e0870a6ae15a1e5525d6caffcb150dc88b00caaacc3e43 + rubocop-performance (1.23.1) sha256=f22f86a795f5e6a6180aac2c6fc172534b173a068d6ed3396d6460523e051b82 + rubocop-rails (2.25.1) sha256=4988933ee02fdb213d22d0f61dc57d6c582317b43867d5106ea1c7a628aae6a5 + rubocop-rspec (2.31.0) sha256=2bae19388d78e1ceace44cd95fd34f3209f4ef20cac1b168d0a1325cbba3d672 + rubocop-rspec_rails (2.29.0) sha256=31911e8a2de980394b707e0861f306666c22bb9cbec0a63917a5a178c21a31a1 + ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 + securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 + stringio (3.1.7) sha256=5b78b7cb242a315fb4fca61a8255d62ec438f58da2b90be66048546ade4507fa + thor (1.3.2) sha256=eef0293b9e24158ccad7ab383ae83534b7ad4ed99c09f96f1a6b036550abbeda + timeout (0.4.3) sha256=9509f079b2b55fe4236d79633bd75e34c1c1e7e3fb4b56cb5fda61f80a0fe30e + tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b + unicode-display_width (2.6.0) sha256=12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a + unparser (0.8.0) sha256=7e199512cfdf7683d51a5060d4668988f6c5659a0904f5fae89a55b3cbd548c1 + useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844 + webrick (1.9.1) sha256=b42d3c94f166f3fb73d87e9b359def9b5836c426fc8beacf38f2184a21b2a989 + websocket-driver (0.8.0) sha256=ed0dba4b943c22f17f9a734817e808bc84cdce6a7e22045f5315aa57676d4962 + websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241 + zeitwerk (2.7.3) sha256=b2e86b4a9b57d26ba68a15230dcc7fe6f040f06831ce64417b0621ad96ba3e85 + +BUNDLED WITH + 2.6.9 diff --git a/gems/gitlab-database-load_balancing/README.md b/gems/gitlab-database-load_balancing/README.md index e1a963dba5087cc767dc11a29c5817e7234d22a4..fb05fa373fe9dd1bf70a3c5b063aebc7992cebdd 100644 --- a/gems/gitlab-database-load_balancing/README.md +++ b/gems/gitlab-database-load_balancing/README.md @@ -1,3 +1,3 @@ # GitLab Database Load Balancing -This gem is a stub for a move of all `Gitlab::Database::LoadBalancing` code. +TODO diff --git a/gems/gitlab-database-load_balancing/gitlab-database-load_balancing.gemspec b/gems/gitlab-database-load_balancing/gitlab-database-load_balancing.gemspec index aefcf205b4a76aa6d7e669f70634538a2652ed51..17e71abac0e521dd2bbc5d0033ae2eef556cbac7 100644 --- a/gems/gitlab-database-load_balancing/gitlab-database-load_balancing.gemspec +++ b/gems/gitlab-database-load_balancing/gitlab-database-load_balancing.gemspec @@ -16,16 +16,16 @@ Gem::Specification.new do |spec| spec.files = Dir['lib/**/*.rb'] spec.require_paths = ["lib"] - spec.add_runtime_dependency 'gitlab-net-dns', '~> 0.9.2' - spec.add_runtime_dependency "pg", '~> 1.5.4' - spec.add_runtime_dependency 'rails', '>= 7' + spec.add_dependency 'gitlab-net-dns', '~> 0.12' + spec.add_dependency "pg", '~> 1.5.6' + spec.add_dependency 'rails', '>= 7.1' - spec.add_development_dependency "gitlab-styles", "~> 10.1.0" - spec.add_development_dependency "pg", '~> 1.5.4' + spec.add_development_dependency "gitlab-styles", "~> 13.1.0" + spec.add_development_dependency "pg", '~> 1.5.6' spec.add_development_dependency "pry" - spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "rspec", "~> 3.13" spec.add_development_dependency "rspec-parameterized", "~> 1.0" spec.add_development_dependency "rspec-rails", "~> 6.0.1" - spec.add_development_dependency "rubocop", "~> 1.50" - spec.add_development_dependency "rubocop-rspec", "~> 2.22" + spec.add_development_dependency "rubocop", "~> 1.71.1" + spec.add_development_dependency "rubocop-rspec", "~> 3.0.4" end diff --git a/lib/gitlab/database/load_balancing.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing.rb similarity index 55% rename from lib/gitlab/database/load_balancing.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing.rb index bc4d33291039ff753dedb381176156473b72de38..328744f3fcaf9e1dd2209b31a572e727b09b5738 100644 --- a/lib/gitlab/database/load_balancing.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing.rb @@ -1,5 +1,15 @@ # frozen_string_literal: true +require 'pg' +require 'active_support/time_with_zone' +require 'active_record' +require 'active_record/database_configurations' +require 'active_job' + +Dir['load_balancing/**/*.rb', base: File.dirname(__FILE__)].each do |file| + require_relative file +end + module Gitlab module Database module LoadBalancing @@ -18,8 +28,13 @@ module LoadBalancing ActiveRecord::ConnectionNotEstablished ].freeze - def self.base_models - @base_models ||= ::Gitlab::Database.database_base_models_using_load_balancing.values.freeze + mattr_accessor :base_models + mattr_accessor :all_database_names + mattr_accessor :default_pool_size + mattr_accessor :enabled, default: true + + def self.configure! + yield(self) end def self.each_load_balancer @@ -53,7 +68,7 @@ def self.release_hosts def self.db_role_for_connection(connection) return ROLE_UNKNOWN if connection.is_a?(::Gitlab::Database::LoadBalancing::ConnectionProxy) - db_config = Database.db_config_for_connection(connection) + db_config = db_config_for_connection(connection) return ROLE_UNKNOWN unless db_config if db_config.name.ends_with?(LoadBalancer::REPLICA_SUFFIX) @@ -62,6 +77,30 @@ def self.db_role_for_connection(connection) ROLE_PRIMARY end end + + def self.db_config_for_connection(connection) + return unless connection + + # For a ConnectionProxy we want to avoid ambiguous db_config as it may + # sometimes default to replica so we always return the primary config + # instead. + if connection.is_a?(::Gitlab::Database::LoadBalancing::ConnectionProxy) + return connection.load_balancer.configuration.db_config + end + + # During application init we might receive `NullPool` + return unless connection.respond_to?(:pool) && + connection.pool.respond_to?(:db_config) + + db_config = connection.pool.db_config + db_config unless empty_config?(db_config) + end + + def self.empty_config?(db_config) + return true unless db_config + + db_config.is_a?(ActiveRecord::ConnectionAdapters::NullPool::NullConfig) + end end end end diff --git a/lib/gitlab/database/load_balancing/action_cable_callbacks.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/action_cable_callbacks.rb similarity index 100% rename from lib/gitlab/database/load_balancing/action_cable_callbacks.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/action_cable_callbacks.rb diff --git a/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/callbacks.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/callbacks.rb new file mode 100644 index 0000000000000000000000000000000000000000..3528079ecf4ba8c0b1ea608e41826c7c2530f688 --- /dev/null +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/callbacks.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Gitlab + module Database + module LoadBalancing + module Callbacks + mattr_accessor :logger_proc + mattr_accessor :metrics_host_gauge_proc, :track_exception_proc + mattr_accessor :set_wal_for_proc, :get_wal_for_proc, :del_wal_for_proc + mattr_accessor :check_feature_flag + + NULL_LOGGER = Logger.new(File::NULL) + + def self.configure! + yield(self) + end + + def self.set_wal_for(key, wal, ex:) + set_wal_for_proc&.call(key, wal, ex: ex) + end + + def self.get_wal_for(key) + get_wal_for_proc&.call(key) + end + + def self.del_wal_for(key) + del_wal_for_proc&.call(key) + end + + def self.logger + logger_proc&.call || NULL_LOGGER + end + + def self.metrics_host_gauge(labels, value) + metrics_host_gauge_proc&.call(labels, value) + end + + def self.track_exception(ex) + track_exception_proc&.call(ex) + end + + def self.feature_enabled?(key, thing = nil, type: nil) + unless key.to_s.start_with?("load_balancer_") || key.to_s == 'log_database_sticking_operations' + raise "Unsupported feature flag #{key}" + end + + check_feature_flag&.call(key, thing, type: type) + end + end + end + end +end diff --git a/lib/gitlab/database/load_balancing/configuration.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/configuration.rb similarity index 91% rename from lib/gitlab/database/load_balancing/configuration.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/configuration.rb index 15adf0dbf24d6be94ae3ca80a41839aaa52cc415..1167c843ec1b727ddef02c865866f7f09e80304f 100644 --- a/lib/gitlab/database/load_balancing/configuration.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/configuration.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'active_support/core_ext/numeric/bytes' + module Gitlab module Database module LoadBalancing @@ -35,7 +37,7 @@ def self.for_model(model) # We iterate over the known/default keys so we don't end up with # random keys in our configuration hash. - config.service_discovery.each do |key, _| + config.service_discovery.each_key do |key| if (value = discover[key]) config.service_discovery[key] = value end @@ -82,7 +84,7 @@ def pool_size # To support this scenario, we always attempt to read the pool size # from the model's configuration. @model.connection_db_config.configuration_hash[:pool] || - Database.default_pool_size + Gitlab::Database::LoadBalancing.default_pool_size end # Returns `true` if the use of load balancing replicas should be @@ -91,7 +93,7 @@ def pool_size # This is disabled for Rake tasks to ensure e.g. database migrations # always produce consistent results. def load_balancing_enabled? - return false if Gitlab::Runtime.rake? + return false unless Gitlab::Database::LoadBalancing.enabled hosts.any? || service_discovery_enabled? end @@ -99,7 +101,7 @@ def load_balancing_enabled? # This is disabled for Rake tasks to ensure e.g. database migrations # always produce consistent results. def service_discovery_enabled? - return false if Gitlab::Runtime.rake? + return false unless Gitlab::Database::LoadBalancing.enabled service_discovery[:record].present? end diff --git a/lib/gitlab/database/load_balancing/connection_proxy.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/connection_proxy.rb similarity index 99% rename from lib/gitlab/database/load_balancing/connection_proxy.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/connection_proxy.rb index fc54353ee88afeea131528ee89bafd706775fb2b..534b02c096dae800382fd830fc393bbc7c73ce08 100644 --- a/lib/gitlab/database/load_balancing/connection_proxy.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/connection_proxy.rb @@ -149,3 +149,5 @@ def read_only_transaction? end end end + +# rubocop:enable GitlabSecurity/PublicSend diff --git a/lib/gitlab/database/load_balancing/host.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/host.rb similarity index 91% rename from lib/gitlab/database/load_balancing/host.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/host.rb index f77eedf265d8903662de55b2a78e725f9c44e0b2..a7f589f6cde5132c69b3f7a3f21f179b9f24a91d 100644 --- a/lib/gitlab/database/load_balancing/host.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/host.rb @@ -7,7 +7,8 @@ module LoadBalancing class Host attr_reader :pool, :last_checked_at, :intervals, :load_balancer, :host, :port - delegate :connection, :release_connection, :enable_query_cache!, :disable_query_cache!, :query_cache_enabled, to: :pool + delegate :connection, :release_connection, :enable_query_cache!, :disable_query_cache!, :query_cache_enabled, + to: :pool CONNECTION_ERRORS = [ ActionView::Template::Error, @@ -84,9 +85,9 @@ def initialize(host, load_balancer, port: nil) # timeout - The time after which the pool should be forcefully # disconnected. def disconnect!(timeout: 120) - start_time = ::Gitlab::Metrics::System.monotonic_time + start_time = ::Gitlab::Utils::System.monotonic_time - while (::Gitlab::Metrics::System.monotonic_time - start_time) <= timeout + while (::Gitlab::Utils::System.monotonic_time - start_time) <= timeout return if try_disconnect sleep(2) @@ -115,7 +116,7 @@ def pool_disconnect! end def offline! - ::Gitlab::Database::LoadBalancing::Logger.warn( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.warn( event: :host_offline, message: 'Marking host as offline', db_host: @host, @@ -135,7 +136,7 @@ def online? # Log that the host came back online if it was previously offline if @online && !was_online - ::Gitlab::Database::LoadBalancing::Logger.info( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.info( event: :host_online, message: 'Host is online after replica status check', db_host: @host, @@ -145,7 +146,7 @@ def online? ) # Always log if the host goes offline elsif !@online - ::Gitlab::Database::LoadBalancing::Logger.warn( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.warn( event: :host_offline, message: 'Host is offline after replica status check', db_host: @host, @@ -181,7 +182,7 @@ def replication_lag_below_threshold? return true if @lag_time <= load_balancer.configuration.max_replication_lag_time if ignore_replication_lag_time? - ::Gitlab::Database::LoadBalancing::Logger.info( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.info( event: :replication_lag_ignored, lag_time: @lag_time, message: 'Replication lag is treated as low because of load_balancer_ignore_replication_lag_time feature flag' @@ -191,7 +192,7 @@ def replication_lag_below_threshold? end if double_replication_lag_time? && @lag_time <= (load_balancer.configuration.max_replication_lag_time * 2) - ::Gitlab::Database::LoadBalancing::Logger.info( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.info( event: :replication_lag_below_double, lag_time: @lag_time, message: 'Replication lag is treated as low because of load_balancer_double_replication_lag_time feature flag' @@ -291,7 +292,7 @@ def query_and_release_fast_timeout(sql) # However, we don't ever run transactions on replicas, and we only do these health checks on replicas. # Double-check that we're not in a transaction, but this path should never happen. if connection.transaction_open? - Gitlab::Database::LoadBalancing::Logger.warn( + Gitlab::Database::LoadBalancing::Callbacks.logger.warn( event: :health_check_in_transaction, message: "Attempt to run a health check query inside of a transaction" ) @@ -310,6 +311,10 @@ def query_and_release_fast_timeout(sql) end end + def inspect + "<#{self.class.name} host:#{@host} port:#{@port}>" + end + private def can_track_logical_lsn? @@ -327,15 +332,15 @@ def latest_lsn_query end def ignore_replication_lag_time? - Feature.enabled?(:load_balancer_ignore_replication_lag_time, type: :ops) + Gitlab::Database::LoadBalancing::Callbacks.feature_enabled?(:load_balancer_ignore_replication_lag_time, type: :ops) end def double_replication_lag_time? - Feature.enabled?(:load_balancer_double_replication_lag_time, type: :ops) + Gitlab::Database::LoadBalancing::Callbacks.feature_enabled?(:load_balancer_double_replication_lag_time, type: :ops) end def low_timeout_for_host_queries? - Feature.enabled?(:load_balancer_low_statement_timeout, Feature.current_pod) + Gitlab::Database::LoadBalancing::Callbacks.feature_enabled?(:load_balancer_low_statement_timeout, :current_pod) end end end diff --git a/lib/gitlab/database/load_balancing/host_list.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/host_list.rb similarity index 92% rename from lib/gitlab/database/load_balancing/host_list.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/host_list.rb index fb3175c7d5d899a0ac277aab20862ce6c90ae83c..7a3750f637b43081e7cc17c57557977507b9e834 100644 --- a/lib/gitlab/database/load_balancing/host_list.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/host_list.rb @@ -10,7 +10,6 @@ def initialize(hosts = []) @hosts = hosts.shuffle @index = 0 @mutex = Mutex.new - @hosts_gauge = Gitlab::Metrics.gauge(:db_load_balancing_hosts, 'Current number of load balancing hosts') set_metrics! end @@ -80,7 +79,7 @@ def next_host end def set_metrics! - @hosts_gauge.set({}, @hosts.length) + Callbacks.metrics_host_gauge({}, @hosts.length) end end end diff --git a/lib/gitlab/database/load_balancing/load_balancer.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/load_balancer.rb similarity index 96% rename from lib/gitlab/database/load_balancing/load_balancer.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/load_balancer.rb index 99ddce46a15499e72ea5981a7e431348b721c9f8..4becd23c8f51e90af4c09a80cc06557d999f2d67 100644 --- a/lib/gitlab/database/load_balancing/load_balancer.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/load_balancer.rb @@ -76,7 +76,7 @@ def read(&block) # times before using the primary instead. will_retry = conflict_retried < @host_list.length * 3 - ::Gitlab::Database::LoadBalancing::Logger.warn( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.warn( event: :host_query_conflict, message: 'Query conflict on host', conflict_retried: conflict_retried, @@ -101,7 +101,7 @@ def read(&block) end end - ::Gitlab::Database::LoadBalancing::Logger.warn( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.warn( event: :no_secondaries_available, message: 'No secondaries were available, using primary instead', conflict_retried: conflict_retried, @@ -136,7 +136,7 @@ def read_write transaction_open = connection.transaction_open? if attempt && attempt > 1 - ::Gitlab::Database::LoadBalancing::Logger.warn( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.warn( event: :read_write_retry, message: 'A read_write block was retried because of connection error' ) @@ -147,7 +147,7 @@ def read_write # No leaking will happen on the final attempt. Leaks are caused by subsequent retries not_final_attempt = attempt && attempt < attempts if transaction_open && connection_error?(e) && not_final_attempt - ::Gitlab::Database::LoadBalancing::Logger.warn( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.warn( event: :transaction_leak, message: 'A write transaction has leaked during database fail-over' ) @@ -315,7 +315,6 @@ def create_replica_connection_pool(pool_size, host = nil, port = nil) # ActiveRecord::ConnectionAdapters::ConnectionHandler handles fetching, # and caching for connections pools for each "connection", so we # leverage that. - # rubocop:disable Database/MultipleDatabases def pool ActiveRecord::Base.connection_handler.retrieve_connection_pool( @configuration.connection_specification_name, @@ -323,7 +322,6 @@ def pool shard: ActiveRecord::Base.default_shard ) || raise(::ActiveRecord::ConnectionNotEstablished) end - # rubocop:enable Database/MultipleDatabases def wal_diff(location1, location2) read_write do |connection| @@ -372,7 +370,7 @@ def get_write_location(ar_connection) END AS location; NEWSQL else - <<~SQL + <<~SQL.squish SELECT pg_current_wal_insert_lsn()::text AS location SQL end @@ -384,7 +382,8 @@ def get_write_location(ar_connection) def raise_if_concurrent_ruby! Gitlab::Utils.raise_if_concurrent_ruby!(:db) rescue StandardError => e - Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e) + ::Gitlab::Database::LoadBalancing::Callbacks.track_exception(e) + raise if Rails.env.development? || Rails.env.test? end end end diff --git a/lib/gitlab/database/load_balancing/primary_host.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/primary_host.rb similarity index 96% rename from lib/gitlab/database/load_balancing/primary_host.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/primary_host.rb index 0359f9e9994ebd5af77bdf30b7d55d09fa776754..f23b63ce9c5496010b9e14a235115c1f84e4cb5a 100644 --- a/lib/gitlab/database/load_balancing/primary_host.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/primary_host.rb @@ -53,7 +53,7 @@ def disconnect!(timeout: 120) end def offline! - ::Gitlab::Database::LoadBalancing::Logger.warn( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.warn( event: :host_offline, message: 'Marking primary host as offline' ) diff --git a/lib/gitlab/database/load_balancing/resolver.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/resolver.rb similarity index 100% rename from lib/gitlab/database/load_balancing/resolver.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/resolver.rb diff --git a/lib/gitlab/database/load_balancing/service_discovery.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/service_discovery.rb similarity index 93% rename from lib/gitlab/database/load_balancing/service_discovery.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/service_discovery.rb index 890dde3c8438767b0ce5eef52732313b752ee096..6f85ae52748637ed278482d97443ff0517ab7f26 100644 --- a/lib/gitlab/database/load_balancing/service_discovery.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/service_discovery.rb @@ -17,8 +17,7 @@ class ServiceDiscovery attr_accessor :refresh_thread, :refresh_thread_last_run, :refresh_thread_interruption_logged - attr_reader :interval, :record, :record_type, :disconnect_timeout, - :load_balancer + attr_reader :interval, :record, :record_type, :disconnect_timeout, :load_balancer MAX_SLEEP_ADJUSTMENT = 10 MAX_DISCOVERY_RETRIES = 3 @@ -96,9 +95,9 @@ def perform_service_discovery rescue StandardError => error # Any exceptions that might occur should be reported to # Sentry, instead of silently terminating this thread. - Gitlab::ErrorTracking.track_exception(error) + ::Gitlab::Database::LoadBalancing::Callbacks.track_exception(error) - Gitlab::Database::LoadBalancing::Logger.error( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.error( event: :service_discovery_failure, message: "Service discovery encountered an error: #{error.message}", host_list_length: load_balancer.host_list.length, @@ -124,7 +123,7 @@ def refresh_if_necessary current = addresses_from_load_balancer if from_dns != current - ::Gitlab::Database::LoadBalancing::Logger.info( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.info( event: :host_list_update, message: "Updating the host list for service discovery", host_list_length: from_dns.length, @@ -212,7 +211,7 @@ def log_refresh_thread_interruption return if refresh_thread_last_run.blank? || refresh_thread_interruption_logged || (refresh_thread_last_run + DISCOVERY_THREAD_REFRESH_DELTA.minutes).future? - Gitlab::Database::LoadBalancing::Logger.error( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.error( event: :service_discovery_refresh_thread_interrupt, refresh_thread_last_run: refresh_thread_last_run, thread_status: refresh_thread&.status&.to_s, @@ -222,6 +221,10 @@ def log_refresh_thread_interruption self.refresh_thread_interruption_logged = true end + def inspect + "<#{self.class.name} nameserver:#{@nameserver}, port:#{@port}>" + end + private def record_type_for(type) @@ -233,12 +236,12 @@ def record_type_for(type) def addresses_from_srv_record(response) srv_resolver = SrvResolver.new(resolver, response.additional) - response.answer.map do |r| + response.answer.filter_map do |r| address = srv_resolver.address_for(r.host.to_s) next unless address Address.new(address.to_s, r.port) - end.compact + end end def addresses_from_a_record(resources) @@ -253,14 +256,14 @@ def sampler def disconnect_old_hosts(hosts) return unless hosts.present? - gentle_disconnect_start = ::Gitlab::Metrics::System.monotonic_time + gentle_disconnect_start = ::Gitlab::Utils::System.monotonic_time gentle_disconnect_deadline = gentle_disconnect_start + disconnect_timeout hosts_to_disconnect = hosts gentle_disconnected_hosts = [] gentle_disconnect_duration = Benchmark.realtime do - while ::Gitlab::Metrics::System.monotonic_time < gentle_disconnect_deadline + while ::Gitlab::Utils::System.monotonic_time < gentle_disconnect_deadline newly_disconnected, still_to_disconnect = hosts_to_disconnect.partition(&:try_disconnect) gentle_disconnected_hosts.concat(newly_disconnected) hosts_to_disconnect = still_to_disconnect @@ -283,7 +286,7 @@ def disconnect_old_hosts(hosts) formatted_all_hosts = formatted_gentle_hosts + formatted_forced_hosts - ::Gitlab::Database::LoadBalancing::Logger.info( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.info( event: :host_list_disconnection, message: "Disconnected #{formatted_all_hosts} old load balancing hosts after #{total_disconnect_duration}s", gentle_disconnected_hosts: formatted_gentle_hosts, diff --git a/lib/gitlab/database/load_balancing/service_discovery/sampler.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/service_discovery/sampler.rb similarity index 93% rename from lib/gitlab/database/load_balancing/service_discovery/sampler.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/service_discovery/sampler.rb index bb0508cc9b622e7ddee846588e7acf4fd4db0680..5bb4ae788ad08a5343459b213a2b04cd67f7d0e9 100644 --- a/lib/gitlab/database/load_balancing/service_discovery/sampler.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/service_discovery/sampler.rb @@ -16,7 +16,7 @@ def initialize(max_replica_pools:, seed: Random.new_seed) def sample(addresses) return addresses if @max_replica_pools.nil? || addresses.count <= @max_replica_pools - ::Gitlab::Database::LoadBalancing::Logger.debug( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.debug( event: :host_list_limit_exceeded, message: "Host list length exceeds max_replica_pools so random hosts will be chosen.", max_replica_pools: @max_replica_pools, @@ -37,7 +37,7 @@ def sample(addresses) while selected_addresses.count < @max_replica_pools # Loop over all hostnames grabbing one address at a time to # evenly distribute across all hostnames - addresses_by_host.each do |host, addresses| + addresses_by_host.each_value do |addresses| next if addresses.empty? selected_addresses << addresses.pop diff --git a/lib/gitlab/database/load_balancing/session.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/session.rb similarity index 100% rename from lib/gitlab/database/load_balancing/session.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/session.rb diff --git a/lib/gitlab/database/load_balancing/session_map.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/session_map.rb similarity index 92% rename from lib/gitlab/database/load_balancing/session_map.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/session_map.rb index 3eb52d2b378e608e3be5914ca2ceff6ec817593f..49cbac0b22335aba4242701984f085b5d2a024dc 100644 --- a/lib/gitlab/database/load_balancing/session_map.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/session_map.rb @@ -40,7 +40,7 @@ def self.cached_instance attr_reader :session_map def initialize - @session_map = Gitlab::Database.all_database_names.to_h do |k| + @session_map = Gitlab::Database::LoadBalancing.all_database_names.to_h do |k| [k.to_sym, Gitlab::Database::LoadBalancing::Session.new] end @@ -55,7 +55,7 @@ def lookup(load_balancer) def validate_db_name(db) if db == :primary - # Allow :primary in general but report the exeception. We should expect primary for: + # Allow :primary in general but report the exception. We should expect primary for: # # 1. rake task db migrations as ActiveRecord::Tasks::PostgresqlDatabaseTasks calls # .establish_connection using a hash which resets the name from :main/:ci to :primary. @@ -64,8 +64,8 @@ def validate_db_name(db) # # 2. In the case of derailed test in memory-on-boot job, the runtime is unknown. # 3. `scripts/regenerate-schema` which runs in RAILS_ENV=test - Gitlab::ErrorTracking.track_exception( - InvalidLoadBalancerNameError.new("Using #{db} load balancer in #{Gitlab::Runtime.safe_identify}.") + Gitlab::Database::LoadBalancing::Callbacks.track_exception( + InvalidLoadBalancerNameError.new("Using #{db} load balancer") ) return @@ -74,7 +74,7 @@ def validate_db_name(db) return if session_map[db] # All other load balancer names are invalid and should raise an error - raise InvalidLoadBalancerNameError, "Invalid load balancer name #{db} in #{Gitlab::Runtime.safe_identify}." + raise InvalidLoadBalancerNameError, "Invalid load balancer name #{db}." end end diff --git a/lib/gitlab/database/load_balancing/setup.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/setup.rb similarity index 90% rename from lib/gitlab/database/load_balancing/setup.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/setup.rb index 2e65e1c8e56faefab1b0e58c091beb8978579bc6..f9f848ebacd44c29396bcd0414697f79185f9b11 100644 --- a/lib/gitlab/database/load_balancing/setup.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/setup.rb @@ -18,7 +18,7 @@ def setup setup_connection_proxy setup_service_discovery - ::Gitlab::Database::LoadBalancing::Logger.debug( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.debug( event: :setup, model: model.name, start_service_discovery: @start_service_discovery @@ -30,7 +30,7 @@ def configure_connection hash = db_config_object.configuration_hash.merge( prepared_statements: false, - pool: Gitlab::Database.default_pool_size + pool: Gitlab::Database::LoadBalancing.default_pool_size ) hash_config = ActiveRecord::DatabaseConfigurations::HashConfig.new( @@ -70,7 +70,7 @@ def load_balancer def setup_class_attribute(attribute, value) @model.class_attribute(attribute) - @model.public_send("#{attribute}=", value) # rubocop:disable GitlabSecurity/PublicSend + @model.public_send(:"#{attribute}=", value) # rubocop:disable GitlabSecurity/PublicSend end def active_record_base? diff --git a/lib/gitlab/database/load_balancing/srv_resolver.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/srv_resolver.rb similarity index 84% rename from lib/gitlab/database/load_balancing/srv_resolver.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/srv_resolver.rb index 1f599ef4a273f9dc48d7cc727d23a96eefd75208..312c95dd0283e550e766028f666c076ee6944b7d 100644 --- a/lib/gitlab/database/load_balancing/srv_resolver.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/srv_resolver.rb @@ -28,12 +28,11 @@ def address_for(host) private def addresses_from_additional - strong_memoize(:addresses_from_additional) do - additional.each_with_object({}) do |rr, h| - h[rr.name] = rr.address if rr.is_a?(Net::DNS::RR::A) || rr.is_a?(Net::DNS::RR::AAAA) - end + additional.each_with_object({}) do |rr, h| + h[rr.name] = rr.address if rr.is_a?(Net::DNS::RR::A) || rr.is_a?(Net::DNS::RR::AAAA) end end + strong_memoize_attr :addresses_from_additional def resolve_host(host) record = resolver.search(host, Net::DNS::ANY).answer.find do |rr| diff --git a/lib/gitlab/database/load_balancing/sticking.rb b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/sticking.rb similarity index 88% rename from lib/gitlab/database/load_balancing/sticking.rb rename to gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/sticking.rb index 8260893f0dd46877458ff7bb74a834a427f8a181..20e08577365764a3aeba8497afeaea98102934e9 100644 --- a/lib/gitlab/database/load_balancing/sticking.rb +++ b/gems/gitlab-database-load_balancing/lib/gitlab/database/load_balancing/sticking.rb @@ -65,7 +65,7 @@ def bulk_stick(namespace, ids) private def log_database_sticking_operations_enabled? - Feature.enabled?(:log_database_sticking_operations, Feature.current_pod) + Callbacks.feature_enabled?(:log_database_sticking_operations, :current_pod) end def capture_stick_logs(namespace, ids, location) @@ -74,7 +74,7 @@ def capture_stick_logs(namespace, ids, location) id = Array(ids).first # Only log the first ID to reduce log volume for bulk operations - ::Gitlab::Database::LoadBalancing::Logger.info( + ::Gitlab::Database::LoadBalancing::Callbacks.logger.info( event: :load_balancer_stick_logging, client_id: "#{namespace}/#{id}", stick_id: id, @@ -95,21 +95,15 @@ def with_primary_write_location end def unstick(namespace, id) - with_redis do |redis| - redis.del(redis_key_for(namespace, id)) - end + Callbacks.del_wal_for(redis_key_for(namespace, id)) end def set_write_location_for(namespace, id, location) - with_redis do |redis| - redis.set(redis_key_for(namespace, id), location, ex: EXPIRATION) - end + Callbacks.set_wal_for(redis_key_for(namespace, id), location, ex: EXPIRATION) end def last_write_location_for(namespace, id) - with_redis do |redis| - redis.get(redis_key_for(namespace, id)) - end + Callbacks.get_wal_for(redis_key_for(namespace, id)) end def redis_key_for(namespace, id) @@ -118,10 +112,6 @@ def redis_key_for(namespace, id) "database-load-balancing/write-location/#{name}/#{namespace}/#{id}" end - def with_redis(&block) - Gitlab::Redis::DbLoadBalancing.with(&block) - end - def use_primary! ::Gitlab::Database::LoadBalancing::SessionMap.current(@load_balancer).use_primary! end diff --git a/spec/fixtures/dns/a_rr.json b/gems/gitlab-database-load_balancing/spec/fixtures/dns/a_rr.json similarity index 100% rename from spec/fixtures/dns/a_rr.json rename to gems/gitlab-database-load_balancing/spec/fixtures/dns/a_rr.json diff --git a/spec/fixtures/dns/a_with_aaaa_rr_in_additional_section.json b/gems/gitlab-database-load_balancing/spec/fixtures/dns/a_with_aaaa_rr_in_additional_section.json similarity index 100% rename from spec/fixtures/dns/a_with_aaaa_rr_in_additional_section.json rename to gems/gitlab-database-load_balancing/spec/fixtures/dns/a_with_aaaa_rr_in_additional_section.json diff --git a/spec/fixtures/dns/aaaa_rr.json b/gems/gitlab-database-load_balancing/spec/fixtures/dns/aaaa_rr.json similarity index 100% rename from spec/fixtures/dns/aaaa_rr.json rename to gems/gitlab-database-load_balancing/spec/fixtures/dns/aaaa_rr.json diff --git a/spec/fixtures/dns/srv_with_a_rr_in_additional_section.json b/gems/gitlab-database-load_balancing/spec/fixtures/dns/srv_with_a_rr_in_additional_section.json similarity index 100% rename from spec/fixtures/dns/srv_with_a_rr_in_additional_section.json rename to gems/gitlab-database-load_balancing/spec/fixtures/dns/srv_with_a_rr_in_additional_section.json diff --git a/spec/lib/gitlab/database/load_balancing/action_cable_callbacks_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/action_cable_callbacks_spec.rb similarity index 100% rename from spec/lib/gitlab/database/load_balancing/action_cable_callbacks_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/action_cable_callbacks_spec.rb diff --git a/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/callbacks_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/callbacks_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..72c274f25eb219cec142af623aae174881bd7e19 --- /dev/null +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/callbacks_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::LoadBalancing::Callbacks do + # Around each example, reset the state of the callbacks so that specs don't pollute state + around do |example| + logger_proc = described_class.logger_proc + metrics_host_gauge_proc = described_class.metrics_host_gauge_proc + track_exception_proc = described_class.track_exception_proc + set_wal_for_proc = described_class.set_wal_for_proc + get_wal_for_proc = described_class.get_wal_for_proc + del_wal_for_proc = described_class.del_wal_for_proc + check_feature_flag = described_class.check_feature_flag + + example.run + + described_class.configure! do |cb| + cb.logger_proc = logger_proc + cb.metrics_host_gauge_proc = metrics_host_gauge_proc + cb.track_exception_proc = track_exception_proc + cb.set_wal_for_proc = set_wal_for_proc + cb.get_wal_for_proc = get_wal_for_proc + cb.del_wal_for_proc = del_wal_for_proc + cb.check_feature_flag = check_feature_flag + end + end + + describe '#feature_enabled?' do + # rubocop:disable Lint/UnusedBlockArgument -- type here mirrors the type kwarg in the real callback + let(:check_flag) { ->(_key, _thing, type: nil) { false } } + # rubocop:enable Lint/UnusedBlockArgument + + before do + described_class.configure! do |cb| + cb.check_feature_flag = check_flag + end + end + + it 'passes through feature flag calls for load_balancer_ feature flags' do + expect(check_flag).to receive(:call).with(:load_balancer_some_flag, anything, type: anything) + described_class.feature_enabled?(:load_balancer_some_flag) + end + + it 'raises an exception for non-load_balancer feature flags' do + expect { described_class.feature_enabled?(:other_flag) }.to raise_error(/Unsupported feature flag/) + end + end +end diff --git a/spec/lib/gitlab/database/load_balancing/configuration_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/configuration_spec.rb similarity index 88% rename from spec/lib/gitlab/database/load_balancing/configuration_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/configuration_spec.rb index 7dc2e0be3e5e90b6ede8c9554bdd4fbedac42bf7..f8c4c5e8136c273990703c7a083d2172afba3352 100644 --- a/spec/lib/gitlab/database/load_balancing/configuration_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/configuration_spec.rb @@ -26,7 +26,7 @@ use_tcp: false, max_replica_pools: nil ) - expect(config.pool_size).to eq(Gitlab::Database.default_pool_size) + expect(config.pool_size).to eq(Gitlab::Database::LoadBalancing.default_pool_size) end end @@ -110,52 +110,52 @@ it 'returns false when running inside a Rake task' do config = described_class.new(ActiveRecord::Base, %w[foo bar]) - allow(Gitlab::Runtime).to receive(:rake?).and_return(true) + allow(Gitlab::Database::LoadBalancing).to receive(:enabled).and_return(false) - expect(config.load_balancing_enabled?).to eq(false) + expect(config.load_balancing_enabled?).to be(false) end it 'returns true when hosts are configured' do config = described_class.new(ActiveRecord::Base, %w[foo bar]) - expect(config.load_balancing_enabled?).to eq(true) + expect(config.load_balancing_enabled?).to be(true) end it 'returns true when a service discovery record is configured' do config = described_class.new(ActiveRecord::Base) config.service_discovery[:record] = 'foo' - expect(config.load_balancing_enabled?).to eq(true) + expect(config.load_balancing_enabled?).to be(true) end it 'returns false when no hosts are configured and service discovery is disabled' do config = described_class.new(ActiveRecord::Base) - expect(config.load_balancing_enabled?).to eq(false) + expect(config.load_balancing_enabled?).to be(false) end end describe '#service_discovery_enabled?' do it 'returns false when running inside a Rake task' do - allow(Gitlab::Runtime).to receive(:rake?).and_return(true) + allow(Gitlab::Database::LoadBalancing).to receive(:enabled).and_return(false) config = described_class.new(ActiveRecord::Base) config.service_discovery[:record] = 'foo' - expect(config.service_discovery_enabled?).to eq(false) + expect(config.service_discovery_enabled?).to be(false) end it 'returns true when a record is configured' do config = described_class.new(ActiveRecord::Base) config.service_discovery[:record] = 'foo' - expect(config.service_discovery_enabled?).to eq(true) + expect(config.service_discovery_enabled?).to be(true) end it 'returns false when no record is configured' do config = described_class.new(ActiveRecord::Base) - expect(config.service_discovery_enabled?).to eq(false) + expect(config.service_discovery_enabled?).to be(false) end end @@ -184,7 +184,7 @@ it 'returns the default pool size' do config = described_class.new(model) - expect(config.pool_size).to eq(Gitlab::Database.default_pool_size) + expect(config.pool_size).to eq(Gitlab::Database::LoadBalancing.default_pool_size) end end end diff --git a/spec/lib/gitlab/database/load_balancing/connection_proxy_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/connection_proxy_spec.rb similarity index 87% rename from spec/lib/gitlab/database/load_balancing/connection_proxy_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/connection_proxy_spec.rb index 4d41fa5d7c61385e6e75c48a97fa379f5d43a521..b30fab6fe375947288208b2b6e80b78d38a992c3 100644 --- a/spec/lib/gitlab/database/load_balancing/connection_proxy_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/connection_proxy_spec.rb @@ -16,7 +16,7 @@ end describe '#select_all' do - let(:override_proxy) { ActiveRecord::Base.connection.class } + let(:override_proxy) { ApplicationRecord.connection.class } # We can't use :Gitlab::Utils::Override because this method is dynamically prepended it 'method signatures match' do @@ -141,12 +141,11 @@ .to receive(:current).with(load_balancer).and_return(session) end - context 'session fallbacks ambiguous queries to replicas' do + context 'when session fallbacks ambiguous queries to replicas' do let(:replica) { double(:connection) } before do - allow(session).to receive(:fallback_to_replicas_for_ambiguous_queries?).and_return(true) - allow(session).to receive(:use_primary?).and_return(false) + allow(session).to receive_messages(fallback_to_replicas_for_ambiguous_queries?: true, use_primary?: false) allow(replica).to receive(:transaction).and_yield allow(replica).to receive(:select) end @@ -174,13 +173,12 @@ end end - context 'session does not fallback to replicas for ambiguous queries' do + context 'when session does not fallback to replicas for ambiguous queries' do let(:primary) { double(:connection) } before do - allow(session).to receive(:fallback_to_replicas_for_ambiguous_queries?).and_return(false) - allow(session).to receive(:use_replicas_for_read_queries?).and_return(false) - allow(session).to receive(:use_primary?).and_return(true) + allow(session).to receive_messages(fallback_to_replicas_for_ambiguous_queries?: false, + use_replicas_for_read_queries?: false, use_primary?: true) allow(primary).to receive(:transaction).and_yield allow(primary).to receive(:select) allow(primary).to receive(:insert) @@ -227,14 +225,13 @@ .not_to raise_error end - context 'current session prefers to fallback ambiguous queries to replicas' do + context 'when current session prefers to fallback ambiguous queries to replicas' do let(:session) { double(:session) } before do allow(Gitlab::Database::LoadBalancing::SessionMap).to receive(:current).with(load_balancer) .and_return(session) - allow(session).to receive(:fallback_to_replicas_for_ambiguous_queries?).and_return(true) - allow(session).to receive(:use_primary?).and_return(false) + allow(session).to receive_messages(fallback_to_replicas_for_ambiguous_queries?: true, use_primary?: false) end it 'runs the query on the replica' do @@ -265,8 +262,7 @@ context 'with a regular session' do it 'uses a secondary' do - allow(session).to receive(:use_primary?).and_return(false) - allow(session).to receive(:use_replicas_for_read_queries?).and_return(false) + allow(session).to receive_messages(use_primary?: false, use_replicas_for_read_queries?: false) expect(connection).to receive(:foo).with('foo') expect(load_balancer).to receive(:read).and_yield(connection) @@ -277,8 +273,7 @@ context 'with a regular session and forcing all reads to replicas' do it 'uses a secondary' do - allow(session).to receive(:use_primary?).and_return(false) - allow(session).to receive(:use_replicas_for_read_queries?).and_return(true) + allow(session).to receive_messages(use_primary?: false, use_replicas_for_read_queries?: true) expect(connection).to receive(:foo).with('foo') expect(load_balancer).to receive(:read).and_yield(connection) @@ -289,8 +284,7 @@ context 'with a session using the primary but forcing all reads to replicas' do it 'uses a secondary' do - allow(session).to receive(:use_primary?).and_return(true) - allow(session).to receive(:use_replicas_for_read_queries?).and_return(true) + allow(session).to receive_messages(use_primary?: true, use_replicas_for_read_queries?: true) expect(connection).to receive(:foo).with('foo') expect(load_balancer).to receive(:read).and_yield(connection) @@ -301,8 +295,7 @@ describe 'with a session using the primary' do it 'uses the primary' do - allow(session).to receive(:use_primary?).and_return(true) - allow(session).to receive(:use_replicas_for_read_queries?).and_return(false) + allow(session).to receive_messages(use_primary?: true, use_replicas_for_read_queries?: false) expect(connection).to receive(:foo).with('foo') diff --git a/spec/lib/gitlab/database/load_balancing/host_list_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/host_list_spec.rb similarity index 92% rename from spec/lib/gitlab/database/load_balancing/host_list_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/host_list_spec.rb index 61183f5a99cd1ce89980d76e474911bcb54d458a..1e2d9cf30630106b301b03d6d169997e5d099c32 100644 --- a/spec/lib/gitlab/database/load_balancing/host_list_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/host_list_spec.rb @@ -19,6 +19,8 @@ allow(load_balancer).to receive(:create_replica_connection_pool) do double(:replica_connection_pool) end + + allow(Gitlab::Database::LoadBalancing::Callbacks).to receive(:metrics_host_gauge) end describe '#initialize' do @@ -130,12 +132,14 @@ host_list.shuffle expect(host_list.length).to eq(host_count) - expect(host_list.hosts).to contain_exactly(*all_hosts) + expect(host_list.hosts).to match_array(all_hosts) end end end def expect_metrics(hosts) - expect(Gitlab::Metrics.client.get(:db_load_balancing_hosts).get({})).to eq(hosts) + expect(Gitlab::Database::LoadBalancing::Callbacks).to have_received(:metrics_host_gauge) + .with({}, hosts) + .at_least(1).times end end diff --git a/spec/lib/gitlab/database/load_balancing/host_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/host_spec.rb similarity index 87% rename from spec/lib/gitlab/database/load_balancing/host_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/host_spec.rb index ec94485f258f06886002fea09d2d17c5243c739a..8b72484f283df047079b0d40a4c477848f2a57cd 100644 --- a/spec/lib/gitlab/database/load_balancing/host_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/host_spec.rb @@ -89,7 +89,7 @@ def wrapped_exception(wrapper, original) it 'marks the host as offline' do expect(host.pool).to receive(:disconnect!) - expect(Gitlab::Database::LoadBalancing::Logger).to receive(:warn) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:warn) .with(hash_including(event: :host_offline)) .and_call_original @@ -105,8 +105,8 @@ def wrapped_exception(wrapper, original) it 'returns the latest status' do expect(host).not_to receive(:refresh_status) - expect(Gitlab::Database::LoadBalancing::Logger).not_to receive(:info) - expect(Gitlab::Database::LoadBalancing::Logger).not_to receive(:warn) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:info) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:warn) expect(host).to be_online end @@ -115,8 +115,8 @@ def wrapped_exception(wrapper, original) host.offline! expect(host).not_to receive(:refresh_status) - expect(Gitlab::Database::LoadBalancing::Logger).not_to receive(:info) - expect(Gitlab::Database::LoadBalancing::Logger).not_to receive(:warn) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:info) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:warn) expect(host).not_to be_online end @@ -137,7 +137,7 @@ def wrapped_exception(wrapper, original) # Hosts are online by default it 'does not log the online event' do - expect(Gitlab::Database::LoadBalancing::Logger) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger) .not_to receive(:info) .with(hash_including(event: :host_online)) @@ -151,7 +151,7 @@ def wrapped_exception(wrapper, original) end it 'logs the online event' do - expect(Gitlab::Database::LoadBalancing::Logger) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger) .to receive(:info) .with(hash_including(event: :host_online)) .and_call_original @@ -166,7 +166,7 @@ def wrapped_exception(wrapper, original) end it 'marks the host offline' do - expect(Gitlab::Database::LoadBalancing::Logger).to receive(:warn) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:warn) .with(hash_including(event: :host_offline)) .and_call_original @@ -266,7 +266,7 @@ def wrapped_exception(wrapper, original) .to receive(:last_checked_at) .and_return(1.year.ago) - expect(host.check_replica_status?).to eq(true) + expect(host.check_replica_status?).to be(true) end it 'returns false when we do not need to check the replica status' do @@ -275,7 +275,7 @@ def wrapped_exception(wrapper, original) .to receive(:last_checked_at) .and_return(Time.zone.now) - expect(host.check_replica_status?).to eq(false) + expect(host.check_replica_status?).to be(false) end end end @@ -287,7 +287,7 @@ def wrapped_exception(wrapper, original) .to receive(:replication_lag_below_threshold?) .and_return(true) - expect(host.replica_is_up_to_date?).to eq(true) + expect(host.replica_is_up_to_date?).to be(true) end end @@ -303,7 +303,7 @@ def wrapped_exception(wrapper, original) .to receive(:data_is_recent_enough?) .and_return(true) - expect(host.replica_is_up_to_date?).to eq(true) + expect(host.replica_is_up_to_date?).to be(true) end it 'returns false when the data is not recent enough' do @@ -311,7 +311,7 @@ def wrapped_exception(wrapper, original) .to receive(:data_is_recent_enough?) .and_return(false) - expect(host.replica_is_up_to_date?).to eq(false) + expect(host.replica_is_up_to_date?).to be(false) end end end @@ -321,8 +321,8 @@ def wrapped_exception(wrapper, original) let(:load_balancer_ignore_replication_lag_time) { false } before do - stub_feature_flags(load_balancer_double_replication_lag_time: load_balancer_double_replication_lag_time) - stub_feature_flags(load_balancer_ignore_replication_lag_time: load_balancer_ignore_replication_lag_time) + stub_load_balancer_feature_flags(load_balancer_double_replication_lag_time: load_balancer_double_replication_lag_time) + stub_load_balancer_feature_flags(load_balancer_ignore_replication_lag_time: load_balancer_ignore_replication_lag_time) end it 'returns true when the lag time is below the threshold' do @@ -330,7 +330,7 @@ def wrapped_exception(wrapper, original) .to receive(:replication_lag_time) .and_return(1) - expect(host.replication_lag_below_threshold?).to eq(true) + expect(host.replication_lag_below_threshold?).to be(true) end it 'returns false when the lag time exceeds the threshold' do @@ -338,7 +338,7 @@ def wrapped_exception(wrapper, original) .to receive(:replication_lag_time) .and_return(9000) - expect(host.replication_lag_below_threshold?).to eq(false) + expect(host.replication_lag_below_threshold?).to be(false) end it 'returns false when no lag time could be calculated' do @@ -346,7 +346,7 @@ def wrapped_exception(wrapper, original) .to receive(:replication_lag_time) .and_return(nil) - expect(host.replication_lag_below_threshold?).to eq(false) + expect(host.replication_lag_below_threshold?).to be(false) end context 'with the load_balancer_double_replication_lag_time feature flag enabled' do @@ -357,7 +357,7 @@ def wrapped_exception(wrapper, original) .to receive(:replication_lag_time) .and_return(121) - expect(host.replication_lag_below_threshold?).to eq(false) + expect(host.replication_lag_below_threshold?).to be(false) end it 'returns true when lag time is below the higher threshold' do @@ -365,7 +365,7 @@ def wrapped_exception(wrapper, original) .to receive(:replication_lag_time) .and_return(119) - expect(host.replication_lag_below_threshold?).to eq(true) + expect(host.replication_lag_below_threshold?).to be(true) end end @@ -377,14 +377,14 @@ def wrapped_exception(wrapper, original) .to receive(:replication_lag_time) .and_return(3600) - expect(host.replication_lag_below_threshold?).to eq(true) + expect(host.replication_lag_below_threshold?).to be(true) end end end describe '#data_is_recent_enough?' do it 'returns true when the data is recent enough' do - expect(host.data_is_recent_enough?).to eq(true) + expect(host.data_is_recent_enough?).to be(true) end it 'returns false when the data is not recent enough' do @@ -399,7 +399,7 @@ def wrapped_exception(wrapper, original) .to receive(:query_and_release) .and_return({ 'diff' => diff }) - expect(host.data_is_recent_enough?).to eq(false) + expect(host.data_is_recent_enough?).to be(false) end it 'returns false when no lag size could be calculated' do @@ -407,7 +407,7 @@ def wrapped_exception(wrapper, original) .to receive(:replication_lag_size) .and_return(nil) - expect(host.data_is_recent_enough?).to eq(false) + expect(host.data_is_recent_enough?).to be(false) end end @@ -424,13 +424,13 @@ def wrapped_exception(wrapper, original) expect(host.replication_lag_time).to be_nil end + # TODO: Figure out flag stubbing and fix context 'with the flag set' do before do - stub_feature_flags(load_balancer_low_statement_timeout: Feature.current_pod) + stub_load_balancer_feature_flags(load_balancer_low_statement_timeout: true) end it 'returns quickly if the underlying query takes a long time' do - allow(host.connection).to receive(:transaction_open?).and_return(false) allow(host.connection).to receive(:select_all).and_call_original expect(host.connection).to receive(:select_all).with(described_class::REPLICATION_LAG_QUERY) do host.connection.select_all('select pg_sleep(5)') @@ -449,35 +449,39 @@ def wrapped_exception(wrapper, original) it 'does not use a low statement timeout if a transaction is already open' do allow(host.connection).to receive(:select_all).and_call_original expect(host.connection).to receive(:select_all).with(described_class::REPLICATION_LAG_QUERY) do - host.connection.select_all(<<~SQL) - select - EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::float as lag, - pg_sleep(1) + host.connection.select_all(<<~SQL.squish) + select + EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::float as lag, + pg_sleep(1) SQL end - expect(Gitlab::Database::LoadBalancing::Logger) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger) .to receive(:warn).with(hash_including(event: :health_check_in_transaction)) duration = Benchmark.realtime do # without a low statement timeout the query succeeds and gives the real lag time # 0 lag because this isn't a replica during testing - expect(host.replication_lag_time).to eq(0.0) + # Must open an explicit transaction - gem testing isn't wrapping tests in transactions by default + host.connection.transaction do + expect(host.replication_lag_time).to eq(0.0) + end end # We waited at least 1 second for the pg_sleep(1) expect(duration).to be > (1) end end + # TODO: Figure out flag stubbing and fix context 'with the flag not set' do before do - stub_feature_flags(load_balancer_low_statement_timeout: false) + stub_load_balancer_feature_flags(load_balancer_low_statement_timeout: false) end it 'waits for the underlying query when it takes a long time' do allow(host.connection).to receive(:select_all).and_call_original expect(host.connection).to receive(:select_all).with(described_class::REPLICATION_LAG_QUERY).once do - host.connection.select_all(<<~SQL) + host.connection.select_all(<<~SQL.squish) SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::float as lag, pg_sleep(1) as sleep @@ -609,7 +613,7 @@ def wrapped_exception(wrapper, original) let(:diff_result) { [{ "diff" => 123 }] } it 'returns false' do - expect(host.caught_up?('foo')).to eq(false) + expect(host.caught_up?('foo')).to be(false) end end end @@ -621,7 +625,7 @@ def wrapped_exception(wrapper, original) .to receive(:connection) .and_raise(wrapped_error) - expect(host.caught_up?('foo')).to eq(false) + expect(host.caught_up?('foo')).to be(false) end end end diff --git a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/load_balancer_spec.rb similarity index 92% rename from spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/load_balancer_spec.rb index 3c14dc23a8083ad3cd5c29e2a8711c970da7b45b..babeac02ba22421655d7b512ea27fecce8963224 100644 --- a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/load_balancer_spec.rb @@ -125,8 +125,7 @@ def twice_wrapped_exception(top, middle, original) allow(lb).to receive(:host).and_return(host) allow(Rails.application.executor).to receive(:active?).and_return(true) - allow(host).to receive(:query_cache_enabled).and_return(false) - allow(host).to receive(:connection).and_return(connection) + allow(host).to receive_messages(query_cache_enabled: false, connection: connection) expect(host).to receive(:enable_query_cache!).once @@ -139,8 +138,7 @@ def twice_wrapped_exception(top, middle, original) allow(lb).to receive(:host).and_return(host) allow(Rails.application.executor).to receive(:active?).and_return(false) - allow(host).to receive(:query_cache_enabled).and_return(false) - allow(host).to receive(:connection).and_return(connection) + allow(host).to receive_messages(query_cache_enabled: false, connection: connection) expect(host).not_to receive(:enable_query_cache!) @@ -191,7 +189,7 @@ def twice_wrapped_exception(top, middle, original) lb.read { raise conflict_error } end - context 'only primary is configured' do + context 'when only primary is configured' do let(:lb) do config = Gitlab::Database::LoadBalancing::Configuration.new(ActiveRecord::Base) allow(config).to receive(:load_balancing_enabled?).and_return(false) @@ -230,7 +228,7 @@ def twice_wrapped_exception(top, middle, original) # When no hosts are configured, we don't want to produce any warnings, as # they aren't useful/too noisy. - expect(Gitlab::Database::LoadBalancing::Logger).not_to receive(:warn) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:warn) expect { |b| lb.read(&b) } .to yield_with_args(ActiveRecord::Base.retrieve_connection) @@ -402,25 +400,25 @@ def twice_wrapped_exception(top, middle, original) it 'returns true for a connection error' do error = ActiveRecord::ConnectionNotEstablished.new - expect(lb.connection_error?(error)).to eq(true) + expect(lb.connection_error?(error)).to be(true) end it 'returns false for a missing database error' do error = ActiveRecord::NoDatabaseError.new - expect(lb.connection_error?(error)).to eq(false) + expect(lb.connection_error?(error)).to be(false) end it 'returns true for a wrapped connection error' do wrapped = wrapped_exception(ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotEstablished) - expect(lb.connection_error?(wrapped)).to eq(true) + expect(lb.connection_error?(wrapped)).to be(true) end it 'returns true for a wrapped connection error from a view' do wrapped = wrapped_exception(ActionView::Template::Error, ActiveRecord::ConnectionNotEstablished) - expect(lb.connection_error?(wrapped)).to eq(true) + expect(lb.connection_error?(wrapped)).to be(true) end it 'returns true for deeply wrapped/nested errors' do @@ -430,25 +428,25 @@ def twice_wrapped_exception(top, middle, original) ActiveRecord::ConnectionNotEstablished ) - expect(lb.connection_error?(top)).to eq(true) + expect(lb.connection_error?(top)).to be(true) end it 'returns true for an invalid encoding error' do error = RuntimeError.new('invalid encoding name: unicode') - expect(lb.connection_error?(error)).to eq(true) + expect(lb.connection_error?(error)).to be(true) end it 'returns false for errors not related to database connections' do error = RuntimeError.new - expect(lb.connection_error?(error)).to eq(false) + expect(lb.connection_error?(error)).to be(false) end it 'returns false for ActiveRecord errors without a cause' do error = ActiveRecord::RecordNotUnique.new - expect(lb.connection_error?(error)).to eq(false) + expect(lb.connection_error?(error)).to be(false) end end @@ -463,13 +461,13 @@ def twice_wrapped_exception(top, middle, original) end it 'returns for a serialization error' do - expect(lb.serialization_failure?(conflict_error.new)).to eq(true) + expect(lb.serialization_failure?(conflict_error.new)).to be(true) end it 'returns true for a wrapped error' do wrapped = wrapped_exception(ActionView::Template::Error, conflict_error) - expect(lb.serialization_failure?(wrapped)).to eq(true) + expect(lb.serialization_failure?(wrapped)).to be(true) end end @@ -478,7 +476,7 @@ def twice_wrapped_exception(top, middle, original) let(:hosts) { lb.host_list.hosts } let(:set_host) { request_cache[described_class::CACHE_KEY] } - subject { lb.select_up_to_date_host(location) } + subject(:select_up_to_date_host) { lb.select_up_to_date_host(location) } context 'when none of the replicas are caught up' do before do @@ -487,7 +485,7 @@ def twice_wrapped_exception(top, middle, original) end it 'returns NONE_CAUGHT_UP and does not update the host thread-local variable' do - expect(subject).to eq(described_class::NONE_CAUGHT_UP) + expect(select_up_to_date_host).to eq(described_class::NONE_CAUGHT_UP) expect(set_host).to be_nil end @@ -495,7 +493,7 @@ def twice_wrapped_exception(top, middle, original) expect(ActiveSupport::Notifications).to receive(:instrument) .with('caught_up_replica_pick.load_balancing', { result: false }) - subject + select_up_to_date_host end end @@ -506,7 +504,7 @@ def twice_wrapped_exception(top, middle, original) end it 'returns ANY_CAUGHT_UP and sets host thread-local variable' do - expect(subject).to eq(described_class::ANY_CAUGHT_UP) + expect(select_up_to_date_host).to eq(described_class::ANY_CAUGHT_UP) expect(set_host).to eq(hosts[0]) end @@ -514,7 +512,7 @@ def twice_wrapped_exception(top, middle, original) expect(ActiveSupport::Notifications).to receive(:instrument) .with('caught_up_replica_pick.load_balancing', { result: true }) - subject + select_up_to_date_host end end @@ -525,7 +523,7 @@ def twice_wrapped_exception(top, middle, original) end it 'returns ALL_CAUGHT_UP and sets host thread-local variable' do - expect(subject).to eq(described_class::ALL_CAUGHT_UP) + expect(select_up_to_date_host).to eq(described_class::ALL_CAUGHT_UP) expect(set_host).to be_in([hosts[0], hosts[1]]) end @@ -533,7 +531,7 @@ def twice_wrapped_exception(top, middle, original) expect(ActiveSupport::Notifications).to receive(:instrument) .with('caught_up_replica_pick.load_balancing', { result: true }) - subject + select_up_to_date_host end end end @@ -594,7 +592,11 @@ def with_replica_pool(*args) it 'returns the diff between two write locations' do loc1 = lb.send(:get_write_location, lb.pool.connection) - create(:user) # This ensures we get a new WAL location + ActiveRecord::Schema.define do + create_table :_test_load_balancing_test, force: true do |t| + t.string :name, null: true + end + end loc2 = lb.send(:get_write_location, lb.pool.connection) diff = lb.wal_diff(loc2, loc1) diff --git a/spec/lib/gitlab/database/load_balancing/primary_host_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/primary_host_spec.rb similarity index 91% rename from spec/lib/gitlab/database/load_balancing/primary_host_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/primary_host_spec.rb index 53605d14c1726d81b14c4c7d0b27430eceb6578c..f5ef9a0767c2860057b537fc355385fa0fa2f429 100644 --- a/spec/lib/gitlab/database/load_balancing/primary_host_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/primary_host_spec.rb @@ -52,7 +52,7 @@ describe '#offline!' do it 'logs the event but does nothing else' do - expect(Gitlab::Database::LoadBalancing::Logger).to receive(:warn) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:warn) .with(hash_including(event: :host_offline)) .and_call_original @@ -62,7 +62,7 @@ describe '#online?' do it 'returns true' do - expect(host.online?).to eq(true) + expect(host.online?).to be(true) end end @@ -74,7 +74,7 @@ describe '#caught_up?' do it 'returns true' do - expect(host.caught_up?('foo')).to eq(true) + expect(host.caught_up?('foo')).to be(true) end end diff --git a/spec/lib/gitlab/database/load_balancing/resolver_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/resolver_spec.rb similarity index 86% rename from spec/lib/gitlab/database/load_balancing/resolver_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/resolver_spec.rb index 4af36693383d54867ab1e761c6cb6ee74219c78a..cc7c38084df0b46b14af5f7c47d82244cfd5711b 100644 --- a/spec/lib/gitlab/database/load_balancing/resolver_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/resolver_spec.rb @@ -16,14 +16,14 @@ end context 'when nameserver is not an IP' do - subject { described_class.new('localhost').resolve } + subject(:resolve_response) { described_class.new('localhost').resolve } it 'looks the nameserver up in the hosts file' do allow_next_instance_of(Resolv::Hosts) do |instance| allow(instance).to receive(:getaddress).with('localhost').and_return('127.0.0.2') end - expect(subject.address).to eq(ip_addr) + expect(resolve_response.address).to eq(ip_addr) end context 'when nameserver is not in the hosts file' do @@ -41,8 +41,8 @@ .with('localhost', Net::DNS::A) .and_return(packet) - expect(subject.address).to eq(ip_addr) - expect(subject.ttl).to eq(raw_ttl.seconds.from_now) + expect(resolve_response.address).to eq(ip_addr) + expect(resolve_response.ttl).to eq(raw_ttl.seconds.from_now) end context 'when nameserver is not in DNS' do @@ -55,7 +55,7 @@ .with('localhost', Net::DNS::A) .and_return(double(:packet, answer: [])) - expect { subject }.to raise_exception( + expect { resolve_response }.to raise_exception( described_class::UnresolvableNameserverError, 'could not resolve localhost' ) @@ -72,7 +72,7 @@ .with('localhost', Net::DNS::A) .and_raise(Net::DNS::Resolver::NoResponseError) - expect { subject }.to raise_exception( + expect { resolve_response }.to raise_exception( described_class::UnresolvableNameserverError, 'no response from DNS server(s)' ) diff --git a/spec/lib/gitlab/database/load_balancing/service_discovery/sampler_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/service_discovery/sampler_spec.rb similarity index 93% rename from spec/lib/gitlab/database/load_balancing/service_discovery/sampler_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/service_discovery/sampler_spec.rb index 9aa263524b14d41dc89b05be4824ec7d63a17c3d..b29ea1bc46c779f2c51ca230ed2fafcb9608d6e3 100644 --- a/spec/lib/gitlab/database/load_balancing/service_discovery/sampler_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/service_discovery/sampler_spec.rb @@ -27,8 +27,8 @@ it 'samples random ports across all hosts' do expect(sampler.sample(addresses)).to eq([ address_class.new("127.0.0.1", 6432), - address_class.new("127.0.0.2", 6435), - address_class.new("127.0.0.1", 6435) + address_class.new("127.0.0.2", 6435), + address_class.new("127.0.0.1", 6435) ]) end diff --git a/spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/service_discovery_spec.rb similarity index 89% rename from spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/service_discovery_spec.rb index 4d35e16a8651392177ab0d4f3a0c3779f11c80f6..b89533d58bca0e16065a5ecdb075c9edfd97a67f 100644 --- a/spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/service_discovery_spec.rb @@ -33,7 +33,7 @@ describe '#initialize' do describe ':record_type' do - subject do + subject(:service_discovery) do described_class.new( load_balancer, nameserver: 'localhost', @@ -46,14 +46,14 @@ context 'with a supported type' do let(:record_type) { 'SRV' } - it { expect(subject.record_type).to eq Net::DNS::SRV } + it { expect(service_discovery.record_type).to eq Net::DNS::SRV } end context 'with an unsupported type' do let(:record_type) { 'AAAA' } it 'raises an argument error' do - expect { subject }.to raise_error(ArgumentError, 'Unsupported record type: AAAA') + expect { service_discovery }.to raise_error(ArgumentError, 'Unsupported record type: AAAA') end end end @@ -88,7 +88,7 @@ expect(service).not_to receive(:sleep) - expect(Gitlab::ErrorTracking).not_to receive(:track_exception) + expect(Gitlab::Database::LoadBalancing::Callbacks).not_to receive(:track_exception) service.perform_service_discovery end @@ -96,7 +96,7 @@ context 'with StandardError' do before do - allow(Gitlab::ErrorTracking).to receive(:track_exception) + allow(Gitlab::Database::LoadBalancing::Callbacks).to receive(:track_exception) allow(service).to receive(:sleep) end @@ -139,7 +139,7 @@ .to receive(:refresh_if_necessary) .and_raise(error).exactly(described_class::MAX_DISCOVERY_RETRIES).times - expect(Gitlab::ErrorTracking) + expect(Gitlab::Database::LoadBalancing::Callbacks) .to receive(:track_exception) .with(error).exactly(described_class::MAX_DISCOVERY_RETRIES).times @@ -155,12 +155,7 @@ context 'when a refresh is necessary' do before do allow(service) - .to receive(:addresses_from_load_balancer) - .and_return(%w[localhost]) - - allow(service) - .to receive(:addresses_from_dns) - .and_return([10, [address_foo, address_bar]]) + .to receive_messages(addresses_from_load_balancer: %w[localhost], addresses_from_dns: [10, [address_foo, address_bar]]) end it 'refreshes the load balancer hosts' do @@ -175,12 +170,7 @@ context 'when a refresh is not necessary' do before do allow(service) - .to receive(:addresses_from_load_balancer) - .and_return(%w[localhost]) - - allow(service) - .to receive(:addresses_from_dns) - .and_return([10, %w[localhost]]) + .to receive_messages(addresses_from_load_balancer: %w[localhost], addresses_from_dns: [10, %w[localhost]]) end it 'does not refresh the load balancer hosts' do @@ -197,6 +187,11 @@ allow(service) .to receive(:load_balancer) .and_return(load_balancer) + + # TODO: real feature flag stubbing + allow(Gitlab::Database::LoadBalancing::Callbacks).to receive(:feature_enabled?) + .with(:load_balancer_replace_hosts, :current_pod) + .and_return(true) end let(:address_foo) { described_class::Address.new('foo') } @@ -251,7 +246,7 @@ end it 'does not log any load balancing event' do - expect(::Gitlab::Database::LoadBalancing::Logger).not_to receive(:info) + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:info) service.replace_hosts([address_foo, address_bar]) end @@ -454,15 +449,15 @@ let(:refresh_thread) { nil } let(:last_run_timestamp) { nil } - subject { service.log_refresh_thread_interruption } + subject(:log_refresh_thread_interruption) { service.log_refresh_thread_interruption } context 'without refresh thread timestamp' do it 'does not log any interruption' do expect(service.refresh_thread_last_run).to be_nil - expect(Gitlab::Database::LoadBalancing::Logger).not_to receive(:error) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:error) - subject + log_refresh_thread_interruption end end @@ -470,9 +465,9 @@ let(:last_run_timestamp) { Time.current } it 'does not log if last run time plus delta is in future' do - expect(Gitlab::Database::LoadBalancing::Logger).not_to receive(:error) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:error) - subject + log_refresh_thread_interruption end context 'with way past last run timestamp' do @@ -482,22 +477,22 @@ it 'does not log if the interruption is already logged' do service.refresh_thread_interruption_logged = true - expect(Gitlab::Database::LoadBalancing::Logger).not_to receive(:error) + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:error) - subject + log_refresh_thread_interruption end it 'logs the error if the interruption was not logged before' do expect(service.refresh_thread_interruption_logged).not_to be_present - expect(Gitlab::Database::LoadBalancing::Logger).to receive(:error).with( + expect(Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:error).with( event: :service_discovery_refresh_thread_interrupt, refresh_thread_last_run: last_run_timestamp, thread_status: refresh_thread.status.to_s, thread_backtrace: 'backtrace\nfoo' ) - subject + log_refresh_thread_interruption expect(service.refresh_thread_interruption_logged).to be_truthy end @@ -543,8 +538,8 @@ end gentle_disconnected_hosts = service.load_balancer.host_list.hosts.map { |h| "#{h.host}:#{h.port}" } - allow(::Gitlab::Database::LoadBalancing::Logger).to receive(:info).and_call_original - expect(::Gitlab::Database::LoadBalancing::Logger).to receive(:info) + allow(::Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:info).and_call_original + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:info) .with(hash_including( event: :host_list_disconnection, gentle_disconnected_hosts: gentle_disconnected_hosts, @@ -565,8 +560,8 @@ expect(connection).to receive(:steal!).and_call_original allow(service).to receive(:addresses_from_dns).and_return([Gitlab::Database::LoadBalancing::Resolver::FAR_FUTURE_TTL, []]) - allow(::Gitlab::Database::LoadBalancing::Logger).to receive(:info).and_call_original - expect(::Gitlab::Database::LoadBalancing::Logger).to receive(:info) + allow(::Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:info).and_call_original + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:info) .with(hash_including( event: :host_list_disconnection, force_disconnected_hosts: ["#{host.host}:#{host.port}"] diff --git a/spec/lib/gitlab/database/load_balancing/session_map_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/session_map_spec.rb similarity index 68% rename from spec/lib/gitlab/database/load_balancing/session_map_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/session_map_spec.rb index 929fc0564248072c3c2c4bb738ece3272e526eff..c3a230cd05917d63f5898d0ea09764ab3f35dfbf 100644 --- a/spec/lib/gitlab/database/load_balancing/session_map_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/session_map_spec.rb @@ -37,10 +37,9 @@ context 'when using a rake runtime' do let(:pri_session) { Gitlab::Database::LoadBalancing::Session.new } - let(:pri_lb) { instance_double('Gitlab::Database::LoadBalancing::LoadBalancer', name: :primary) } + let(:pri_lb) { instance_double(Gitlab::Database::LoadBalancing::LoadBalancer, name: :primary) } before do - allow(Gitlab::Runtime).to receive(:rake?).and_return(true) sm = described_class.new sm.session_map[:primary] = pri_session RequestStore[described_class::CACHE_KEY] = sm @@ -56,48 +55,29 @@ end context 'when receiving invalid db type' do - let(:pri_lb) { instance_double('Gitlab::Database::LoadBalancing::LoadBalancer', name: :primary) } - let(:invalid_lb) { instance_double('Gitlab::Database::LoadBalancing::LoadBalancer', name: :invalid) } + let(:pri_lb) { instance_double(Gitlab::Database::LoadBalancing::LoadBalancer, name: :primary) } + let(:invalid_lb) { instance_double(Gitlab::Database::LoadBalancing::LoadBalancer, name: :invalid) } subject(:current) { described_class.current(lb) } - Gitlab::Runtime::AVAILABLE_RUNTIMES.each do |runtime| - context "when using #{runtime} runtime" do - before do - allow(Gitlab::Runtime).to receive(runtime).and_return(true) - allow(Gitlab::Runtime).to receive(:safe_identify).and_return(runtime) - end - - context 'when db is invalid' do - let(:lb) { instance_double('Gitlab::Database::LoadBalancing::LoadBalancer', name: :invalid) } - - it 'raises error' do - expect do - current - end.to raise_error(instance_of(Gitlab::Database::LoadBalancing::SessionMap::InvalidLoadBalancerNameError)) - end - end + context 'when db is invalid' do + let(:lb) { instance_double(Gitlab::Database::LoadBalancing::LoadBalancer, name: :invalid) } - context 'when db is primary' do - let(:lb) { instance_double('Gitlab::Database::LoadBalancing::LoadBalancer', name: :primary) } - - it 'reports error without raising' do - expect(Gitlab::ErrorTracking).to receive(:track_exception) - .with(an_instance_of(Gitlab::Database::LoadBalancing::SessionMap::InvalidLoadBalancerNameError)) - expect(current).to be_instance_of(Gitlab::Database::LoadBalancing::Session) - end - end + it 'raises error' do + expect do + current + end.to raise_error(instance_of(Gitlab::Database::LoadBalancing::SessionMap::InvalidLoadBalancerNameError)) end end - it 'handles unknown runtimes' do - allow(Gitlab::Runtime).to receive(:rake?).and_return(false) - allow(Gitlab::Runtime).to receive(:safe_identify).and_return(nil) + context 'when db is primary' do + let(:lb) { instance_double(Gitlab::Database::LoadBalancing::LoadBalancer, name: :primary) } - expect(described_class.current(pri_lb)).to be_instance_of(Gitlab::Database::LoadBalancing::Session) - expect do - described_class.current(invalid_lb) - end.to raise_error(instance_of(Gitlab::Database::LoadBalancing::SessionMap::InvalidLoadBalancerNameError)) + it 'reports error without raising' do + expect(Gitlab::Database::LoadBalancing::Callbacks).to receive(:track_exception) + .with(an_instance_of(Gitlab::Database::LoadBalancing::SessionMap::InvalidLoadBalancerNameError)) + expect(current).to be_instance_of(Gitlab::Database::LoadBalancing::Session) + end end end end @@ -110,12 +90,12 @@ it 'clears instance from RequestStore' do described_class.clear_session - expect(RequestStore[described_class::CACHE_KEY]).to eq(nil) + expect(RequestStore[described_class::CACHE_KEY]).to be_nil end end context 'when wrapping queries with load balancing sessions' do - let(:dbs) { Gitlab::Database.database_base_models.values } + let(:dbs) { Gitlab::Database::LoadBalancing.base_models } let(:names) { dbs.map { |m| m.load_balancer.name }.uniq } let(:scoped_session) { Gitlab::Database::LoadBalancing::ScopedSessions.new(dbs, {}) } @@ -163,15 +143,15 @@ end describe '.with_sessions' do - let(:main_lb) { instance_double('Gitlab::Database::LoadBalancing::LoadBalancer', name: :main) } - let(:ci_lb) { instance_double('Gitlab::Database::LoadBalancing::LoadBalancer', name: :ci) } - let(:sec_lb) { instance_double('Gitlab::Database::LoadBalancing::LoadBalancer', name: :sec) } - let(:invalid_lb) { instance_double('Gitlab::Database::LoadBalancing::LoadBalancer', name: :invalid) } + let(:main_lb) { instance_double(Gitlab::Database::LoadBalancing::LoadBalancer, name: :main) } + let(:ci_lb) { instance_double(Gitlab::Database::LoadBalancing::LoadBalancer, name: :ci) } + let(:sec_lb) { instance_double(Gitlab::Database::LoadBalancing::LoadBalancer, name: :sec) } + let(:invalid_lb) { instance_double(Gitlab::Database::LoadBalancing::LoadBalancer, name: :invalid) } - let(:main) { instance_double('ActiveRecord::Base', load_balancer: main_lb) } - let(:ci) { instance_double('ActiveRecord::Base', load_balancer: ci_lb) } - let(:sec) { instance_double('ActiveRecord::Base', load_balancer: sec_lb) } - let(:invalid) { instance_double('ActiveRecord::Base', load_balancer: invalid_lb) } + let(:main) { instance_double(ActiveRecord::Base, load_balancer: main_lb) } + let(:ci) { instance_double(ActiveRecord::Base, load_balancer: ci_lb) } + let(:sec) { instance_double(ActiveRecord::Base, load_balancer: sec_lb) } + let(:invalid) { instance_double(ActiveRecord::Base, load_balancer: invalid_lb) } let(:all_dbs) { [main, ci, sec] } let(:scoped_dbs) { [main, ci] } @@ -201,11 +181,11 @@ with_sessions.use_primary! scoped_dbs.each do |db| - expect(described_class.current(db.load_balancer).use_primary?).to eq(true) + expect(described_class.current(db.load_balancer).use_primary?).to be(true) end (all_dbs - scoped_dbs).each do |db| - expect(described_class.current(db.load_balancer).use_primary?).to eq(false) + expect(described_class.current(db.load_balancer).use_primary?).to be(false) end end end @@ -214,16 +194,16 @@ it 'applies use_primary to all scoped sessions' do with_sessions.use_primary do scoped_dbs.each do |db| - expect(described_class.current(db.load_balancer).use_primary?).to eq(true) + expect(described_class.current(db.load_balancer).use_primary?).to be(true) end (all_dbs - scoped_dbs).each do |db| - expect(described_class.current(db.load_balancer).use_primary?).to eq(false) + expect(described_class.current(db.load_balancer).use_primary?).to be(false) end end all_dbs.each do |db| - expect(described_class.current(db.load_balancer).use_primary?).to eq(false) + expect(described_class.current(db.load_balancer).use_primary?).to be(false) end end end @@ -236,13 +216,13 @@ end scoped_dbs.each do |db| - expect(described_class.current(db.load_balancer).performed_write?).to eq(true) - expect(described_class.current(db.load_balancer).use_primary?).to eq(false) + expect(described_class.current(db.load_balancer).performed_write?).to be(true) + expect(described_class.current(db.load_balancer).use_primary?).to be(false) end (all_dbs - scoped_dbs).each do |db| - expect(described_class.current(db.load_balancer).performed_write?).to eq(true) - expect(described_class.current(db.load_balancer).use_primary?).to eq(true) + expect(described_class.current(db.load_balancer).performed_write?).to be(true) + expect(described_class.current(db.load_balancer).use_primary?).to be(true) end end end @@ -252,16 +232,16 @@ it 'applies use_replicas_for_read_queries to all scoped sessions' do with_sessions.use_replicas_for_read_queries do scoped_dbs.each do |db| - expect(described_class.current(db.load_balancer).use_replicas_for_read_queries?).to eq(true) + expect(described_class.current(db.load_balancer).use_replicas_for_read_queries?).to be(true) end (all_dbs - scoped_dbs).each do |db| - expect(described_class.current(db.load_balancer).use_replicas_for_read_queries?).to eq(false) + expect(described_class.current(db.load_balancer).use_replicas_for_read_queries?).to be(false) end end all_dbs.each do |db| - expect(described_class.current(db.load_balancer).use_replicas_for_read_queries?).to eq(false) + expect(described_class.current(db.load_balancer).use_replicas_for_read_queries?).to be(false) end end end @@ -270,16 +250,16 @@ it 'applies fallback_to_replicas_for_ambiguous_queries to all scoped sessions' do with_sessions.fallback_to_replicas_for_ambiguous_queries do scoped_dbs.each do |db| - expect(described_class.current(db.load_balancer).fallback_to_replicas_for_ambiguous_queries?).to eq(true) + expect(described_class.current(db.load_balancer).fallback_to_replicas_for_ambiguous_queries?).to be(true) end (all_dbs - scoped_dbs).each do |db| - expect(described_class.current(db.load_balancer).fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(described_class.current(db.load_balancer).fallback_to_replicas_for_ambiguous_queries?).to be(false) end end all_dbs.each do |db| - expect(described_class.current(db.load_balancer).fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(described_class.current(db.load_balancer).fallback_to_replicas_for_ambiguous_queries?).to be(false) end end end diff --git a/spec/lib/gitlab/database/load_balancing/session_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/session_spec.rb similarity index 82% rename from spec/lib/gitlab/database/load_balancing/session_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/session_spec.rb index b83f175c0ccda6fe74c9dfa8c199d9589baa0768..a32b85f59052339d70d4abaa945f157ac236f1d1 100644 --- a/spec/lib/gitlab/database/load_balancing/session_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/session_spec.rb @@ -9,11 +9,11 @@ instance.use_primary! - expect(instance.use_primary?).to eq(true) + expect(instance.use_primary?).to be(true) end it 'returns false when a secondary should be used' do - expect(described_class.new.use_primary?).to eq(false) + expect(described_class.new.use_primary?).to be(false) end it 'returns true when a write was performed' do @@ -21,7 +21,7 @@ instance.write! - expect(instance.use_primary?).to eq(true) + expect(instance.use_primary?).to be(true) end end @@ -36,7 +36,7 @@ it 'restores state after use' do expect { |blk| instance.use_primary(&blk) }.to yield_with_no_args - expect(instance.use_primary?).to eq(true) + expect(instance.use_primary?).to be(true) end end @@ -44,14 +44,14 @@ it 'restores state after use' do expect { |blk| instance.use_primary(&blk) }.to yield_with_no_args - expect(instance.use_primary?).to eq(false) + expect(instance.use_primary?).to be(false) end end it 'uses primary during block' do expect do |blk| instance.use_primary do - expect(instance.use_primary?).to eq(true) + expect(instance.use_primary?).to be(true) # call yield probe blk.to_proc.call @@ -64,7 +64,7 @@ instance.write! end - expect(instance.use_primary?).to eq(true) + expect(instance.use_primary?).to be(true) end end @@ -74,7 +74,7 @@ instance.write! - expect(instance.performed_write?).to eq(true) + expect(instance.performed_write?).to be(true) end end @@ -85,7 +85,7 @@ instance.ignore_writes { instance.write! } expect(instance).not_to be_using_primary - expect(instance.performed_write?).to eq true + expect(instance.performed_write?).to be true end it 'does not prevent using primary if an exception is raised' do @@ -108,31 +108,31 @@ it 'sets the flag inside the block' do expect do |blk| instance.use_replicas_for_read_queries do - expect(instance.use_replicas_for_read_queries?).to eq(true) + expect(instance.use_replicas_for_read_queries?).to be(true) # call yield probe blk.to_proc.call end end.to yield_control - expect(instance.use_replicas_for_read_queries?).to eq(false) + expect(instance.use_replicas_for_read_queries?).to be(false) end it 'restores state after use' do expect do |blk| instance.use_replicas_for_read_queries do instance.use_replicas_for_read_queries do - expect(instance.use_replicas_for_read_queries?).to eq(true) + expect(instance.use_replicas_for_read_queries?).to be(true) # call yield probe blk.to_proc.call end - expect(instance.use_replicas_for_read_queries?).to eq(true) + expect(instance.use_replicas_for_read_queries?).to be(true) end end.to yield_control - expect(instance.use_replicas_for_read_queries?).to eq(false) + expect(instance.use_replicas_for_read_queries?).to be(false) end context 'when primary was used before' do @@ -143,14 +143,14 @@ it 'sets the flag inside the block' do expect do |blk| instance.use_replicas_for_read_queries do - expect(instance.use_replicas_for_read_queries?).to eq(true) + expect(instance.use_replicas_for_read_queries?).to be(true) # call yield probe blk.to_proc.call end end.to yield_control - expect(instance.use_replicas_for_read_queries?).to eq(false) + expect(instance.use_replicas_for_read_queries?).to be(false) end end @@ -162,14 +162,14 @@ it 'sets the flag inside the block' do expect do |blk| instance.use_replicas_for_read_queries do - expect(instance.use_replicas_for_read_queries?).to eq(true) + expect(instance.use_replicas_for_read_queries?).to be(true) # call yield probe blk.to_proc.call end end.to yield_control - expect(instance.use_replicas_for_read_queries?).to eq(false) + expect(instance.use_replicas_for_read_queries?).to be(false) end end end @@ -180,31 +180,31 @@ it 'sets the flag inside the block' do expect do |blk| instance.fallback_to_replicas_for_ambiguous_queries do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(true) # call yield probe blk.to_proc.call end end.to yield_control - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end it 'restores state after use' do expect do |blk| instance.fallback_to_replicas_for_ambiguous_queries do instance.fallback_to_replicas_for_ambiguous_queries do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(true) # call yield probe blk.to_proc.call end - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(true) end end.to yield_control - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end context 'when primary was used before' do @@ -213,18 +213,18 @@ end it 'uses primary during block' do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) expect do |blk| instance.fallback_to_replicas_for_ambiguous_queries do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) # call yield probe blk.to_proc.call end end.to yield_control - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end end @@ -234,82 +234,82 @@ end it 'uses primary during block' do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) expect do |blk| instance.fallback_to_replicas_for_ambiguous_queries do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) # call yield probe blk.to_proc.call end end.to yield_control - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end end context 'when primary was used inside the block' do it 'uses primary aterward' do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) instance.fallback_to_replicas_for_ambiguous_queries do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(true) instance.use_primary! - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end it 'restores state after use' do instance.fallback_to_replicas_for_ambiguous_queries do instance.fallback_to_replicas_for_ambiguous_queries do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(true) instance.use_primary! - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end end context 'when a write was performed inside the block' do it 'uses primary aterward' do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) instance.fallback_to_replicas_for_ambiguous_queries do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(true) instance.write! - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end it 'restores state after use' do instance.fallback_to_replicas_for_ambiguous_queries do instance.fallback_to_replicas_for_ambiguous_queries do - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(true) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(true) instance.write! - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end - expect(instance.fallback_to_replicas_for_ambiguous_queries?).to eq(false) + expect(instance.fallback_to_replicas_for_ambiguous_queries?).to be(false) end end end diff --git a/spec/lib/gitlab/database/load_balancing/setup_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/setup_spec.rb similarity index 96% rename from spec/lib/gitlab/database/load_balancing/setup_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/setup_spec.rb index fa6d71bca7fab962ba1910564db63b562324a02a..98f80ba25aad77c4bf09472bb5e65171bfff7f1a 100644 --- a/spec/lib/gitlab/database/load_balancing/setup_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/setup_spec.rb @@ -30,7 +30,7 @@ .with('test', 'main', { host: 'localhost', prepared_statements: false, - pool: Gitlab::Database.default_pool_size + pool: Gitlab::Database::LoadBalancing.default_pool_size }) .and_call_original @@ -119,7 +119,7 @@ end end - context 'uses correct base models', :reestablished_active_record_base do + context 'when using correct base models', :reestablished_active_record_base do using RSpec::Parameterized::TableSyntax let(:ci_class) do @@ -156,7 +156,7 @@ def self.name end # Make load balancer to force init with a dedicated replicas connections - models.each do |_, model| + models.each_value do |model| described_class.new(model).tap do |subject| subject.configuration.hosts = [subject.configuration.db_config.host] subject.setup diff --git a/spec/lib/gitlab/database/load_balancing/srv_resolver_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/srv_resolver_spec.rb similarity index 75% rename from spec/lib/gitlab/database/load_balancing/srv_resolver_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/srv_resolver_spec.rb index 6ac0608d4851de8bc46072513861c4c2939fb589..4ab17457255978a37f5a33aac823029fc034778b 100644 --- a/spec/lib/gitlab/database/load_balancing/srv_resolver_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/srv_resolver_spec.rb @@ -9,11 +9,11 @@ describe '#address_for' do let(:host) { 'patroni-02-db-gstg.node.east-us-2.consul.' } - subject { described_class.new(resolver, additional).address_for(host) } + subject(:address) { described_class.new(resolver, additional).address_for(host) } context 'when additional section contains an A record' do it 'returns an IP4 address' do - expect(subject).to eq(IPAddr.new('10.224.29.102')) + expect(address).to eq(IPAddr.new('10.224.29.102')) end end @@ -22,7 +22,7 @@ let(:additional) { dns_response_packet_from_fixture('a_with_aaaa_rr_in_additional_section').additional } it 'returns an IP6 address' do - expect(subject).to eq(IPAddr.new('2001:503:a83e::2:30')) + expect(address).to eq(IPAddr.new('2001:503:a83e::2:30')) end end @@ -35,7 +35,7 @@ end it 'returns an IP4 address' do - expect(subject).to eq(IPAddr.new('10.224.29.102')) + expect(address).to eq(IPAddr.new('10.224.29.102')) end end @@ -45,15 +45,16 @@ end it 'returns an IP6 address' do - expect(subject).to eq(IPAddr.new('2a00:1450:400e:80a::200e')) + expect(address).to eq(IPAddr.new('2a00:1450:400e:80a::200e')) end end end end def dns_response_packet_from_fixture(fixture_name) - fixture = File.read(Rails.root + "spec/fixtures/dns/#{fixture_name}.json") - encoded_payload = Gitlab::Json.parse(fixture)['payload'] + path = File.join(File.dirname(__FILE__), "../../../fixtures/dns/#{fixture_name}.json") + fixture = File.read(path) + encoded_payload = JSON.parse(fixture)['payload'] payload = Base64.decode64(encoded_payload) Net::DNS::Packet.parse(payload) diff --git a/spec/lib/gitlab/database/load_balancing/sticking_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/sticking_spec.rb similarity index 78% rename from spec/lib/gitlab/database/load_balancing/sticking_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/sticking_spec.rb index 5462778fd62dd347ce1fbd6821cdfc5c1a250e54..92de1a723a69d63e1ee72c146eb75419a91207c6 100644 --- a/spec/lib/gitlab/database/load_balancing/sticking_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/sticking_spec.rb @@ -13,17 +13,14 @@ described_class.new(load_balancer) end - let(:redis) { instance_double(::Gitlab::Redis::MultiStore) } - before do Gitlab::Database::LoadBalancing::SessionMap.clear_session - allow(::Gitlab::Redis::DbLoadBalancing).to receive(:with).and_yield(redis) allow(ActiveRecord::Base.load_balancer) .to receive(:primary_write_location) .and_return(primary_write_location) - allow(redis).to receive(:get) + allow(Gitlab::Database::LoadBalancing::Callbacks).to receive(:get_wal_for) .with("database-load-balancing/write-location/#{load_balancer.name}/user/42") .and_return(last_write_location) end @@ -43,17 +40,17 @@ it 'returns true' do expect(load_balancer).not_to receive(:select_up_to_date_host) - expect(sticking.find_caught_up_replica(:user, 42)).to eq(true) + expect(sticking.find_caught_up_replica(:user, 42)).to be(true) end context 'when use_primary_on_empty_location is true' do it 'returns false, does not unstick and calls use_primary!' do expect(load_balancer).not_to receive(:select_up_to_date_host) - expect(redis).not_to receive(:del) + expect(Gitlab::Database::LoadBalancing::Callbacks).not_to receive(:del_wal_for) expect(::Gitlab::Database::LoadBalancing::SessionMap.current(load_balancer)).to receive(:use_primary!) - expect(sticking.find_caught_up_replica(:user, 42, use_primary_on_empty_location: true)).to eq(false) + expect(sticking.find_caught_up_replica(:user, 42, use_primary_on_empty_location: true)).to be(false) end end end @@ -63,11 +60,10 @@ expect(load_balancer).to receive(:select_up_to_date_host).with(last_write_location) .and_return(::Gitlab::Database::LoadBalancing::LoadBalancer::ALL_CAUGHT_UP) - expect(redis) - .to receive(:del) + expect(Gitlab::Database::LoadBalancing::Callbacks).to receive(:del_wal_for) .with("database-load-balancing/write-location/#{load_balancer.name}/user/42") - expect(sticking.find_caught_up_replica(:user, 42)).to eq(true) + expect(sticking.find_caught_up_replica(:user, 42)).to be(true) end end @@ -76,9 +72,9 @@ expect(load_balancer).to receive(:select_up_to_date_host).with(last_write_location) .and_return(::Gitlab::Database::LoadBalancing::LoadBalancer::ANY_CAUGHT_UP) - expect(redis).not_to receive(:del) + expect(Gitlab::Database::LoadBalancing::Callbacks).not_to receive(:del_wal_for) - expect(sticking.find_caught_up_replica(:user, 42)).to eq(true) + expect(sticking.find_caught_up_replica(:user, 42)).to be(true) end end @@ -89,18 +85,18 @@ end it 'returns false, does not unstick and calls use_primary!' do - expect(redis).not_to receive(:del) + expect(Gitlab::Database::LoadBalancing::Callbacks).not_to receive(:del_wal_for) expect(::Gitlab::Database::LoadBalancing::SessionMap.current(load_balancer)).to receive(:use_primary!) - expect(sticking.find_caught_up_replica(:user, 42)).to eq(false) + expect(sticking.find_caught_up_replica(:user, 42)).to be(false) end context 'when use_primary_on_failure is false' do it 'does not call use_primary!' do - expect(redis).not_to receive(:del) + expect(Gitlab::Database::LoadBalancing::Callbacks).not_to receive(:del_wal_for) expect(::Gitlab::Database::LoadBalancing::SessionMap.current(load_balancer)).not_to receive(:use_primary!) - expect(sticking.find_caught_up_replica(:user, 42, use_primary_on_failure: false)).to eq(false) + expect(sticking.find_caught_up_replica(:user, 42, use_primary_on_failure: false)).to be(false) end end end @@ -113,8 +109,7 @@ .and_return(false) ids.each do |id| - expect(redis) - .to receive(:set) + expect(Gitlab::Database::LoadBalancing::Callbacks).to receive(:set_wal_for) .with("database-load-balancing/write-location/#{load_balancer.name}/user/#{id}", 'the-primary-lsn', ex: 30) end @@ -146,12 +141,12 @@ describe '#log_database_sticking_operations_enabled?' do it 'returns true when the feature flag is enabled' do - stub_feature_flags(log_database_sticking_operations: true) + stub_load_balancer_feature_flags(log_database_sticking_operations: true) expect(sticking.send(:log_database_sticking_operations_enabled?)).to be true end it 'returns false when the feature flag is disabled' do - stub_feature_flags(log_database_sticking_operations: false) + stub_load_balancer_feature_flags(log_database_sticking_operations: false) expect(sticking.send(:log_database_sticking_operations_enabled?)).to be false end end @@ -162,7 +157,7 @@ context 'when logging is enabled and namespace is user' do before do - stub_feature_flags(log_database_sticking_operations: true) + stub_load_balancer_feature_flags(log_database_sticking_operations: true) allow(sticking).to receive(:with_primary_write_location).and_yield(location) allow(sticking).to receive(:set_write_location_for) @@ -170,7 +165,7 @@ end it 'logs the sticking operation with correct parameters' do - expect(::Gitlab::Database::LoadBalancing::Logger).to receive(:info).with( + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:info).with( event: :load_balancer_stick_logging, client_id: "#{namespace}/#{id}", stick_id: id, @@ -181,7 +176,7 @@ end it 'logs only the first ID for bulk sticking operations with correct parameters' do - expect(::Gitlab::Database::LoadBalancing::Logger).to receive(:info).with( + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger).to receive(:info).with( event: :load_balancer_stick_logging, client_id: "#{namespace}/#{ids.first}", stick_id: ids.first, @@ -195,7 +190,7 @@ context 'when logging is disabled' do before do - stub_feature_flags(log_database_sticking_operations: false) + stub_load_balancer_feature_flags(log_database_sticking_operations: false) allow(sticking).to receive(:with_primary_write_location).and_yield(location) allow(sticking).to receive(:set_write_location_for) @@ -203,7 +198,7 @@ end it 'does not log anything' do - expect(::Gitlab::Database::LoadBalancing::Logger).not_to receive(:info) + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:info) sticking.stick(namespace, id) end @@ -213,7 +208,7 @@ let(:namespace) { 'project' } before do - stub_feature_flags(log_database_sticking_operations: true) + stub_load_balancer_feature_flags(log_database_sticking_operations: true) allow(sticking).to receive(:with_primary_write_location).and_yield(location) allow(sticking).to receive(:set_write_location_for) @@ -221,7 +216,7 @@ end it 'does not log anything' do - expect(::Gitlab::Database::LoadBalancing::Logger).not_to receive(:info) + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger).not_to receive(:info) sticking.bulk_stick(namespace, ids) end diff --git a/spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/transaction_leaking_spec.rb similarity index 89% rename from spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/transaction_leaking_spec.rb index 76eb473c07bcadf3f8e82b6994e47d573176e160..16a09912df823b0b80938bb5c88628b54b029930 100644 --- a/spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing/transaction_leaking_spec.rb @@ -17,7 +17,7 @@ Gitlab::Database::LoadBalancing::Setup.new(model).setup - model.connection.execute(<<~SQL) + model.connection.execute(<<~SQL.squish) CREATE TABLE IF NOT EXISTS #{test_table_name} (id SERIAL PRIMARY KEY, value INTEGER) SQL @@ -27,7 +27,7 @@ end after do - model.connection.execute(<<~SQL) + model.connection.execute(<<~SQL.squish) DROP TABLE IF EXISTS #{test_table_name} SQL @@ -43,7 +43,7 @@ def execute(conn) # This will result in a PG error, which is not raised. # Instead, we retry the statement on a fresh connection (where the pid is different and it does nothing) # and the load balancer continues with a fresh connection and no transaction if a transaction was open previously - conn.execute(<<~SQL) + conn.execute(<<~SQL.squish) SELECT CASE WHEN pg_backend_pid() = #{backend_pid} THEN pg_terminate_backend(#{backend_pid}) @@ -57,7 +57,7 @@ def execute(conn) context 'in a transaction' do it 'raises an exception when a retry would occur' do - expect(::Gitlab::Database::LoadBalancing::Logger) + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger) .not_to receive(:warn).with(hash_including(event: :transaction_leak)) expect do @@ -70,9 +70,9 @@ def execute(conn) context 'without a transaction' do it 'retries' do - expect(::Gitlab::Database::LoadBalancing::Logger) + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger) .not_to receive(:warn).with(hash_including(event: :transaction_leak)) - expect(::Gitlab::Database::LoadBalancing::Logger) + expect(::Gitlab::Database::LoadBalancing::Callbacks.logger) .to receive(:warn).with(hash_including(event: :read_write_retry)) expect { execute(model.connection) }.not_to raise_error diff --git a/spec/lib/gitlab/database/load_balancing_spec.rb b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing_spec.rb similarity index 96% rename from spec/lib/gitlab/database/load_balancing_spec.rb rename to gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing_spec.rb index 92682896fac25f93c61af39edee6c1685215199a..fff6df47af910801f5d363b6f7a269e993894d1d 100644 --- a/spec/lib/gitlab/database/load_balancing_spec.rb +++ b/gems/gitlab-database-load_balancing/spec/gitlab/database/load_balancing_spec.rb @@ -3,22 +3,6 @@ require 'spec_helper' RSpec.describe Gitlab::Database::LoadBalancing, :suppress_gitlab_schemas_validate_connection, feature_category: :database do - describe '.base_models' do - it 'returns the models to apply load balancing to' do - models = described_class.base_models - - expect(models).to include(ActiveRecord::Base) - - if Gitlab::Database.has_config?(:ci) - expect(models).to include(Ci::ApplicationRecord) - end - end - - it 'returns the models as a frozen array' do - expect(described_class.base_models).to be_frozen - end - end - describe '.each_load_balancer' do it 'yields every load balancer to the supplied block' do lbs = [] @@ -44,7 +28,7 @@ allow(lb).to receive(:primary_only?).and_return(true) end - expect(described_class.primary_only?).to eq(true) + expect(described_class.primary_only?).to be(true) end it 'returns false if at least one has replicas' do @@ -52,7 +36,7 @@ allow(lb).to receive(:primary_only?).and_return(index != 0) end - expect(described_class.primary_only?).to eq(false) + expect(described_class.primary_only?).to be(false) end end @@ -497,7 +481,7 @@ def current_session end end - context 'custom connection handling' do + context 'with custom connection handling' do where(:queries, :expected_role) do [ # Reload cache. The schema loading queries should be handled by @@ -562,7 +546,7 @@ def current_session end end - context 'a write inside a transaction inside fallback_to_replicas_for_ambiguous_queries block' do + context 'with a write inside a transaction inside fallback_to_replicas_for_ambiguous_queries block' do it 'raises an exception' do expect do ::Gitlab::Database::LoadBalancing::SessionMap diff --git a/gems/gitlab-database-load_balancing/spec/spec_helper.rb b/gems/gitlab-database-load_balancing/spec/spec_helper.rb index 71faf49cce0c789072005377ad4e8e977a6144a9..80e3f60b24d33acf9ab752c0ccccf1958160ed17 100644 --- a/gems/gitlab-database-load_balancing/spec/spec_helper.rb +++ b/gems/gitlab-database-load_balancing/spec/spec_helper.rb @@ -8,9 +8,16 @@ require 'gitlab/utils/all' require 'gitlab/safe_request_store' +require "gitlab/database/load_balancing" + +require_relative 'support/database_replica' +require_relative 'support/application_record' +require_relative 'support/stub_load_balancer_feature_flags' + RSpec.configure do |config| include StubRails include NextInstanceOf + include StubLoadBalancerFeatureFlags # Enable flags like --only-failures and --next-failure config.example_status_persistence_file_path = ".rspec_status" @@ -25,4 +32,17 @@ config.around(:example, :request_store) do |example| ::Gitlab::SafeRequestStore.ensure_request_store { example.run } end + + config.before(:all) do + Gitlab::Database::LoadBalancing.configure! do |load_balancing| + load_balancing.default_pool_size = 20 + # TODO support sec? Or abstract away from it + load_balancing.base_models = [ActiveRecord::Base, Ci::ApplicationRecord, Sec::ApplicationRecord] + load_balancing.all_database_names = %w[main ci sec] + + load_balancing.base_models.each do |model| + Gitlab::Database::LoadBalancing::Setup.new(model).setup + end + end + end end diff --git a/gems/gitlab-database-load_balancing/spec/support/application_record.rb b/gems/gitlab-database-load_balancing/spec/support/application_record.rb new file mode 100644 index 0000000000000000000000000000000000000000..07f9179ae62e315ac3ff1f7c63510185740bcf93 --- /dev/null +++ b/gems/gitlab-database-load_balancing/spec/support/application_record.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end + +module Ci + class ApplicationRecord < ApplicationRecord + self.abstract_class = true + end +end + +module Sec + class ApplicationRecord < ApplicationRecord + self.abstract_class = true + end +end diff --git a/gems/gitlab-database-load_balancing/spec/support/database_replica.rb b/gems/gitlab-database-load_balancing/spec/support/database_replica.rb new file mode 100644 index 0000000000000000000000000000000000000000..7df712e2fdac7c6396bfc6428d457a8f48964b2f --- /dev/null +++ b/gems/gitlab-database-load_balancing/spec/support/database_replica.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +RSpec.configure do |config| + config.around(:each, :database_replica) do |example| + old_proxies = {} + + Gitlab::Database::LoadBalancing.base_models.each do |model| + old_proxies[model] = [model.load_balancer, model.connection, model.sticking] + + config = Gitlab::Database::LoadBalancing::Configuration + .new(model, [model.connection_db_config.configuration_hash[:host]]) + + model.load_balancer = Gitlab::Database::LoadBalancing::LoadBalancer.new(config) + model.sticking = Gitlab::Database::LoadBalancing::Sticking.new(model.load_balancer) + model.connection = Gitlab::Database::LoadBalancing::ConnectionProxy.new(model.load_balancer) + end + + Gitlab::Database::LoadBalancing::SessionMap.clear_session + + example.run + + Gitlab::Database::LoadBalancing::SessionMap.clear_session + + old_proxies.each do |model, proxy| + model.load_balancer, model.connection, model.sticking = proxy + end + end +end diff --git a/gems/gitlab-database-load_balancing/spec/support/stub_load_balancer_feature_flags.rb b/gems/gitlab-database-load_balancing/spec/support/stub_load_balancer_feature_flags.rb new file mode 100644 index 0000000000000000000000000000000000000000..8d2ff99626190b8c905d997f9b4e729f48312a0f --- /dev/null +++ b/gems/gitlab-database-load_balancing/spec/support/stub_load_balancer_feature_flags.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module StubLoadBalancerFeatureFlags + def stub_load_balancer_feature_flags(flag_mappings) + flag_mappings.each do |flag, value| + allow(Gitlab::Database::LoadBalancing::Callbacks).to receive(:feature_enabled?).with(flag, anything).and_return(value) + end + end +end diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index 5d347f954d90bf703f5313aef7801e2a9c6a2e7a..3c8cc7b5287a72bd720c50974e09d06977e73d50 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'gitlab/database/load_balancing' + module Gitlab module Database MAIN_DATABASE_NAME = 'main' @@ -298,27 +300,7 @@ def self.gitlab_schemas_for_connection(connection) end def self.db_config_for_connection(connection) - return unless connection - - # For a ConnectionProxy we want to avoid ambiguous db_config as it may - # sometimes default to replica so we always return the primary config - # instead. - if connection.is_a?(::Gitlab::Database::LoadBalancing::ConnectionProxy) - return connection.load_balancer.configuration.db_config - end - - # During application init we might receive `NullPool` - return unless connection.respond_to?(:pool) && - connection.pool.respond_to?(:db_config) - - db_config = connection.pool.db_config - db_config unless empty_config?(db_config) - end - - def self.empty_config?(db_config) - return true unless db_config - - db_config.is_a?(ActiveRecord::ConnectionAdapters::NullPool::NullConfig) + Gitlab::Database::LoadBalancing.db_config_for_connection(connection) end # At the moment, the connection can only be retrieved by diff --git a/scripts/feature_flags/used-feature-flags b/scripts/feature_flags/used-feature-flags index 0bb47a5f622e79a158fb081c9fc82ed35cb31d0d..7285d721f4c2e39f4958e74bfa9aa3eb37d08d90 100755 --- a/scripts/feature_flags/used-feature-flags +++ b/scripts/feature_flags/used-feature-flags @@ -71,6 +71,11 @@ Dir.glob(flag_definition_paths).each do |flag_definition_path| next end + if feature_flag_name.start_with?('load_balancer_') + puts "Skipping the #{feature_flag_name} feature flag since it starts with 'load_balancer_'." + next + end + all_flags[feature_flag_name] = File.exist?(File.join('tmp', 'feature_flags', "#{feature_flag_name}.used")) end diff --git a/spec/initializers/load_balancing_spec.rb b/spec/initializers/load_balancing_spec.rb index 9b7459bd2fcaa316872054741cb63e76fbbd8547..0b483d059cb23d3261ebdbe4188a7f3fd4fa06d8 100644 --- a/spec/initializers/load_balancing_spec.rb +++ b/spec/initializers/load_balancing_spec.rb @@ -2,18 +2,16 @@ require 'spec_helper' -RSpec.describe 'load_balancing', :delete, :reestablished_active_record_base, feature_category: :cell do - subject(:initialize_load_balancer) do - load Rails.root.join('config/initializers/load_balancing.rb') - end - - before do - # Stub out middleware call, as not idempotent - allow(Gitlab::Application.instance.middleware).to receive(:use) - end +RSpec.describe 'load_balancing', feature_category: :cell do + context 'with replica hosts configured', :delete, :reestablished_active_record_base do + subject(:initialize_load_balancer) do + load Rails.root.join('config/initializers/load_balancing.rb') + end - context 'with replica hosts configured' do before do + # Stub out middleware call, as not idempotent + allow(Gitlab::Application.instance.middleware).to receive(:use) + # Setup host-based load balancing # Patch in our load balancer config, simply pointing at the test database twice allow(Gitlab::Database::LoadBalancing::Configuration).to receive(:for_model) do |base_model| @@ -97,4 +95,91 @@ def simulate_puma_worker end end end + + describe Gitlab::Database::LoadBalancing do + describe '.base_models' do + it 'returns the models to apply load balancing to' do + models = described_class.base_models + + expect(models).to include(ActiveRecord::Base) + expect(models).to include(Ci::ApplicationRecord) if Gitlab::Database.has_config?(:ci) + end + + it 'returns the models as a frozen array' do + expect(described_class.base_models).to be_frozen + end + end + end + + describe Gitlab::Database::LoadBalancing::Callbacks do + describe '.metrics_host_gauge' do + it 'sets prometheus metric', :reestablished_active_record_base do + expect(described_class).to receive(:metrics_host_gauge) + .with({}, 1).and_call_original + + # Metric will be set once configured host list: + # - 1 host since we only have primary + # - this test will fail if running CI against replicas + model = Gitlab::Database::LoadBalancing.base_models.first + Gitlab::Database::LoadBalancing::Setup.new(model).setup + + metric = ::Prometheus::Client.registry.get(:db_load_balancing_hosts) + expect(metric).not_to be_nil + expect(metric.values[{}].get).to eq(1) + end + end + + context 'when sticking connection', :database_replica, :redis, :request_store do + let(:key) { 'database-load-balancing/write-location/main/user/100' } + let(:wal) { '0/D525E3A8' } + + it 'does set latest WAL and unstick once caught-up' do + allow(ApplicationRecord.load_balancer).to receive(:primary_write_location).and_return(wal) + + expect(described_class).to receive(:set_wal_for) + .with(key, wal, ex: 30) + .and_call_original + + expect { ApplicationRecord.sticking.stick(:user, 100) } + .to change { described_class.get_wal_for(key) }.from(nil).to(wal) + + expect(described_class).to receive(:del_wal_for) + .with(key).and_call_original + + expect(ApplicationRecord.load_balancer).to receive(:select_up_to_date_host) + .with(wal).and_return(Gitlab::Database::LoadBalancing::LoadBalancer::ALL_CAUGHT_UP) + + expect { ApplicationRecord.sticking.find_caught_up_replica(:user, 100) } + .to change { described_class.get_wal_for(key) }.from(wal).to(nil) + end + + describe '.set_wal_for' do + it 'does get latest set value' do + expect { described_class.set_wal_for(key, wal, ex: 30) } + .to change { described_class.get_wal_for(key) }.from(nil).to(wal) + end + end + + describe '.del_wal_for' do + it 'does delete set value' do + described_class.set_wal_for(key, wal, ex: 30) + + expect { described_class.del_wal_for(key) } + .to change { described_class.get_wal_for(key) }.from(wal).to(nil) + end + end + end + + describe '.track_exception' do + it 'forwards exception to ErrorTracking' do + expect(Gitlab::ErrorTracking).to receive(:track_exception) + .with(Gitlab::Utils::ConcurrentRubyThreadIsUsedError) + + Gitlab::Utils.restrict_within_concurrent_ruby do + expect { ApplicationRecord.connection.execute("SELECT 1") } + .to raise_error(Gitlab::Utils::ConcurrentRubyThreadIsUsedError) + end + end + end + end end