diff options
author | Sean Griffin <sean@thoughtbot.com> | 2015-03-29 13:51:56 -0600 |
---|---|---|
committer | Sean Griffin <sean@thoughtbot.com> | 2015-03-29 13:54:39 -0600 |
commit | 445c12f7dfa345b86e05dc610d665f9afde14c26 (patch) | |
tree | 132709696b9ab8666a8d10cb1eb6e4c73fb6ffc2 /activerecord/lib/active_record | |
parent | e8a334a9ee87362e3a99a8a24eb7d28a6ad7ae04 (diff) | |
download | rails-445c12f7dfa345b86e05dc610d665f9afde14c26.tar.gz rails-445c12f7dfa345b86e05dc610d665f9afde14c26.tar.bz2 rails-445c12f7dfa345b86e05dc610d665f9afde14c26.zip |
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
Diffstat (limited to 'activerecord/lib/active_record')
3 files changed, 34 insertions, 10 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb index 9d421d6975..191c828e60 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb @@ -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'] diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e616cee5eb..00fc69c878 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -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 diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb index 82d9327fc0..3b01e3f8ca 100644 --- a/activerecord/lib/active_record/type/hash_lookup_type_map.rb +++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb @@ -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) |