From a750116efe1c1d9f9b6a008887044f0e8aa7be2d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= <ayufan@ayufan.eu>
Date: Wed, 22 Nov 2023 10:39:09 +0100
Subject: [PATCH] Introduce `gitlab-database-load_balancing` stub

---
 .gitlab/ci/gitlab-gems.gitlab-ci.yml          |   3 +
 .gitlab/ci/templates/gem.gitlab-ci.yml        |   1 +
 Gemfile.lock                                  |   1 +
 gems/gem-pg.gitlab-ci.yml                     |  80 +++++
 .../gitlab-database-load_balancing/.gitignore |  11 +
 .../.gitlab-ci.yml                            |   4 +
 gems/gitlab-database-load_balancing/.rspec    |   3 +
 .../.rubocop.yml                              |   6 +
 gems/gitlab-database-load_balancing/Gemfile   |  14 +
 .../Gemfile.lock                              | 303 ++++++++++++++++++
 gems/gitlab-database-load_balancing/README.md |   3 +
 gems/gitlab-database-load_balancing/Rakefile  |  12 +
 .../gitlab-database-load_balancing.gemspec    |  31 ++
 .../spec/spec_helper.rb                       |  28 ++
 gems/gitlab-http/Gemfile.lock                 |   8 +-
 gems/gitlab-rspec/Gemfile.lock                |   6 +
 gems/gitlab-rspec/gitlab-rspec.gemspec        |   1 +
 gems/gitlab-rspec/lib/gitlab/rspec/all.rb     |   1 +
 .../lib/gitlab/rspec/stub_rails.rb            |  57 ++++
 gems/gitlab-utils/Gemfile.lock                |   6 +
 gems/rspec_flaky/Gemfile.lock                 |  22 +-
 21 files changed, 598 insertions(+), 3 deletions(-)
 create mode 100644 gems/gem-pg.gitlab-ci.yml
 create mode 100644 gems/gitlab-database-load_balancing/.gitignore
 create mode 100644 gems/gitlab-database-load_balancing/.gitlab-ci.yml
 create mode 100644 gems/gitlab-database-load_balancing/.rspec
 create mode 100644 gems/gitlab-database-load_balancing/.rubocop.yml
 create mode 100644 gems/gitlab-database-load_balancing/Gemfile
 create mode 100644 gems/gitlab-database-load_balancing/Gemfile.lock
 create mode 100644 gems/gitlab-database-load_balancing/README.md
 create mode 100644 gems/gitlab-database-load_balancing/Rakefile
 create mode 100644 gems/gitlab-database-load_balancing/gitlab-database-load_balancing.gemspec
 create mode 100644 gems/gitlab-database-load_balancing/spec/spec_helper.rb
 create mode 100644 gems/gitlab-rspec/lib/gitlab/rspec/stub_rails.rb

diff --git a/.gitlab/ci/gitlab-gems.gitlab-ci.yml b/.gitlab/ci/gitlab-gems.gitlab-ci.yml
index 99c9242ee665..650a5cb572a1 100644
--- a/.gitlab/ci/gitlab-gems.gitlab-ci.yml
+++ b/.gitlab/ci/gitlab-gems.gitlab-ci.yml
@@ -35,3 +35,6 @@ include:
   - local: .gitlab/ci/templates/gem.gitlab-ci.yml
     inputs:
       gem_name: "gitlab-secret_detection"
+  - local: .gitlab/ci/templates/gem.gitlab-ci.yml
+    inputs:
+      gem_name: "gitlab-database-load_balancing"
diff --git a/.gitlab/ci/templates/gem.gitlab-ci.yml b/.gitlab/ci/templates/gem.gitlab-ci.yml
index 449150bde6cc..3faf56daace6 100644
--- a/.gitlab/ci/templates/gem.gitlab-ci.yml
+++ b/.gitlab/ci/templates/gem.gitlab-ci.yml
@@ -21,6 +21,7 @@ spec:
           # Ensure dependency updates don't fail child pipelines: https://gitlab.com/gitlab-org/gitlab/-/issues/417428
         - "Gemfile.lock"
         - "gems/gem.gitlab-ci.yml"
+        - "gems/gem-pg.gitlab-ci.yml"
           # Ensure new cop in the monolith don't break internal gems Rubocop checks: https://gitlab.com/gitlab-org/gitlab/-/issues/419915
         - ".rubocop.yml"
         - "rubocop/**/*"
diff --git a/Gemfile.lock b/Gemfile.lock
index 3e4f4ee2686a..8b9e9e629afa 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -44,6 +44,7 @@ PATH
   remote: gems/gitlab-rspec
   specs:
     gitlab-rspec (0.1.0)
+      activerecord (>= 6.1, < 8)
       activesupport (>= 6.1, < 8)
       rspec (~> 3.0)
 
diff --git a/gems/gem-pg.gitlab-ci.yml b/gems/gem-pg.gitlab-ci.yml
new file mode 100644
index 000000000000..c48e18fa297c
--- /dev/null
+++ b/gems/gem-pg.gitlab-ci.yml
@@ -0,0 +1,80 @@
+# The template generates jobs for gems vendored in the main GitLab project
+# under `gem_path_prefix` (defaults to `gems/`).
+#
+# Inputs
+# - `gem_name`: The name of the gem, i.e. if the gem is located at `gems/gitlab-rspec`, `gem_name` should be set to `gitlab-rspec`.
+# - `gem_path_prefix`: The prefix of the gem path, i.e. if the gem is located at `vendor/gems/gitlab-rspec`, `gem_path_prefix` should be set to `vendor/gems/`. Defaults to `gems/`.
+spec:
+  inputs:
+    gem_name:
+    gem_path_prefix:
+      default: "gems/"
+---
+workflow:
+  name: '[$[[inputs.gem_name]] gem] Ruby $RUBY_VERSION pipeline'
+  rules:
+    - when: always
+
+variables:
+  BUNDLE_PATH: "vendor"
+  BUNDLE_FROZEN: "true"
+  GIT_DEPTH: "20"
+  # 'GIT_STRATEGY: clone' optimizes the pack-objects cache hit ratio
+  GIT_STRATEGY: "clone"
+  GIT_SUBMODULE_STRATEGY: "none"
+  GET_SOURCES_ATTEMPTS: "3"
+  # Default Ruby version for jobs that don't use .ruby_matrix
+  RUBY_VERSION: "3.0"
+
+default:
+  image: "ruby:${RUBY_VERSION}"
+  cache:
+    key: "$[[inputs.gem_name]]-${RUBY_VERSION}"
+    paths:
+      - "$[[inputs.gem_path_prefix]]$[[inputs.gem_name]]/vendor/ruby"
+  before_script:
+    - cp config/{database.yml.postgresql,database.yml}
+    - "sed -i 's/username: postgres$/username: gitlab/g' config/database.yml"
+    - "sed -i 's/password:\\s*$/password: password/g' config/database.yml"
+    - "sed -i 's/host: localhost$/host: postgres/g' config/database.yml"
+    - cd $[[inputs.gem_path_prefix]]$[[inputs.gem_name]]
+    - ruby -v                                   # Print out ruby version for debugging
+    - bundle_version=$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1 | sed -e 's/[[:space:]]//')
+    - gem install bundler --version "$bundle_version" --no-document # Bundler is not installed with the image
+    - bundle config                             # Show bundler configuration
+    - bundle install --jobs=$(nproc) --retry=3
+
+.ruby_matrix:
+  parallel:
+    matrix:
+      - RUBY_VERSION: ["3.0", "3.1", "3.2"]
+
+.ruby_and_postgres_matrix:
+  services:
+    - name: postgres:${POSTGRES_VERSION}
+      command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+  parallel:
+    matrix:
+      - RUBY_VERSION: ["3.0", "3.1", "3.2"]
+        POSTGRES_VERSION: ["12", "13", "14"]
+
+rubocop:
+  extends: .ruby_matrix
+  rules:
+    - exists: ["$[[inputs.gem_path_prefix]]$[[inputs.gem_name]]/.rubocop.yml"]
+  script:
+    - bundle exec rubocop
+
+rspec:
+  extends: .ruby_and_postgres_matrix
+  variables:
+    POSTGRES_USER: gitlab
+    POSTGRES_PASSWORD: password
+  script:
+    - RAILS_ENV=test bundle exec rspec
+  coverage: '/LOC \((\d+\.\d+%)\) covered.$/'
+  artifacts:
+    expire_in: 31d
+    when: always
+    paths:
+      - coverage/
diff --git a/gems/gitlab-database-load_balancing/.gitignore b/gems/gitlab-database-load_balancing/.gitignore
new file mode 100644
index 000000000000..b04a8c840df1
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/.gitignore
@@ -0,0 +1,11 @@
+/.bundle/
+/.yardoc
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
+
+# rspec failure tracking
+.rspec_status
diff --git a/gems/gitlab-database-load_balancing/.gitlab-ci.yml b/gems/gitlab-database-load_balancing/.gitlab-ci.yml
new file mode 100644
index 000000000000..6816d641291d
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/.gitlab-ci.yml
@@ -0,0 +1,4 @@
+include:
+  - local: gems/gem-pg.gitlab-ci.yml
+    inputs:
+      gem_name: "gitlab-database-load_balancing"
diff --git a/gems/gitlab-database-load_balancing/.rspec b/gems/gitlab-database-load_balancing/.rspec
new file mode 100644
index 000000000000..34c5164d9b56
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/.rspec
@@ -0,0 +1,3 @@
+--format documentation
+--color
+--require spec_helper
diff --git a/gems/gitlab-database-load_balancing/.rubocop.yml b/gems/gitlab-database-load_balancing/.rubocop.yml
new file mode 100644
index 000000000000..583fa8227eec
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/.rubocop.yml
@@ -0,0 +1,6 @@
+inherit_from:
+  - ../config/rubocop.yml
+
+Gemfile/MissingFeatureCategory:
+  Exclude:
+    - 'Gemfile'
diff --git a/gems/gitlab-database-load_balancing/Gemfile b/gems/gitlab-database-load_balancing/Gemfile
new file mode 100644
index 000000000000..05b508b1333f
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/Gemfile
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+# Specify your gem's dependencies in gitlab-safe_request_store.gemspec
+gemspec
+
+group :development, :test do
+  gem 'gitlab-rspec', path: '../gitlab-rspec'
+end
+
+gem 'activerecord-gitlab', path: '../activerecord-gitlab'
+gem 'gitlab-utils', path: '../gitlab-utils'
+gem 'gitlab-safe_request_store', path: '../gitlab-safe_request_store'
diff --git a/gems/gitlab-database-load_balancing/Gemfile.lock b/gems/gitlab-database-load_balancing/Gemfile.lock
new file mode 100644
index 000000000000..b2d66b9a3861
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/Gemfile.lock
@@ -0,0 +1,303 @@
+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)
+      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)
+      nokogiri (~> 1.15.2)
+      rake (~> 13.0)
+
+PATH
+  remote: .
+  specs:
+    gitlab-database-load_balancing (0.1.0)
+      gitlab-net-dns (~> 0.9.2)
+      pg (~> 1.5.4)
+      rails (~> 7.0.8)
+
+GEM
+  remote: https://rubygems.org/
+  specs:
+    actioncable (7.0.8)
+      actionpack (= 7.0.8)
+      activesupport (= 7.0.8)
+      nio4r (~> 2.0)
+      websocket-driver (>= 0.6.1)
+    actionmailbox (7.0.8)
+      actionpack (= 7.0.8)
+      activejob (= 7.0.8)
+      activerecord (= 7.0.8)
+      activestorage (= 7.0.8)
+      activesupport (= 7.0.8)
+      mail (>= 2.7.1)
+      net-imap
+      net-pop
+      net-smtp
+    actionmailer (7.0.8)
+      actionpack (= 7.0.8)
+      actionview (= 7.0.8)
+      activejob (= 7.0.8)
+      activesupport (= 7.0.8)
+      mail (~> 2.5, >= 2.5.4)
+      net-imap
+      net-pop
+      net-smtp
+      rails-dom-testing (~> 2.0)
+    actionpack (7.0.8)
+      actionview (= 7.0.8)
+      activesupport (= 7.0.8)
+      rack (~> 2.0, >= 2.2.4)
+      rack-test (>= 0.6.3)
+      rails-dom-testing (~> 2.0)
+      rails-html-sanitizer (~> 1.0, >= 1.2.0)
+    actiontext (7.0.8)
+      actionpack (= 7.0.8)
+      activerecord (= 7.0.8)
+      activestorage (= 7.0.8)
+      activesupport (= 7.0.8)
+      globalid (>= 0.6.0)
+      nokogiri (>= 1.8.5)
+    actionview (7.0.8)
+      activesupport (= 7.0.8)
+      builder (~> 3.1)
+      erubi (~> 1.4)
+      rails-dom-testing (~> 2.0)
+      rails-html-sanitizer (~> 1.1, >= 1.2.0)
+    activejob (7.0.8)
+      activesupport (= 7.0.8)
+      globalid (>= 0.3.6)
+    activemodel (7.0.8)
+      activesupport (= 7.0.8)
+    activerecord (7.0.8)
+      activemodel (= 7.0.8)
+      activesupport (= 7.0.8)
+    activestorage (7.0.8)
+      actionpack (= 7.0.8)
+      activejob (= 7.0.8)
+      activerecord (= 7.0.8)
+      activesupport (= 7.0.8)
+      marcel (~> 1.0)
+      mini_mime (>= 1.1.0)
+    activesupport (7.0.8)
+      concurrent-ruby (~> 1.0, >= 1.0.2)
+      i18n (>= 1.6, < 2)
+      minitest (>= 5.1)
+      tzinfo (~> 2.0)
+    addressable (2.8.5)
+      public_suffix (>= 2.0.2, < 6.0)
+    ast (2.4.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)
+    crass (1.0.6)
+    date (3.3.3)
+    debug_inspector (1.1.0)
+    diff-lcs (1.5.0)
+    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)
+    globalid (1.2.1)
+      activesupport (>= 6.1)
+    i18n (1.12.0)
+      concurrent-ruby (~> 1.0)
+    json (2.6.3)
+    loofah (2.21.4)
+      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.2)
+    method_source (1.0.0)
+    mini_mime (1.1.5)
+    mini_portile2 (2.8.5)
+    minitest (5.17.0)
+    net-imap (0.4.4)
+      date
+      net-protocol
+    net-pop (0.1.2)
+      net-protocol
+    net-protocol (0.2.1)
+      timeout
+    net-smtp (0.4.0)
+      net-protocol
+    nio4r (2.5.9)
+    nokogiri (1.15.4)
+      mini_portile2 (~> 2.8.2)
+      racc (~> 1.4)
+    parallel (1.22.1)
+    parser (3.2.2.3)
+      ast (~> 2.4.1)
+      racc
+    pg (1.5.4)
+    proc_to_ast (0.1.0)
+      coderay
+      parser
+      unparser
+    pry (0.14.2)
+      coderay (~> 1.1)
+      method_source (~> 1.0)
+    public_suffix (5.0.3)
+    racc (1.6.2)
+    rack (2.2.8)
+    rack-test (2.1.0)
+      rack (>= 1.3)
+    rails (7.0.8)
+      actioncable (= 7.0.8)
+      actionmailbox (= 7.0.8)
+      actionmailer (= 7.0.8)
+      actionpack (= 7.0.8)
+      actiontext (= 7.0.8)
+      actionview (= 7.0.8)
+      activejob (= 7.0.8)
+      activemodel (= 7.0.8)
+      activerecord (= 7.0.8)
+      activestorage (= 7.0.8)
+      activesupport (= 7.0.8)
+      bundler (>= 1.15.0)
+      railties (= 7.0.8)
+    rails-dom-testing (2.2.0)
+      activesupport (>= 5.0.0)
+      minitest
+      nokogiri (>= 1.6)
+    rails-html-sanitizer (1.6.0)
+      loofah (~> 2.21)
+      nokogiri (~> 1.14)
+    railties (7.0.8)
+      actionpack (= 7.0.8)
+      activesupport (= 7.0.8)
+      method_source
+      rake (>= 12.2)
+      thor (~> 1.0)
+      zeitwerk (~> 2.5)
+    rainbow (3.1.1)
+    rake (13.1.0)
+    regexp_parser (2.7.0)
+    request_store (1.5.1)
+      rack (>= 1.4)
+    rexml (3.2.5)
+    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)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.12.0)
+    rspec-mocks (3.12.3)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.12.0)
+    rspec-parameterized (1.0.0)
+      rspec-parameterized-core (< 2)
+      rspec-parameterized-table_syntax (< 2)
+    rspec-parameterized-core (1.0.0)
+      parser
+      proc_to_ast
+      rspec (>= 2.13, < 4)
+      unparser
+    rspec-parameterized-table_syntax (1.0.1)
+      binding_of_caller
+      rspec-parameterized-core (< 2)
+    rspec-rails (6.0.3)
+      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.12.0)
+    rubocop (1.50.2)
+      json (~> 2.3)
+      parallel (~> 1.10)
+      parser (>= 3.2.0.0)
+      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)
+      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)
+      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)
+      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)
+    ruby-progressbar (1.11.0)
+    thor (1.3.0)
+    timeout (0.4.0)
+    tzinfo (2.0.6)
+      concurrent-ruby (~> 1.0)
+    unicode-display_width (2.4.2)
+    unparser (0.6.8)
+      diff-lcs (~> 1.3)
+      parser (>= 3.2.0)
+    websocket-driver (0.7.6)
+      websocket-extensions (>= 0.1.0)
+    websocket-extensions (0.1.5)
+    zeitwerk (2.6.12)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  activerecord-gitlab!
+  gitlab-database-load_balancing!
+  gitlab-rspec!
+  gitlab-safe_request_store!
+  gitlab-styles (~> 10.1.0)
+  gitlab-utils!
+  pg (~> 1.5.4)
+  pry
+  rspec (~> 3.0)
+  rspec-parameterized (~> 1.0)
+  rspec-rails (~> 6.0.1)
+  rubocop (~> 1.50)
+  rubocop-rspec (~> 2.22)
+
+BUNDLED WITH
+   2.4.16
diff --git a/gems/gitlab-database-load_balancing/README.md b/gems/gitlab-database-load_balancing/README.md
new file mode 100644
index 000000000000..e1a963dba508
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/README.md
@@ -0,0 +1,3 @@
+# GitLab Database Load Balancing
+
+This gem is a stub for a move of all `Gitlab::Database::LoadBalancing` code.
diff --git a/gems/gitlab-database-load_balancing/Rakefile b/gems/gitlab-database-load_balancing/Rakefile
new file mode 100644
index 000000000000..cca717544930
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/Rakefile
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require "bundler/gem_tasks"
+require "rspec/core/rake_task"
+
+RSpec::Core::RakeTask.new(:spec)
+
+require "rubocop/rake_task"
+
+RuboCop::RakeTask.new
+
+task default: %i[spec rubocop]
diff --git a/gems/gitlab-database-load_balancing/gitlab-database-load_balancing.gemspec b/gems/gitlab-database-load_balancing/gitlab-database-load_balancing.gemspec
new file mode 100644
index 000000000000..3abf196ed20c
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/gitlab-database-load_balancing.gemspec
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+Gem::Specification.new do |spec|
+  spec.name = "gitlab-database-load_balancing"
+  spec.version = "0.1.0"
+  spec.authors = ["group::database"]
+  spec.email = ["engineering@gitlab.com"]
+
+  spec.summary = "GitLab specific support for read-only replicas"
+  spec.description = "Provides a code on top of existing databases to support read-only replicas."
+  spec.homepage = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/gems/gitlab-safe_request_store"
+  spec.license = 'MIT'
+  spec.required_ruby_version = ">= 3.0"
+  spec.metadata["rubygems_mfa_required"] = "true"
+
+  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.0.8'
+
+  spec.add_development_dependency "gitlab-styles", "~> 10.1.0"
+  spec.add_development_dependency "pg", '~> 1.5.4'
+  spec.add_development_dependency "pry"
+  spec.add_development_dependency "rspec", "~> 3.0"
+  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"
+end
diff --git a/gems/gitlab-database-load_balancing/spec/spec_helper.rb b/gems/gitlab-database-load_balancing/spec/spec_helper.rb
new file mode 100644
index 000000000000..71faf49cce0c
--- /dev/null
+++ b/gems/gitlab-database-load_balancing/spec/spec_helper.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'rails'
+require 'rspec/mocks'
+require 'rspec-parameterized'
+
+require 'gitlab/rspec/all'
+require 'gitlab/utils/all'
+require 'gitlab/safe_request_store'
+
+RSpec.configure do |config|
+  include StubRails
+  include NextInstanceOf
+
+  # Enable flags like --only-failures and --next-failure
+  config.example_status_persistence_file_path = ".rspec_status"
+
+  # Disable RSpec exposing methods globally on `Module` and `main`
+  config.disable_monkey_patching!
+
+  config.expect_with :rspec do |c|
+    c.syntax = :expect
+  end
+
+  config.around(:example, :request_store) do |example|
+    ::Gitlab::SafeRequestStore.ensure_request_store { example.run }
+  end
+end
diff --git a/gems/gitlab-http/Gemfile.lock b/gems/gitlab-http/Gemfile.lock
index 1f4910d1d576..5fb1963d8f31 100644
--- a/gems/gitlab-http/Gemfile.lock
+++ b/gems/gitlab-http/Gemfile.lock
@@ -2,6 +2,7 @@ PATH
   remote: ../gitlab-rspec
   specs:
     gitlab-rspec (0.1.0)
+      activerecord (>= 6.1, < 8)
       activesupport (>= 6.1, < 8)
       rspec (~> 3.0)
 
@@ -42,6 +43,11 @@ GEM
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.1, >= 1.2.0)
+    activemodel (7.0.7)
+      activesupport (= 7.0.7)
+    activerecord (7.0.7)
+      activemodel (= 7.0.7)
+      activesupport (= 7.0.7)
     activesupport (7.0.7)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 1.6, < 2)
@@ -76,11 +82,9 @@ GEM
       nokogiri (>= 1.12.0)
     method_source (1.0.0)
     mini_mime (1.1.2)
-    mini_portile2 (2.8.4)
     minitest (5.18.1)
     multi_xml (0.6.0)
     nokogiri (1.15.4)
-      mini_portile2 (~> 2.8.2)
       racc (~> 1.4)
     parallel (1.23.0)
     parser (3.2.2.3)
diff --git a/gems/gitlab-rspec/Gemfile.lock b/gems/gitlab-rspec/Gemfile.lock
index 7dff91cbd2d8..d375d858b012 100644
--- a/gems/gitlab-rspec/Gemfile.lock
+++ b/gems/gitlab-rspec/Gemfile.lock
@@ -2,6 +2,7 @@ PATH
   remote: .
   specs:
     gitlab-rspec (0.1.0)
+      activerecord (>= 6.1, < 8)
       activesupport (>= 6.1, < 8)
       rspec (~> 3.0)
 
@@ -21,6 +22,11 @@ GEM
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.1, >= 1.2.0)
+    activemodel (7.0.4.3)
+      activesupport (= 7.0.4.3)
+    activerecord (7.0.4.3)
+      activemodel (= 7.0.4.3)
+      activesupport (= 7.0.4.3)
     activesupport (7.0.4.3)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 1.6, < 2)
diff --git a/gems/gitlab-rspec/gitlab-rspec.gemspec b/gems/gitlab-rspec/gitlab-rspec.gemspec
index 47c1e420ecdb..bfd404df019f 100644
--- a/gems/gitlab-rspec/gitlab-rspec.gemspec
+++ b/gems/gitlab-rspec/gitlab-rspec.gemspec
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
   spec.files = Dir["lib/**/*.rb"]
   spec.require_paths = ["lib"]
 
+  spec.add_runtime_dependency "activerecord", ">= 6.1", "< 8"
   spec.add_runtime_dependency "activesupport", ">= 6.1", "< 8"
   spec.add_runtime_dependency "rspec", "~> 3.0"
 
diff --git a/gems/gitlab-rspec/lib/gitlab/rspec/all.rb b/gems/gitlab-rspec/lib/gitlab/rspec/all.rb
index 518161fa549a..02504a6a881e 100644
--- a/gems/gitlab-rspec/lib/gitlab/rspec/all.rb
+++ b/gems/gitlab-rspec/lib/gitlab/rspec/all.rb
@@ -4,6 +4,7 @@
 require_relative "stub_env"
 require_relative "next_instance_of"
 require_relative "next_found_instance_of"
+require_relative "stub_rails"
 
 require_relative "configurations/time_travel"
 
diff --git a/gems/gitlab-rspec/lib/gitlab/rspec/stub_rails.rb b/gems/gitlab-rspec/lib/gitlab/rspec/stub_rails.rb
new file mode 100644
index 000000000000..26eb8151a017
--- /dev/null
+++ b/gems/gitlab-rspec/lib/gitlab/rspec/stub_rails.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'active_support'
+require 'active_record'
+
+# rubocop:disable Database/MultipleDatabases -- simulate Rails environment
+# rubocop:disable Database/EstablishConnection -- simulate Rails environment
+
+module StubRails
+  extend ActiveSupport::Concern
+
+  included do
+    class RailsApp < Rails::Application # rubocop:disable Lint/ConstantDefinitionInBlock -- load only when included
+    end
+
+    logger = Logger.new($stdout, level: Logger::INFO, formatter: ->(_, _, _, msg) { msg })
+
+    # load timezones
+    begin
+      TZInfo::DataSource.get
+    rescue TZInfo::DataSourceNotFound => e
+      raise e.exception "tzinfo-data is not present. " \
+                        "Please add gem 'tzinfo-data' to your Gemfile and run bundle install"
+    end
+    Time.zone_default = Time.find_zone!(Rails.application.config.time_zone)
+
+    ActiveRecord::Base.configurations = Rails.application.config.database_configuration
+
+    # Create and connect to main database
+    begin
+      rails_establish_connection(logger)
+    rescue ActiveRecord::NoDatabaseError
+      rails_create_main_database(logger)
+      rails_establish_connection(logger)
+    end
+  end
+
+  def rails_establish_connection(_logger)
+    ActiveRecord::Base.establish_connection
+    ActiveRecord::Base.connection.execute("SELECT VERSION()")
+  end
+
+  def rails_create_main_database(logger)
+    db_config = ActiveRecord::Base.configurations.find_db_config(Rails.env)
+    logger.info("Creating database #{db_config.database}...")
+
+    ActiveRecord::Base.establish_connection(db_config.configuration_hash.merge(
+      database: "postgres",
+      schema_search_path: "public"
+    ))
+    ActiveRecord::Base.connection.create_database(
+      db_config.database, { encoding: 'utf8' }.merge(db_config.configuration_hash))
+  end
+end
+
+# rubocop:enable Database/MultipleDatabases
+# rubocop:enable Database/EstablishConnection
diff --git a/gems/gitlab-utils/Gemfile.lock b/gems/gitlab-utils/Gemfile.lock
index e6cfe03e60ed..ef7c2d57c7a1 100644
--- a/gems/gitlab-utils/Gemfile.lock
+++ b/gems/gitlab-utils/Gemfile.lock
@@ -2,6 +2,7 @@ PATH
   remote: ../gitlab-rspec
   specs:
     gitlab-rspec (0.1.0)
+      activerecord (>= 6.1, < 8)
       activesupport (>= 6.1, < 8)
       rspec (~> 3.0)
 
@@ -31,6 +32,11 @@ GEM
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.1, >= 1.2.0)
+    activemodel (7.0.5)
+      activesupport (= 7.0.5)
+    activerecord (7.0.5)
+      activemodel (= 7.0.5)
+      activesupport (= 7.0.5)
     activesupport (7.0.5)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 1.6, < 2)
diff --git a/gems/rspec_flaky/Gemfile.lock b/gems/rspec_flaky/Gemfile.lock
index 6be845e81fb0..491d589d80c9 100644
--- a/gems/rspec_flaky/Gemfile.lock
+++ b/gems/rspec_flaky/Gemfile.lock
@@ -2,6 +2,7 @@ PATH
   remote: ../gitlab-rspec
   specs:
     gitlab-rspec (0.1.0)
+      activerecord (>= 6.1, < 8)
       activesupport (>= 6.1, < 8)
       rspec (~> 3.0)
 
@@ -15,18 +16,34 @@ PATH
 GEM
   remote: https://rubygems.org/
   specs:
-    activesupport (7.0.6)
+    activemodel (7.1.1)
+      activesupport (= 7.1.1)
+    activerecord (7.1.1)
+      activemodel (= 7.1.1)
+      activesupport (= 7.1.1)
+      timeout (>= 0.4.0)
+    activesupport (7.1.1)
+      base64
+      bigdecimal
       concurrent-ruby (~> 1.0, >= 1.0.2)
+      connection_pool (>= 2.2.5)
+      drb
       i18n (>= 1.6, < 2)
       minitest (>= 5.1)
+      mutex_m
       tzinfo (~> 2.0)
     ast (2.4.2)
+    base64 (0.2.0)
+    bigdecimal (3.1.4)
     binding_of_caller (1.0.0)
       debug_inspector (>= 0.0.1)
     coderay (1.1.3)
     concurrent-ruby (1.2.2)
+    connection_pool (2.4.1)
     debug_inspector (1.1.0)
     diff-lcs (1.5.0)
+    drb (2.2.0)
+      ruby2_keywords
     gitlab-styles (10.1.0)
       rubocop (~> 1.50.2)
       rubocop-graphql (~> 0.18)
@@ -37,6 +54,7 @@ GEM
       concurrent-ruby (~> 1.0)
     json (2.6.3)
     minitest (5.18.1)
+    mutex_m (0.2.0)
     parallel (1.23.0)
     parser (3.2.2.3)
       ast (~> 2.4.1)
@@ -104,6 +122,8 @@ GEM
       rubocop-capybara (~> 2.17)
       rubocop-factory_bot (~> 2.22)
     ruby-progressbar (1.13.0)
+    ruby2_keywords (0.0.5)
+    timeout (0.4.1)
     tzinfo (2.0.6)
       concurrent-ruby (~> 1.0)
     unicode-display_width (2.4.2)
-- 
GitLab