DEV: Adds a freedom patch to cache connection type map (#25923)
Why this change? This patch has been added to address the problems identified in https://github.com/rails/rails/issues/35311. For every, new connection created using the PostgreSQL adapter, 3 queries are executed to fetch type map information from the `pg_type` system catalog, adding about 1ms overhead to every connection creation. On multisite clusters where connections are reaped more aggressively, the 3 queries executed accounts for a significant portion of CPU usage on the PostgreSQL cluster. This patch works around the problem by caching the type map in a class level attribute to reuse across connections.
This commit is contained in:
parent
9a07a9872f
commit
408d2f8e69
|
@ -0,0 +1,44 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# This patch has been added to address the problems identified in https://github.com/rails/rails/issues/35311. For every,
|
||||||
|
# new connection created using the PostgreSQL adapter, 3 queries are executed to fetch the type map adding about 1ms overhead
|
||||||
|
# to every connection creation. In multisite clusters where connections are reaped more aggressively, the 3 queries executed
|
||||||
|
# accounts for a significant portion of CPU usage on the PostgreSQL cluster. This patch works around the problem by
|
||||||
|
# caching the type map in a class level attribute to reuse across connections.
|
||||||
|
#
|
||||||
|
# The latest attempt to fix the problem in Rails is in https://github.com/rails/rails/pull/46409 but it has gone stale.
|
||||||
|
module FreedomPatches
|
||||||
|
module PostgreSQLAdapter
|
||||||
|
# Definition as of writing: https://github.com/rails/rails/blob/5bf5344521a6f305ca17e0004273322a0a26f50a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L614
|
||||||
|
def initialize_type_map(m = type_map)
|
||||||
|
if !self.class.type_map.nil?
|
||||||
|
@type_map = self.class.type_map
|
||||||
|
else
|
||||||
|
super.tap { self.class.type_map = @type_map }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module PostgreSQLAdapterClassMethods
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
@type_map_mutex = Mutex.new
|
||||||
|
@type_map = nil
|
||||||
|
|
||||||
|
def self.type_map
|
||||||
|
@type_map_mutex.synchronize { @type_map }
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.type_map=(type_map)
|
||||||
|
@type_map_mutex.synchronize { @type_map = type_map }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(FreedomPatches::PostgreSQLAdapter)
|
||||||
|
|
||||||
|
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.include(
|
||||||
|
FreedomPatches::PostgreSQLAdapterClassMethods,
|
||||||
|
)
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe "Caching PostgreSQL connection type map" do
|
||||||
|
it "caches the type map and avoid querying the database for type map information on every new connection" do
|
||||||
|
expect(ActiveRecord::Base.connection.class.type_map).to be_present
|
||||||
|
|
||||||
|
pg_type_queries = []
|
||||||
|
|
||||||
|
subscriber =
|
||||||
|
ActiveSupport::Notifications.subscribe("sql.active_record") do |*, payload|
|
||||||
|
if payload[:name] == "SCHEMA"
|
||||||
|
sql = payload[:sql]
|
||||||
|
pg_type_queries.push(sql) if sql.include?("pg_type")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect do ActiveRecord::Base.connection.reconnect! end.not_to change { pg_type_queries.length }
|
||||||
|
ensure
|
||||||
|
ActiveSupport::Notifications.unsubscribe(subscriber)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue