activerecord 7.0.8 → 7.1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1554 -1452
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -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 +20 -4
- 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 +15 -9
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- 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 +31 -7
- data/lib/active_record/associations/preloader.rb +13 -10
- 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 +313 -217
- 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 +52 -34
- 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 +74 -51
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- 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 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -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 +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -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 +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
- 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 +52 -39
- 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 +209 -79
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +175 -153
- 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 +9 -4
- 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 +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- 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 +112 -28
- data/lib/active_record/errors.rb +112 -18
- 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 +135 -71
- data/lib/active_record/future_result.rb +31 -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 +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -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 +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +104 -5
- data/lib/active_record/migration/compatibility.rb +139 -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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +219 -111
- data/lib/active_record/model_schema.rb +64 -44
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +188 -37
- 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 +109 -47
- data/lib/active_record/railties/controller_runtime.rb +12 -6
- data/lib/active_record/railties/databases.rake +142 -148
- 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 +174 -44
- 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 +187 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- 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/polymorphic_array_value.rb +4 -6
- 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 +352 -63
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +91 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -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 +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +27 -15
- 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 +1 -9
- 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 +48 -12
- 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,19 +51,17 @@ 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
|
#
|
60
58
|
# Connections can be obtained and used from a connection pool in several
|
61
59
|
# ways:
|
62
60
|
#
|
63
|
-
# 1. Simply use {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling.connection]
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
# {ActiveRecord::Base.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
|
68
|
-
# This will be the default behavior for Active Record when used in conjunction with
|
61
|
+
# 1. Simply use {ActiveRecord::Base.connection}[rdoc-ref:ConnectionHandling.connection].
|
62
|
+
# When you're done with the connection(s) and wish it to be returned to the pool, you call
|
63
|
+
# {ActiveRecord::Base.connection_handler.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
|
64
|
+
# This is the default behavior for Active Record when used in conjunction with
|
69
65
|
# Action Pack's request handling cycle.
|
70
66
|
# 2. Manually check out a connection from the pool with
|
71
67
|
# {ActiveRecord::Base.connection_pool.checkout}[rdoc-ref:#checkout]. You are responsible for
|
@@ -78,6 +74,12 @@ module ActiveRecord
|
|
78
74
|
# Connections in the pool are actually AbstractAdapter objects (or objects
|
79
75
|
# compatible with AbstractAdapter's interface).
|
80
76
|
#
|
77
|
+
# While a thread has a connection checked out from the pool using one of the
|
78
|
+
# above three methods, that connection will automatically be the one used
|
79
|
+
# by ActiveRecord queries executing on that thread. It is not required to
|
80
|
+
# explicitly pass the checked out connection to \Rails models or queries, for
|
81
|
+
# example.
|
82
|
+
#
|
81
83
|
# == Options
|
82
84
|
#
|
83
85
|
# There are several connection-pooling-related options that you can add to
|
@@ -105,11 +107,9 @@ module ActiveRecord
|
|
105
107
|
include ConnectionAdapters::AbstractPool
|
106
108
|
|
107
109
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
108
|
-
attr_reader :db_config, :size, :reaper, :pool_config, :
|
110
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
|
109
111
|
|
110
|
-
|
111
|
-
deprecate :connection_klass
|
112
|
-
delegate :schema_cache, :schema_cache=, to: :pool_config
|
112
|
+
delegate :schema_reflection, :schema_reflection=, to: :pool_config
|
113
113
|
|
114
114
|
# Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
|
115
115
|
# object which describes database connection information (e.g. adapter,
|
@@ -122,7 +122,6 @@ module ActiveRecord
|
|
122
122
|
|
123
123
|
@pool_config = pool_config
|
124
124
|
@db_config = pool_config.db_config
|
125
|
-
@connection_class = pool_config.connection_class
|
126
125
|
@role = pool_config.role
|
127
126
|
@shard = pool_config.shard
|
128
127
|
|
@@ -158,18 +157,20 @@ module ActiveRecord
|
|
158
157
|
|
159
158
|
@async_executor = build_async_executor
|
160
159
|
|
161
|
-
lazily_set_schema_cache
|
162
|
-
|
163
160
|
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
164
161
|
@reaper.run
|
165
162
|
end
|
166
163
|
|
167
164
|
def lock_thread=(lock_thread)
|
168
165
|
if lock_thread
|
169
|
-
@lock_thread =
|
166
|
+
@lock_thread = ActiveSupport::IsolatedExecutionState.context
|
170
167
|
else
|
171
168
|
@lock_thread = nil
|
172
169
|
end
|
170
|
+
|
171
|
+
if (active_connection = @thread_cached_conns[connection_cache_key(current_thread)])
|
172
|
+
active_connection.lock_thread = @lock_thread
|
173
|
+
end
|
173
174
|
end
|
174
175
|
|
175
176
|
# Retrieve the connection associated with the current thread, or call
|
@@ -181,6 +182,12 @@ module ActiveRecord
|
|
181
182
|
@thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
|
182
183
|
end
|
183
184
|
|
185
|
+
def connection_class # :nodoc:
|
186
|
+
pool_config.connection_class
|
187
|
+
end
|
188
|
+
alias :connection_klass :connection_class
|
189
|
+
deprecate :connection_klass, deprecator: ActiveRecord.deprecator
|
190
|
+
|
184
191
|
# Returns true if there is an open connection being used for the current thread.
|
185
192
|
#
|
186
193
|
# This method only works for connections that have been obtained through
|
@@ -197,18 +204,23 @@ module ActiveRecord
|
|
197
204
|
# This method only works for connections that have been obtained through
|
198
205
|
# #connection or #with_connection methods, connections obtained through
|
199
206
|
# #checkout will not be automatically released.
|
200
|
-
def release_connection(owner_thread =
|
207
|
+
def release_connection(owner_thread = ActiveSupport::IsolatedExecutionState.context)
|
201
208
|
if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
|
202
209
|
checkin conn
|
203
210
|
end
|
204
211
|
end
|
205
212
|
|
206
|
-
#
|
207
|
-
# already
|
208
|
-
#
|
209
|
-
#
|
213
|
+
# Yields a connection from the connection pool to the block. If no connection
|
214
|
+
# is already checked out by the current thread, a connection will be checked
|
215
|
+
# out from the pool, yielded to the block, and then returned to the pool when
|
216
|
+
# the block is finished. If a connection has already been checked out on the
|
217
|
+
# current thread, such as via #connection or #with_connection, that existing
|
218
|
+
# connection will be the one yielded and it will not be returned to the pool
|
219
|
+
# automatically at the end of the block; it is expected that such an existing
|
220
|
+
# connection will be properly returned to the pool by the code that checked
|
221
|
+
# it out.
|
210
222
|
def with_connection
|
211
|
-
unless conn = @thread_cached_conns[connection_cache_key(
|
223
|
+
unless conn = @thread_cached_conns[connection_cache_key(ActiveSupport::IsolatedExecutionState.context)]
|
212
224
|
conn = connection
|
213
225
|
fresh_connection = true
|
214
226
|
end
|
@@ -338,7 +350,9 @@ module ActiveRecord
|
|
338
350
|
# Raises:
|
339
351
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
340
352
|
def checkout(checkout_timeout = @checkout_timeout)
|
341
|
-
checkout_and_verify(acquire_connection(checkout_timeout))
|
353
|
+
connection = checkout_and_verify(acquire_connection(checkout_timeout))
|
354
|
+
connection.lock_thread = @lock_thread
|
355
|
+
connection
|
342
356
|
end
|
343
357
|
|
344
358
|
# Check-in a database connection back into the pool, indicating that you
|
@@ -355,6 +369,7 @@ module ActiveRecord
|
|
355
369
|
conn.expire
|
356
370
|
end
|
357
371
|
|
372
|
+
conn.lock_thread = nil
|
358
373
|
@available.add conn
|
359
374
|
end
|
360
375
|
end
|
@@ -448,8 +463,7 @@ module ActiveRecord
|
|
448
463
|
@available.num_waiting
|
449
464
|
end
|
450
465
|
|
451
|
-
#
|
452
|
-
# Example:
|
466
|
+
# Returns the connection pool's usage statistic.
|
453
467
|
#
|
454
468
|
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
455
469
|
def stat
|
@@ -510,7 +524,7 @@ module ActiveRecord
|
|
510
524
|
end
|
511
525
|
|
512
526
|
def current_thread
|
513
|
-
@lock_thread ||
|
527
|
+
@lock_thread || ActiveSupport::IsolatedExecutionState.context
|
514
528
|
end
|
515
529
|
|
516
530
|
# Take control of all existing connections so a "group" action such as
|
@@ -527,13 +541,13 @@ module ActiveRecord
|
|
527
541
|
def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
|
528
542
|
collected_conns = synchronize do
|
529
543
|
# account for our own connections
|
530
|
-
@connections.select { |conn| conn.owner ==
|
544
|
+
@connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
|
531
545
|
end
|
532
546
|
|
533
547
|
newly_checked_out = []
|
534
548
|
timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
|
535
549
|
|
536
|
-
@available.with_a_bias_for(
|
550
|
+
@available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
|
537
551
|
loop do
|
538
552
|
synchronize do
|
539
553
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
@@ -580,7 +594,7 @@ module ActiveRecord
|
|
580
594
|
|
581
595
|
thread_report = []
|
582
596
|
@connections.each do |conn|
|
583
|
-
unless conn.owner ==
|
597
|
+
unless conn.owner == ActiveSupport::IsolatedExecutionState.context
|
584
598
|
thread_report << "#{conn} is owned by #{conn.owner}"
|
585
599
|
end
|
586
600
|
end
|
@@ -641,7 +655,13 @@ module ActiveRecord
|
|
641
655
|
conn
|
642
656
|
else
|
643
657
|
reap
|
644
|
-
|
658
|
+
# Retry after reaping, which may return an available connection,
|
659
|
+
# remove an inactive connection, or both
|
660
|
+
if conn = @available.poll || try_to_checkout_new_connection
|
661
|
+
conn
|
662
|
+
else
|
663
|
+
@available.poll(checkout_timeout)
|
664
|
+
end
|
645
665
|
end
|
646
666
|
end
|
647
667
|
|
@@ -653,9 +673,12 @@ module ActiveRecord
|
|
653
673
|
alias_method :release, :remove_connection_from_thread_cache
|
654
674
|
|
655
675
|
def new_connection
|
656
|
-
Base.public_send(db_config.adapter_method, db_config.configuration_hash)
|
657
|
-
|
658
|
-
|
676
|
+
connection = Base.public_send(db_config.adapter_method, db_config.configuration_hash)
|
677
|
+
connection.pool = self
|
678
|
+
connection.check_version
|
679
|
+
connection
|
680
|
+
rescue ConnectionNotEstablished => ex
|
681
|
+
raise ex.set_pool(self)
|
659
682
|
end
|
660
683
|
|
661
684
|
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
@@ -702,10 +725,10 @@ module ActiveRecord
|
|
702
725
|
|
703
726
|
def checkout_and_verify(c)
|
704
727
|
c._run_checkout_callbacks do
|
705
|
-
c.
|
728
|
+
c.clean!
|
706
729
|
end
|
707
730
|
c
|
708
|
-
rescue
|
731
|
+
rescue Exception
|
709
732
|
remove c
|
710
733
|
c.disconnect!
|
711
734
|
raise
|