-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FIX: Falling back to primary PG server not reliable on Rails 7.1
This commit drops the reliance on `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#active?` in favor or using `ActiveRecord::ConnectionAdatpers::PostgreSQLAdatper#execute` to check if the app can connect to the primary PG server. This is because `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#active?` was changed in Rails 7.1 to return `false` if the connection has not be used to do something meaningful. Ref: rails/rails@8551e64. Due to this change, our fallback to primary checker will keep thinking that the primary PG server is down. Note that before Rails 7.1, `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#active?` will always execute a fake query to check if the query can be executed. Instead of relying on ActiveRecord's internal API, we will instead rely on `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#execute` to execute a blank query as our means of verifying if the PG server is up and ready to receive connections. This commit also updates the ActiveRecord tests to be more reliable by reducing the Unicorn worker processes to 1 so that we don't have to rely on flooding the app with requests to get all the Unicorn processes to failover/fallback. Co-authored-by: Loïc Guitaut <loic@discourse.org>
- Loading branch information
Showing
18 changed files
with
300 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,4 +16,4 @@ | |
*.rdb | ||
|
||
*.gem | ||
Gemfile.lock | ||
Gemfile.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
--color | ||
--require spec_helper | ||
--format documentation | ||
--order random |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
# frozen_string_literal: true | ||
|
||
module RailsFailover | ||
VERSION = "2.1.0" | ||
VERSION = "2.1.1" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# frozen_string_literal: true | ||
|
||
module GenericHelper | ||
def wait_for(timeout:, &blk) | ||
till = Time.now + (timeout.to_f / 1000) | ||
sleep 0.001 while Time.now < till && !blk.call | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# frozen_string_literal: true | ||
|
||
module PostgresHelper | ||
def start_pg_primary | ||
return if pg_primary_is_up? | ||
system("make start_pg_primary") | ||
wait_for_pg_primary_to_be_up | ||
end | ||
|
||
def stop_pg_primary | ||
return if pg_primary_is_down? | ||
system("make stop_pg_primary") | ||
wait_for_pg_primary_to_be_down | ||
end | ||
|
||
def start_pg_replica | ||
system("make start_pg_replica") | ||
wait_for_pg_replica_to_be_up | ||
end | ||
|
||
def stop_pg_replica | ||
system("make stop_pg_replica") | ||
wait_for_pg_replica_to_be_down | ||
end | ||
|
||
private | ||
|
||
def pg_primary_is_up? | ||
File.exist?(pg_primary_pid_path) | ||
end | ||
|
||
def pg_primary_is_down? | ||
!File.exist?(pg_primary_pid_path) | ||
end | ||
|
||
def wait_for_pg_primary_to_be_up | ||
wait_for_pg_to_be_up(role: :primary) | ||
end | ||
|
||
def wait_for_pg_primary_to_be_down | ||
wait_for_pg_to_be_down(role: :primary) | ||
end | ||
|
||
def wait_for_pg_replica_to_be_up | ||
wait_for_pg_to_be_up(role: :replica) | ||
end | ||
|
||
def wait_for_pg_replica_to_be_down | ||
wait_for_pg_to_be_down(role: :replica) | ||
end | ||
|
||
def wait_for_pg_to_be_up(role:) | ||
wait_for(timeout: 5) { File.exist?(self.send("pg_#{role}_pid_path")) } | ||
end | ||
|
||
def wait_for_pg_to_be_down(role:) | ||
wait_for(timeout: 5) { !File.exist?(self.send("pg_#{role}_pid_path")) } | ||
end | ||
|
||
def pg_replica_pid_path | ||
"#{gem_root}/tmp/replica/data/postmaster.pid" | ||
end | ||
|
||
def pg_primary_pid_path | ||
"#{gem_root}/tmp/primary/data/postmaster.pid" | ||
end | ||
|
||
def gem_root | ||
File.expand_path("../..", __dir__) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# frozen_string_literal: true | ||
|
||
module RailsServerHelper | ||
def setup_rails_server | ||
execute_command( | ||
"cd spec/support/dummy_app && BUNDLE_GEMFILE=Gemfile $(which bundle) install --quiet", | ||
) | ||
|
||
execute_command( | ||
"cd spec/support/dummy_app && BUNDLE_GEMFILE=Gemfile RAILS_ENV=production bin/bundle exec rails db:create db:migrate db:seed", | ||
) | ||
end | ||
|
||
def start_rails_server | ||
if ( | ||
(unicorn_master_pid = get_unicorn_master_pid) != 0 && | ||
(get_unicorn_worker_pids(unicorn_master_pid).size == 1.to_i) | ||
) | ||
return | ||
end | ||
|
||
system( | ||
"cd spec/support/dummy_app && BUNDLE_GEMFILE=Gemfile SECRET_KEY_BASE=somekey bin/bundle exec unicorn -c config/unicorn.conf.rb -D -E production", | ||
) | ||
|
||
count = 0 | ||
timeout = 10 | ||
|
||
while (unicorn_master_pid = get_unicorn_master_pid) == 0 | ||
raise "Timeout while waiting for unicorn master to be up" if count == timeout | ||
count += 1 | ||
sleep 1 | ||
end | ||
|
||
count = 0 | ||
timeout = 10 | ||
|
||
while get_unicorn_worker_pids(unicorn_master_pid).size != 1.to_i | ||
raise "Timeout while waiting for unicorn worker to be up" if count == timeout | ||
count += 1 | ||
sleep 1 | ||
end | ||
|
||
true | ||
end | ||
|
||
def stop_rails_server | ||
system("kill -15 #{get_unicorn_master_pid}") | ||
|
||
count = 0 | ||
timeout = 10 | ||
|
||
while get_unicorn_master_pid != 0 | ||
raise "Timeout while waiting for unicorn master to be down" if count == timeout | ||
count += 1 | ||
sleep 1 | ||
end | ||
|
||
true | ||
end | ||
|
||
def teardown_rails_server | ||
execute_command( | ||
"cd spec/support/dummy_app && BUNDLE_GEMFILE=Gemfile DISABLE_DATABASE_ENVIRONMENT_CHECK=1 RAILS_ENV=production bin/bundle exec rails db:drop", | ||
) | ||
end | ||
|
||
private | ||
|
||
def execute_command(command) | ||
output = `#{command}` | ||
raise "Command failed: #{command}\nOutput: #{output}" unless $?.success? | ||
|
||
puts output if ENV["VERBOSE"] | ||
|
||
output | ||
end | ||
|
||
def get_unicorn_master_pid | ||
execute_command( | ||
"ps aux | grep \"unicorn master\" | grep -v \"grep\" | awk '{print $2}'", | ||
).strip.to_i | ||
end | ||
|
||
def get_unicorn_worker_pids(unicorn_master_pid) | ||
execute_command("pgrep -P #{unicorn_master_pid}").split("\n").map(&:to_i) | ||
end | ||
end |
Oops, something went wrong.