1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Reduce memory usage when loading types in PG

We were never clearing the `PG::Result` object used to query the types
when the connection is first established. This would lead to a
potentially large amount of memory being retained for the life of the
connection.

Investigating this issue also revealed several low hanging fruit on the
performance of these methods, and the number of allocations has been
reduced by ~90%.

Fixes #19578
This commit is contained in:
Sean Griffin 2015-03-29 13:51:56 -06:00
parent e8a334a9ee
commit 445c12f7df
4 changed files with 40 additions and 10 deletions

View file

@ -1,3 +1,9 @@
* Reduce memory usage from loading types on pg.
Fixes #19578.
*Sean Griffin*
* Add `config.active_record.warn_on_records_fetched_greater_than` option
When set to an integer, a warning will be logged whenever a result set

View file

@ -15,10 +15,10 @@ module ActiveRecord
def run(records)
nodes = records.reject { |row| @store.key? row['oid'].to_i }
mapped, nodes = nodes.partition { |row| @store.key? row['typname'] }
ranges, nodes = nodes.partition { |row| row['typtype'] == 'r' }
enums, nodes = nodes.partition { |row| row['typtype'] == 'e' }
domains, nodes = nodes.partition { |row| row['typtype'] == 'd' }
arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
ranges, nodes = nodes.partition { |row| row['typtype'] == 'r'.freeze }
enums, nodes = nodes.partition { |row| row['typtype'] == 'e'.freeze }
domains, nodes = nodes.partition { |row| row['typtype'] == 'd'.freeze }
arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in'.freeze }
composites, nodes = nodes.partition { |row| row['typelem'].to_i != 0 }
mapped.each { |row| register_mapped_type(row) }
@ -29,6 +29,18 @@ module ActiveRecord
composites.each { |row| register_composite_type(row) }
end
def query_conditions_for_initial_load(type_map)
known_type_names = type_map.keys.map { |n| "'#{n}'" }
known_type_types = %w('r' 'e' 'd')
<<-SQL % [known_type_names.join(", "), known_type_types.join(", ")]
WHERE
t.typname IN (%s)
OR t.typtype IN (%s)
OR t.typinput::varchar = 'array_in'
OR t.typelem != 0
SQL
end
private
def register_mapped_type(row)
alias_type row['oid'], row['typname']

View file

@ -594,6 +594,8 @@ module ActiveRecord
end
def load_additional_types(type_map, oids = nil) # :nodoc:
initializer = OID::TypeMapInitializer.new(type_map)
if supports_ranges?
query = <<-SQL
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
@ -609,11 +611,13 @@ module ActiveRecord
if oids
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
else
query += initializer.query_conditions_for_initial_load(type_map)
end
initializer = OID::TypeMapInitializer.new(type_map)
records = execute(query, 'SCHEMA')
initializer.run(records)
execute_and_clear(query, 'SCHEMA', []) do |records|
initializer.run(records)
end
end
FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
@ -823,9 +827,11 @@ module ActiveRecord
'float8' => PG::TextDecoder::Float,
'bool' => PG::TextDecoder::Boolean,
}
query = <<-SQL
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
query = <<-SQL % known_coder_types.join(", ")
SELECT t.oid, t.typname
FROM pg_type as t
WHERE t.typname IN (%s)
SQL
coders = execute_and_clear(query, "SCHEMA", []) do |result|
result

View file

@ -1,12 +1,18 @@
module ActiveRecord
module Type
class HashLookupTypeMap < TypeMap # :nodoc:
delegate :key?, to: :@mapping
def alias_type(type, alias_type)
register_type(type) { |_, *args| lookup(alias_type, *args) }
end
def key?(key)
@mapping.key?(key)
end
def keys
@mapping.keys
end
private
def perform_fetch(type, *args, &block)