active_record_postgresql_xverify

It is a library to solve Amazon RDS failover problems.

cf. brianmario/mysql2#948

Note

This library does not retry queries. Just reconnect.

Caution

The timing of verify has changed, so it probably won't work properly with Rails 7.1. cf. rails/rails#44576

Gem Version CI

How it works

Installation

Add this line to your application's Gemfile:

gem 'active_record_postgresql_xverify'

And then execute:

$ bundle

Or install it yourself as:

$ gem install active_record_postgresql_xverify

Usage

#!/usr/bin/env ruby
require 'active_record'
require 'active_record_postgresql_xverify'
require 'logger'

ActiveRecord::Base.establish_connection(
  adapter:  'postgresql',
  host:     '127.0.0.1',
  port:      5432,
  username: 'root',
  database: 'bookshelf',
)

ActiveRecord::Base.logger = Logger.new($stdout)
ActiveRecord::Base.logger.formatter = proc {|_, _, _, message| "#{message}\n" }

ActiveRecordPostgresqlXverify.verify = ->(conn) do
  ping = begin
           conn.query ''
           true
         rescue PG::Error
           false
         end

  ping && false # force reconnect
end
# Default: ->(conn) do
#            begin
#              conn.query ''
#              true
#            rescue PG::Error
#              false
#            end

ActiveRecordPostgresqlXverify.handle_if = ->(config) do
  config[:host] == '127.0.0.1'
end
# Default: ->(_) { true }

ActiveRecordPostgresqlXverify.only_on_error = false
# Default: true

# postgres=> CREATE DATABASE bookshelf;
# bookshelf=> CREATE TABLE books (id INT PRIMARY KEY);
class Book < ActiveRecord::Base; end

def pg_backend_pid(model)
  conn = model.connection.instance_variable_get(:@connection) || Book.connection.instance_variable_get(:@raw_connection)
  conn.backend_pid
end

Book.count
prev_process_id = pg_backend_pid(Book)

ActiveRecord::Base.connection_handler.connection_pool_list.each(&:release_connection)

Book.count
curr_process_id = pg_backend_pid(Book)

p curr_process_id == prev_process_id #=> false

Rails configuration

In config/environments/production.rb:

ActiveRecordPostgresqlXverify.verify = ->(conn) do
  ping = begin
           conn.query ''
           true
         rescue PG::Error
           false
         end

  ping && conn.query('show transaction_read_only').getvalue(0, 0) == 'off'
end
# Same as below:
#   ActiveRecordPostgresqlXverify.verify = ActiveRecordPostgresqlXverify::Verifiers::AURORA_MASTER

Test

bundle install
bundle exec appraisal install
docker-compose up -d
bundle exec appraisal ar71 rake

Related links