activerecord 7.0.8.6 → 7.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1340 -1568
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +12 -9
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +193 -97
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +71 -94
- data/lib/active_record/core.rb +128 -138
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +89 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +119 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +213 -109
- data/lib/active_record/model_schema.rb +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +169 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +85 -15
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +41 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +52 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -5,6 +5,8 @@ require "concurrent/map"
|
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
module ConnectionAdapters
|
8
|
+
# = Active Record Connection Handler
|
9
|
+
#
|
8
10
|
# ConnectionHandler is a collection of ConnectionPool objects. It is used
|
9
11
|
# for keeping separate connection pools that connect to different databases.
|
10
12
|
#
|
@@ -56,7 +58,7 @@ module ActiveRecord
|
|
56
58
|
FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
|
57
59
|
private_constant :FINALIZER
|
58
60
|
|
59
|
-
class
|
61
|
+
class StringConnectionName # :nodoc:
|
60
62
|
attr_reader :name
|
61
63
|
|
62
64
|
def initialize(name)
|
@@ -73,8 +75,8 @@ module ActiveRecord
|
|
73
75
|
end
|
74
76
|
|
75
77
|
def initialize
|
76
|
-
# These caches are keyed by pool_config.
|
77
|
-
@
|
78
|
+
# These caches are keyed by pool_config.connection_name (PoolConfig#connection_name).
|
79
|
+
@connection_name_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
|
78
80
|
|
79
81
|
# Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
|
80
82
|
ObjectSpace.define_finalizer self, FINALIZER
|
@@ -88,121 +90,154 @@ module ActiveRecord
|
|
88
90
|
ActiveSupport::IsolatedExecutionState[:active_record_prevent_writes] = prevent_writes
|
89
91
|
end
|
90
92
|
|
91
|
-
# Prevent writing to the database regardless of role.
|
92
|
-
#
|
93
|
-
# In some cases you may want to prevent writes to the database
|
94
|
-
# even if you are on a database that can write. +while_preventing_writes+
|
95
|
-
# will prevent writes to the database for the duration of the block.
|
96
|
-
#
|
97
|
-
# This method does not provide the same protection as a readonly
|
98
|
-
# user and is meant to be a safeguard against accidental writes.
|
99
|
-
#
|
100
|
-
# See +READ_QUERY+ for the queries that are blocked by this
|
101
|
-
# method.
|
102
|
-
def while_preventing_writes(enabled = true)
|
103
|
-
unless ActiveRecord.legacy_connection_handling
|
104
|
-
raise NotImplementedError, "`while_preventing_writes` is only available on the connection_handler with legacy_connection_handling"
|
105
|
-
end
|
106
|
-
|
107
|
-
original, self.prevent_writes = self.prevent_writes, enabled
|
108
|
-
yield
|
109
|
-
ensure
|
110
|
-
self.prevent_writes = original
|
111
|
-
end
|
112
|
-
|
113
93
|
def connection_pool_names # :nodoc:
|
114
|
-
|
94
|
+
connection_name_to_pool_manager.keys
|
115
95
|
end
|
116
96
|
|
117
97
|
def all_connection_pools
|
118
|
-
|
98
|
+
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
99
|
+
The `all_connection_pools` method is deprecated in favor of `connection_pool_list`.
|
100
|
+
Call `connection_pool_list(:all)` to get the same behavior as `all_connection_pools`.
|
101
|
+
MSG
|
102
|
+
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
|
119
103
|
end
|
120
104
|
|
121
|
-
|
122
|
-
|
105
|
+
# Returns the pools for a connection handler and given role. If +:all+ is passed,
|
106
|
+
# all pools belonging to the connection handler will be returned.
|
107
|
+
def connection_pool_list(role = nil)
|
108
|
+
if role.nil?
|
109
|
+
deprecation_for_pool_handling(__method__)
|
110
|
+
role = ActiveRecord::Base.current_role
|
111
|
+
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
|
112
|
+
elsif role == :all
|
113
|
+
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
|
114
|
+
else
|
115
|
+
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
|
116
|
+
end
|
123
117
|
end
|
124
118
|
alias :connection_pools :connection_pool_list
|
125
119
|
|
126
|
-
def
|
127
|
-
|
120
|
+
def each_connection_pool(role = nil, &block) # :nodoc:
|
121
|
+
role = nil if role == :all
|
122
|
+
return enum_for(__method__, role) unless block_given?
|
123
|
+
|
124
|
+
connection_name_to_pool_manager.each_value do |manager|
|
125
|
+
manager.each_pool_config(role) do |pool_config|
|
126
|
+
yield pool_config.pool
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def establish_connection(config, owner_name: Base, role: Base.current_role, shard: Base.current_shard, clobber: false)
|
132
|
+
owner_name = determine_owner_name(owner_name, config)
|
128
133
|
|
129
134
|
pool_config = resolve_pool_config(config, owner_name, role, shard)
|
130
135
|
db_config = pool_config.db_config
|
131
136
|
|
132
|
-
|
133
|
-
# if the user calls `establish_connection :primary`.
|
134
|
-
if owner_to_pool_manager.key?(pool_config.connection_specification_name)
|
135
|
-
remove_connection_pool(pool_config.connection_specification_name, role: role, shard: shard)
|
136
|
-
end
|
137
|
+
pool_manager = set_pool_manager(pool_config.connection_name)
|
137
138
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
payload[:config] = db_config.configuration_hash
|
144
|
-
end
|
139
|
+
# If there is an existing pool with the same values as the pool_config
|
140
|
+
# don't remove the connection. Connections should only be removed if we are
|
141
|
+
# establishing a connection on a class that is already connected to a different
|
142
|
+
# configuration.
|
143
|
+
existing_pool_config = pool_manager.get_pool_config(role, shard)
|
145
144
|
|
146
|
-
if
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
145
|
+
if !clobber && existing_pool_config && existing_pool_config.db_config == db_config
|
146
|
+
# Update the pool_config's connection class if it differs. This is used
|
147
|
+
# for ensuring that ActiveRecord::Base and the primary_abstract_class use
|
148
|
+
# the same pool. Without this granular swapping will not work correctly.
|
149
|
+
if owner_name.primary_class? && (existing_pool_config.connection_class != owner_name)
|
150
|
+
existing_pool_config.connection_class = owner_name
|
151
|
+
end
|
153
152
|
|
154
|
-
|
155
|
-
|
153
|
+
existing_pool_config.pool
|
154
|
+
else
|
155
|
+
disconnect_pool_from_pool_manager(pool_manager, role, shard)
|
156
|
+
pool_manager.set_pool_config(role, shard, pool_config)
|
157
|
+
|
158
|
+
payload = {
|
159
|
+
connection_name: pool_config.connection_name,
|
160
|
+
role: role,
|
161
|
+
shard: shard,
|
162
|
+
config: db_config.configuration_hash
|
163
|
+
}
|
164
|
+
|
165
|
+
ActiveSupport::Notifications.instrumenter.instrument("!connection.active_record", payload) do
|
166
|
+
pool_config.pool
|
167
|
+
end
|
156
168
|
end
|
157
169
|
end
|
158
170
|
|
159
171
|
# Returns true if there are any active connections among the connection
|
160
172
|
# pools that the ConnectionHandler is managing.
|
161
|
-
def active_connections?(role =
|
162
|
-
|
173
|
+
def active_connections?(role = nil)
|
174
|
+
if role.nil?
|
175
|
+
deprecation_for_pool_handling(__method__)
|
176
|
+
role = ActiveRecord::Base.current_role
|
177
|
+
end
|
178
|
+
|
179
|
+
each_connection_pool(role).any?(&:active_connection?)
|
163
180
|
end
|
164
181
|
|
165
182
|
# Returns any connections in use by the current thread back to the pool,
|
166
183
|
# and also returns connections to the pool cached by threads that are no
|
167
184
|
# longer alive.
|
168
|
-
def clear_active_connections!(role =
|
169
|
-
|
185
|
+
def clear_active_connections!(role = nil)
|
186
|
+
if role.nil?
|
187
|
+
deprecation_for_pool_handling(__method__)
|
188
|
+
role = ActiveRecord::Base.current_role
|
189
|
+
end
|
190
|
+
|
191
|
+
each_connection_pool(role).each(&:release_connection)
|
170
192
|
end
|
171
193
|
|
172
194
|
# Clears the cache which maps classes.
|
173
195
|
#
|
174
196
|
# See ConnectionPool#clear_reloadable_connections! for details.
|
175
|
-
def clear_reloadable_connections!(role =
|
176
|
-
|
197
|
+
def clear_reloadable_connections!(role = nil)
|
198
|
+
if role.nil?
|
199
|
+
deprecation_for_pool_handling(__method__)
|
200
|
+
role = ActiveRecord::Base.current_role
|
201
|
+
end
|
202
|
+
|
203
|
+
each_connection_pool(role).each(&:clear_reloadable_connections!)
|
177
204
|
end
|
178
205
|
|
179
|
-
def clear_all_connections!(role =
|
180
|
-
|
206
|
+
def clear_all_connections!(role = nil)
|
207
|
+
if role.nil?
|
208
|
+
deprecation_for_pool_handling(__method__)
|
209
|
+
role = ActiveRecord::Base.current_role
|
210
|
+
end
|
211
|
+
|
212
|
+
each_connection_pool(role).each(&:disconnect!)
|
181
213
|
end
|
182
214
|
|
183
215
|
# Disconnects all currently idle connections.
|
184
216
|
#
|
185
217
|
# See ConnectionPool#flush! for details.
|
186
|
-
def flush_idle_connections!(role =
|
187
|
-
|
218
|
+
def flush_idle_connections!(role = nil)
|
219
|
+
if role.nil?
|
220
|
+
deprecation_for_pool_handling(__method__)
|
221
|
+
role = ActiveRecord::Base.current_role
|
222
|
+
end
|
223
|
+
|
224
|
+
each_connection_pool(role).each(&:flush!)
|
188
225
|
end
|
189
226
|
|
190
227
|
# Locate the connection of the nearest super class. This can be an
|
191
228
|
# active or defined connection: if it is the latter, it will be
|
192
229
|
# opened and set as the active connection for the class it was defined
|
193
230
|
# for (not necessarily the current class).
|
194
|
-
def retrieve_connection(
|
195
|
-
pool = retrieve_connection_pool(
|
231
|
+
def retrieve_connection(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) # :nodoc:
|
232
|
+
pool = retrieve_connection_pool(connection_name, role: role, shard: shard)
|
196
233
|
|
197
234
|
unless pool
|
198
235
|
if shard != ActiveRecord::Base.default_shard
|
199
|
-
message = "No connection pool for '#{
|
200
|
-
elsif ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
|
201
|
-
message = "No connection pool for '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
|
236
|
+
message = "No connection pool for '#{connection_name}' found for the '#{shard}' shard."
|
202
237
|
elsif role != ActiveRecord::Base.default_role
|
203
|
-
message = "No connection pool for '#{
|
238
|
+
message = "No connection pool for '#{connection_name}' found for the '#{role}' role."
|
204
239
|
else
|
205
|
-
message = "No connection pool for '#{
|
240
|
+
message = "No connection pool for '#{connection_name}' found."
|
206
241
|
end
|
207
242
|
|
208
243
|
raise ConnectionNotEstablished, message
|
@@ -213,36 +248,66 @@ module ActiveRecord
|
|
213
248
|
|
214
249
|
# Returns true if a connection that's accessible to this class has
|
215
250
|
# already been opened.
|
216
|
-
def connected?(
|
217
|
-
pool = retrieve_connection_pool(
|
251
|
+
def connected?(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
252
|
+
pool = retrieve_connection_pool(connection_name, role: role, shard: shard)
|
218
253
|
pool && pool.connected?
|
219
254
|
end
|
220
255
|
|
221
|
-
def remove_connection_pool(
|
222
|
-
if pool_manager = get_pool_manager(
|
223
|
-
|
224
|
-
|
225
|
-
if pool_config
|
226
|
-
pool_config.disconnect!
|
227
|
-
pool_config.db_config
|
228
|
-
end
|
256
|
+
def remove_connection_pool(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
257
|
+
if pool_manager = get_pool_manager(connection_name)
|
258
|
+
disconnect_pool_from_pool_manager(pool_manager, role, shard)
|
229
259
|
end
|
230
260
|
end
|
231
261
|
|
232
|
-
# Retrieving the connection pool happens a lot, so we cache it in @
|
262
|
+
# Retrieving the connection pool happens a lot, so we cache it in @connection_name_to_pool_manager.
|
233
263
|
# This makes retrieving the connection pool O(1) once the process is warm.
|
234
264
|
# When a connection is established or removed, we invalidate the cache.
|
235
|
-
def retrieve_connection_pool(
|
236
|
-
pool_config = get_pool_manager(
|
265
|
+
def retrieve_connection_pool(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
266
|
+
pool_config = get_pool_manager(connection_name)&.get_pool_config(role, shard)
|
237
267
|
pool_config&.pool
|
238
268
|
end
|
239
269
|
|
240
270
|
private
|
241
|
-
attr_reader :
|
271
|
+
attr_reader :connection_name_to_pool_manager
|
242
272
|
|
243
|
-
# Returns the pool manager for
|
244
|
-
def get_pool_manager(
|
245
|
-
|
273
|
+
# Returns the pool manager for a connection name / identifier.
|
274
|
+
def get_pool_manager(connection_name)
|
275
|
+
connection_name_to_pool_manager[connection_name]
|
276
|
+
end
|
277
|
+
|
278
|
+
# Get the existing pool manager or initialize and assign a new one.
|
279
|
+
def set_pool_manager(connection_name)
|
280
|
+
connection_name_to_pool_manager[connection_name] ||= PoolManager.new
|
281
|
+
end
|
282
|
+
|
283
|
+
def pool_managers
|
284
|
+
connection_name_to_pool_manager.values
|
285
|
+
end
|
286
|
+
|
287
|
+
def deprecation_for_pool_handling(method)
|
288
|
+
roles = []
|
289
|
+
pool_managers.each do |pool_manager|
|
290
|
+
roles << pool_manager.role_names
|
291
|
+
end
|
292
|
+
|
293
|
+
if roles.flatten.uniq.count > 1
|
294
|
+
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
295
|
+
`#{method}` currently only applies to connection pools in the current
|
296
|
+
role (`#{ActiveRecord::Base.current_role}`). In Rails 7.2, this method
|
297
|
+
will apply to all known pools, regardless of role. To affect only those
|
298
|
+
connections belonging to a specific role, pass the role name as an
|
299
|
+
argument. To switch to the new behavior, pass `:all` as the role name.
|
300
|
+
MSG
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def disconnect_pool_from_pool_manager(pool_manager, role, shard)
|
305
|
+
pool_config = pool_manager.remove_pool_config(role, shard)
|
306
|
+
|
307
|
+
if pool_config
|
308
|
+
pool_config.disconnect!
|
309
|
+
pool_config.db_config
|
310
|
+
end
|
246
311
|
end
|
247
312
|
|
248
313
|
# Returns an instance of PoolConfig for a given adapter.
|
@@ -255,7 +320,7 @@ module ActiveRecord
|
|
255
320
|
# pool_config.db_config.configuration_hash
|
256
321
|
# # => { host: "localhost", database: "foo", adapter: "sqlite3" }
|
257
322
|
#
|
258
|
-
def resolve_pool_config(config,
|
323
|
+
def resolve_pool_config(config, connection_name, role, shard)
|
259
324
|
db_config = Base.configurations.resolve(config)
|
260
325
|
|
261
326
|
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
|
@@ -285,7 +350,17 @@ module ActiveRecord
|
|
285
350
|
raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
|
286
351
|
end
|
287
352
|
|
288
|
-
ConnectionAdapters::PoolConfig.new(
|
353
|
+
ConnectionAdapters::PoolConfig.new(connection_name, db_config, role, shard)
|
354
|
+
end
|
355
|
+
|
356
|
+
def determine_owner_name(owner_name, config)
|
357
|
+
if owner_name.is_a?(String) || owner_name.is_a?(Symbol)
|
358
|
+
StringConnectionName.new(owner_name.to_s)
|
359
|
+
elsif config.is_a?(Symbol)
|
360
|
+
StringConnectionName.new(config.to_s)
|
361
|
+
else
|
362
|
+
owner_name
|
363
|
+
end
|
289
364
|
end
|
290
365
|
end
|
291
366
|
end
|
@@ -6,12 +6,14 @@ require "weakref"
|
|
6
6
|
module ActiveRecord
|
7
7
|
module ConnectionAdapters
|
8
8
|
class ConnectionPool
|
9
|
+
# = Active Record Connection Pool \Reaper
|
10
|
+
#
|
9
11
|
# Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
|
10
12
|
# +pool+. A reaper instantiated with a zero frequency will never reap
|
11
13
|
# the connection pool.
|
12
14
|
#
|
13
15
|
# Configure the frequency by setting +reaping_frequency+ in your database
|
14
|
-
#
|
16
|
+
# YAML file (default 60 seconds).
|
15
17
|
class Reaper
|
16
18
|
attr_reader :pool, :frequency
|
17
19
|
|
@@ -10,35 +10,33 @@ require "active_record/connection_adapters/abstract/connection_pool/reaper"
|
|
10
10
|
module ActiveRecord
|
11
11
|
module ConnectionAdapters
|
12
12
|
module AbstractPool # :nodoc:
|
13
|
-
def get_schema_cache(connection)
|
14
|
-
self.schema_cache ||= SchemaCache.new(connection)
|
15
|
-
schema_cache.connection = connection
|
16
|
-
schema_cache
|
17
|
-
end
|
18
|
-
|
19
|
-
def set_schema_cache(cache)
|
20
|
-
self.schema_cache = cache
|
21
|
-
end
|
22
|
-
|
23
|
-
def lazily_set_schema_cache
|
24
|
-
return unless ActiveRecord.lazily_load_schema_cache
|
25
|
-
|
26
|
-
cache = SchemaCache.load_from(db_config.lazy_schema_cache_path)
|
27
|
-
set_schema_cache(cache)
|
28
|
-
end
|
29
13
|
end
|
30
14
|
|
31
15
|
class NullPool # :nodoc:
|
32
16
|
include ConnectionAdapters::AbstractPool
|
33
17
|
|
34
|
-
|
18
|
+
class NullConfig # :nodoc:
|
19
|
+
def method_missing(*)
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
NULL_CONFIG = NullConfig.new # :nodoc:
|
24
|
+
|
25
|
+
def schema_reflection
|
26
|
+
SchemaReflection.new(nil)
|
27
|
+
end
|
35
28
|
|
36
29
|
def connection_class; end
|
37
30
|
def checkin(_); end
|
38
31
|
def remove(_); end
|
39
32
|
def async_executor; end
|
33
|
+
def db_config
|
34
|
+
NULL_CONFIG
|
35
|
+
end
|
40
36
|
end
|
41
37
|
|
38
|
+
# = Active Record Connection Pool
|
39
|
+
#
|
42
40
|
# Connection pool base class for managing Active Record database
|
43
41
|
# connections.
|
44
42
|
#
|
@@ -53,7 +51,7 @@ module ActiveRecord
|
|
53
51
|
# handle cases in which there are more threads than connections: if all
|
54
52
|
# connections have been checked out, and a thread tries to checkout a
|
55
53
|
# connection anyway, then ConnectionPool will wait until some other thread
|
56
|
-
# has checked in a connection.
|
54
|
+
# has checked in a connection, or the +checkout_timeout+ has expired.
|
57
55
|
#
|
58
56
|
# == Obtaining (checking out) a connection
|
59
57
|
#
|
@@ -64,7 +62,7 @@ module ActiveRecord
|
|
64
62
|
# as with Active Record 2.1 and
|
65
63
|
# earlier (pre-connection-pooling). Eventually, when you're done with
|
66
64
|
# the connection(s) and wish it to be returned to the pool, you call
|
67
|
-
# {ActiveRecord::Base.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
|
65
|
+
# {ActiveRecord::Base.connection_handler.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
|
68
66
|
# This will be the default behavior for Active Record when used in conjunction with
|
69
67
|
# Action Pack's request handling cycle.
|
70
68
|
# 2. Manually check out a connection from the pool with
|
@@ -78,6 +76,12 @@ module ActiveRecord
|
|
78
76
|
# Connections in the pool are actually AbstractAdapter objects (or objects
|
79
77
|
# compatible with AbstractAdapter's interface).
|
80
78
|
#
|
79
|
+
# While a thread has a connection checked out from the pool using one of the
|
80
|
+
# above three methods, that connection will automatically be the one used
|
81
|
+
# by ActiveRecord queries executing on that thread. It is not required to
|
82
|
+
# explicitly pass the checked out connection to \Rails models or queries, for
|
83
|
+
# example.
|
84
|
+
#
|
81
85
|
# == Options
|
82
86
|
#
|
83
87
|
# There are several connection-pooling-related options that you can add to
|
@@ -105,11 +109,9 @@ module ActiveRecord
|
|
105
109
|
include ConnectionAdapters::AbstractPool
|
106
110
|
|
107
111
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
108
|
-
attr_reader :db_config, :size, :reaper, :pool_config, :
|
112
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
|
109
113
|
|
110
|
-
|
111
|
-
deprecate :connection_klass
|
112
|
-
delegate :schema_cache, :schema_cache=, to: :pool_config
|
114
|
+
delegate :schema_reflection, :schema_reflection=, to: :pool_config
|
113
115
|
|
114
116
|
# Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
|
115
117
|
# object which describes database connection information (e.g. adapter,
|
@@ -122,7 +124,6 @@ module ActiveRecord
|
|
122
124
|
|
123
125
|
@pool_config = pool_config
|
124
126
|
@db_config = pool_config.db_config
|
125
|
-
@connection_class = pool_config.connection_class
|
126
127
|
@role = pool_config.role
|
127
128
|
@shard = pool_config.shard
|
128
129
|
|
@@ -158,18 +159,20 @@ module ActiveRecord
|
|
158
159
|
|
159
160
|
@async_executor = build_async_executor
|
160
161
|
|
161
|
-
lazily_set_schema_cache
|
162
|
-
|
163
162
|
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
164
163
|
@reaper.run
|
165
164
|
end
|
166
165
|
|
167
166
|
def lock_thread=(lock_thread)
|
168
167
|
if lock_thread
|
169
|
-
@lock_thread =
|
168
|
+
@lock_thread = ActiveSupport::IsolatedExecutionState.context
|
170
169
|
else
|
171
170
|
@lock_thread = nil
|
172
171
|
end
|
172
|
+
|
173
|
+
if (active_connection = @thread_cached_conns[connection_cache_key(current_thread)])
|
174
|
+
active_connection.lock_thread = @lock_thread
|
175
|
+
end
|
173
176
|
end
|
174
177
|
|
175
178
|
# Retrieve the connection associated with the current thread, or call
|
@@ -181,6 +184,12 @@ module ActiveRecord
|
|
181
184
|
@thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
|
182
185
|
end
|
183
186
|
|
187
|
+
def connection_class # :nodoc:
|
188
|
+
pool_config.connection_class
|
189
|
+
end
|
190
|
+
alias :connection_klass :connection_class
|
191
|
+
deprecate :connection_klass, deprecator: ActiveRecord.deprecator
|
192
|
+
|
184
193
|
# Returns true if there is an open connection being used for the current thread.
|
185
194
|
#
|
186
195
|
# This method only works for connections that have been obtained through
|
@@ -197,18 +206,23 @@ module ActiveRecord
|
|
197
206
|
# This method only works for connections that have been obtained through
|
198
207
|
# #connection or #with_connection methods, connections obtained through
|
199
208
|
# #checkout will not be automatically released.
|
200
|
-
def release_connection(owner_thread =
|
209
|
+
def release_connection(owner_thread = ActiveSupport::IsolatedExecutionState.context)
|
201
210
|
if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
|
202
211
|
checkin conn
|
203
212
|
end
|
204
213
|
end
|
205
214
|
|
206
|
-
#
|
207
|
-
# already
|
208
|
-
#
|
209
|
-
#
|
215
|
+
# Yields a connection from the connection pool to the block. If no connection
|
216
|
+
# is already checked out by the current thread, a connection will be checked
|
217
|
+
# out from the pool, yielded to the block, and then returned to the pool when
|
218
|
+
# the block is finished. If a connection has already been checked out on the
|
219
|
+
# current thread, such as via #connection or #with_connection, that existing
|
220
|
+
# connection will be the one yielded and it will not be returned to the pool
|
221
|
+
# automatically at the end of the block; it is expected that such an existing
|
222
|
+
# connection will be properly returned to the pool by the code that checked
|
223
|
+
# it out.
|
210
224
|
def with_connection
|
211
|
-
unless conn = @thread_cached_conns[connection_cache_key(
|
225
|
+
unless conn = @thread_cached_conns[connection_cache_key(ActiveSupport::IsolatedExecutionState.context)]
|
212
226
|
conn = connection
|
213
227
|
fresh_connection = true
|
214
228
|
end
|
@@ -338,7 +352,9 @@ module ActiveRecord
|
|
338
352
|
# Raises:
|
339
353
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
340
354
|
def checkout(checkout_timeout = @checkout_timeout)
|
341
|
-
checkout_and_verify(acquire_connection(checkout_timeout))
|
355
|
+
connection = checkout_and_verify(acquire_connection(checkout_timeout))
|
356
|
+
connection.lock_thread = @lock_thread
|
357
|
+
connection
|
342
358
|
end
|
343
359
|
|
344
360
|
# Check-in a database connection back into the pool, indicating that you
|
@@ -355,6 +371,7 @@ module ActiveRecord
|
|
355
371
|
conn.expire
|
356
372
|
end
|
357
373
|
|
374
|
+
conn.lock_thread = nil
|
358
375
|
@available.add conn
|
359
376
|
end
|
360
377
|
end
|
@@ -510,7 +527,7 @@ module ActiveRecord
|
|
510
527
|
end
|
511
528
|
|
512
529
|
def current_thread
|
513
|
-
@lock_thread ||
|
530
|
+
@lock_thread || ActiveSupport::IsolatedExecutionState.context
|
514
531
|
end
|
515
532
|
|
516
533
|
# Take control of all existing connections so a "group" action such as
|
@@ -527,13 +544,13 @@ module ActiveRecord
|
|
527
544
|
def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
|
528
545
|
collected_conns = synchronize do
|
529
546
|
# account for our own connections
|
530
|
-
@connections.select { |conn| conn.owner ==
|
547
|
+
@connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
|
531
548
|
end
|
532
549
|
|
533
550
|
newly_checked_out = []
|
534
551
|
timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
|
535
552
|
|
536
|
-
@available.with_a_bias_for(
|
553
|
+
@available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
|
537
554
|
loop do
|
538
555
|
synchronize do
|
539
556
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
@@ -580,7 +597,7 @@ module ActiveRecord
|
|
580
597
|
|
581
598
|
thread_report = []
|
582
599
|
@connections.each do |conn|
|
583
|
-
unless conn.owner ==
|
600
|
+
unless conn.owner == ActiveSupport::IsolatedExecutionState.context
|
584
601
|
thread_report << "#{conn} is owned by #{conn.owner}"
|
585
602
|
end
|
586
603
|
end
|
@@ -653,9 +670,12 @@ module ActiveRecord
|
|
653
670
|
alias_method :release, :remove_connection_from_thread_cache
|
654
671
|
|
655
672
|
def new_connection
|
656
|
-
Base.public_send(db_config.adapter_method, db_config.configuration_hash)
|
657
|
-
|
658
|
-
|
673
|
+
connection = Base.public_send(db_config.adapter_method, db_config.configuration_hash)
|
674
|
+
connection.pool = self
|
675
|
+
connection.check_version
|
676
|
+
connection
|
677
|
+
rescue ConnectionNotEstablished => ex
|
678
|
+
raise ex.set_pool(self)
|
659
679
|
end
|
660
680
|
|
661
681
|
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
@@ -702,10 +722,10 @@ module ActiveRecord
|
|
702
722
|
|
703
723
|
def checkout_and_verify(c)
|
704
724
|
c._run_checkout_callbacks do
|
705
|
-
c.
|
725
|
+
c.clean!
|
706
726
|
end
|
707
727
|
c
|
708
|
-
rescue
|
728
|
+
rescue Exception
|
709
729
|
remove c
|
710
730
|
c.disconnect!
|
711
731
|
raise
|