activerecord 5.2.3 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +898 -532
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +5 -4
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +95 -42
- data/lib/active_record/associations/association_scope.rb +21 -21
- data/lib/active_record/associations/belongs_to_association.rb +50 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -5
- data/lib/active_record/associations/builder/association.rb +23 -21
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +10 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +31 -29
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +27 -28
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/preloader/association.rb +71 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/preloader.rb +48 -35
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +133 -25
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attributes.rb +45 -8
- data/lib/active_record/autosave_association.rb +76 -47
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +293 -132
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +21 -17
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +203 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +381 -146
- data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +222 -112
- data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +175 -187
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +285 -33
- data/lib/active_record/core.rb +308 -100
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +71 -17
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +197 -481
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +26 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +26 -22
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +34 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +141 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +205 -156
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +115 -58
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +402 -78
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +113 -101
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/calculations.rb +157 -93
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +65 -40
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -7
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -40
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +487 -199
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +108 -58
- data/lib/active_record/relation.rb +375 -104
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +6 -8
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/scoping.rb +8 -9
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +51 -8
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -43
- data/lib/active_record/tasks/database_tasks.rb +276 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +59 -117
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +10 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +38 -30
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +13 -12
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +117 -32
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -3,22 +3,32 @@
|
|
3
3
|
require "thread"
|
4
4
|
require "concurrent/map"
|
5
5
|
require "monitor"
|
6
|
+
require "weakref"
|
6
7
|
|
7
8
|
module ActiveRecord
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
module ConnectionAdapters
|
10
|
+
module AbstractPool # :nodoc:
|
11
|
+
def get_schema_cache(connection)
|
12
|
+
self.schema_cache ||= SchemaCache.new(connection)
|
13
|
+
schema_cache.connection = connection
|
14
|
+
schema_cache
|
15
|
+
end
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def set_schema_cache(cache)
|
18
|
+
self.schema_cache = cache
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class NullPool # :nodoc:
|
23
|
+
include ConnectionAdapters::AbstractPool
|
24
|
+
|
25
|
+
attr_accessor :schema_cache
|
26
|
+
|
27
|
+
def owner_name
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
20
31
|
|
21
|
-
module ConnectionAdapters
|
22
32
|
# Connection pool base class for managing Active Record database
|
23
33
|
# connections.
|
24
34
|
#
|
@@ -129,7 +139,7 @@ module ActiveRecord
|
|
129
139
|
|
130
140
|
# Remove the head of the queue.
|
131
141
|
#
|
132
|
-
# If +timeout+ is not given, remove and return the head the
|
142
|
+
# If +timeout+ is not given, remove and return the head of the
|
133
143
|
# queue if the number of available elements is strictly
|
134
144
|
# greater than the number of threads currently waiting (that
|
135
145
|
# is, don't jump ahead in line). Otherwise, return +nil+.
|
@@ -146,7 +156,6 @@ module ActiveRecord
|
|
146
156
|
end
|
147
157
|
|
148
158
|
private
|
149
|
-
|
150
159
|
def internal_poll(timeout)
|
151
160
|
no_wait_poll || (timeout && wait_poll(timeout))
|
152
161
|
end
|
@@ -173,7 +182,7 @@ module ActiveRecord
|
|
173
182
|
@queue.pop
|
174
183
|
end
|
175
184
|
|
176
|
-
# Remove and return the head the queue if the number of
|
185
|
+
# Remove and return the head of the queue if the number of
|
177
186
|
# available elements is strictly greater than the number of
|
178
187
|
# threads currently waiting. Otherwise, return +nil+.
|
179
188
|
def no_wait_poll
|
@@ -185,7 +194,7 @@ module ActiveRecord
|
|
185
194
|
def wait_poll(timeout)
|
186
195
|
@num_waiting += 1
|
187
196
|
|
188
|
-
t0 =
|
197
|
+
t0 = Concurrent.monotonic_time
|
189
198
|
elapsed = 0
|
190
199
|
loop do
|
191
200
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
@@ -194,7 +203,7 @@ module ActiveRecord
|
|
194
203
|
|
195
204
|
return remove if any?
|
196
205
|
|
197
|
-
elapsed =
|
206
|
+
elapsed = Concurrent.monotonic_time - t0
|
198
207
|
if elapsed >= timeout
|
199
208
|
msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
|
200
209
|
[timeout, elapsed]
|
@@ -294,50 +303,90 @@ module ActiveRecord
|
|
294
303
|
@frequency = frequency
|
295
304
|
end
|
296
305
|
|
306
|
+
@mutex = Mutex.new
|
307
|
+
@pools = {}
|
308
|
+
@threads = {}
|
309
|
+
|
310
|
+
class << self
|
311
|
+
def register_pool(pool, frequency) # :nodoc:
|
312
|
+
@mutex.synchronize do
|
313
|
+
unless @threads[frequency]&.alive?
|
314
|
+
@threads[frequency] = spawn_thread(frequency)
|
315
|
+
end
|
316
|
+
@pools[frequency] ||= []
|
317
|
+
@pools[frequency] << WeakRef.new(pool)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
private
|
322
|
+
def spawn_thread(frequency)
|
323
|
+
Thread.new(frequency) do |t|
|
324
|
+
# Advise multi-threaded app servers to ignore this thread for
|
325
|
+
# the purposes of fork safety warnings
|
326
|
+
Thread.current.thread_variable_set(:fork_safe, true)
|
327
|
+
running = true
|
328
|
+
while running
|
329
|
+
sleep t
|
330
|
+
@mutex.synchronize do
|
331
|
+
@pools[frequency].select! do |pool|
|
332
|
+
pool.weakref_alive? && !pool.discarded?
|
333
|
+
end
|
334
|
+
|
335
|
+
@pools[frequency].each do |p|
|
336
|
+
p.reap
|
337
|
+
p.flush
|
338
|
+
rescue WeakRef::RefError
|
339
|
+
end
|
340
|
+
|
341
|
+
if @pools[frequency].empty?
|
342
|
+
@pools.delete(frequency)
|
343
|
+
@threads.delete(frequency)
|
344
|
+
running = false
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
297
352
|
def run
|
298
353
|
return unless frequency && frequency > 0
|
299
|
-
|
300
|
-
loop do
|
301
|
-
sleep t
|
302
|
-
p.reap
|
303
|
-
p.flush
|
304
|
-
end
|
305
|
-
}
|
354
|
+
self.class.register_pool(pool, frequency)
|
306
355
|
end
|
307
356
|
end
|
308
357
|
|
309
358
|
include MonitorMixin
|
310
359
|
include QueryCache::ConnectionPoolConfiguration
|
360
|
+
include ConnectionAdapters::AbstractPool
|
311
361
|
|
312
|
-
attr_accessor :automatic_reconnect, :checkout_timeout
|
313
|
-
attr_reader :
|
362
|
+
attr_accessor :automatic_reconnect, :checkout_timeout
|
363
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :owner_name
|
314
364
|
|
315
|
-
|
365
|
+
delegate :schema_cache, :schema_cache=, to: :pool_config
|
366
|
+
|
367
|
+
# Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
|
316
368
|
# object which describes database connection information (e.g. adapter,
|
317
369
|
# host name, username, password, etc), as well as the maximum size for
|
318
370
|
# this ConnectionPool.
|
319
371
|
#
|
320
372
|
# The default ConnectionPool maximum size is 5.
|
321
|
-
def initialize(
|
373
|
+
def initialize(pool_config)
|
322
374
|
super()
|
323
375
|
|
324
|
-
@
|
325
|
-
|
326
|
-
@
|
327
|
-
if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
|
328
|
-
@idle_timeout = @idle_timeout.to_f
|
329
|
-
@idle_timeout = nil if @idle_timeout <= 0
|
330
|
-
end
|
376
|
+
@pool_config = pool_config
|
377
|
+
@db_config = pool_config.db_config
|
378
|
+
@owner_name = pool_config.connection_specification_name
|
331
379
|
|
332
|
-
|
333
|
-
@
|
380
|
+
@checkout_timeout = db_config.checkout_timeout
|
381
|
+
@idle_timeout = db_config.idle_timeout
|
382
|
+
@size = db_config.pool
|
334
383
|
|
335
384
|
# This variable tracks the cache of threads mapped to reserved connections, with the
|
336
385
|
# sole purpose of speeding up the +connection+ method. It is not the authoritative
|
337
386
|
# registry of which thread owns which connection. Connection ownership is tracked by
|
338
387
|
# the +connection.owner+ attr on each +connection+ instance.
|
339
388
|
# The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
|
340
|
-
# then that +thread+ does indeed own that +conn+. However, an absence of
|
389
|
+
# then that +thread+ does indeed own that +conn+. However, an absence of such
|
341
390
|
# mapping does not mean that the +thread+ doesn't own the said connection. In
|
342
391
|
# that case +conn.owner+ attr should be consulted.
|
343
392
|
# Access and modification of <tt>@thread_cached_conns</tt> does not require
|
@@ -358,10 +407,7 @@ module ActiveRecord
|
|
358
407
|
|
359
408
|
@lock_thread = false
|
360
409
|
|
361
|
-
|
362
|
-
# also be useful if someone wants a very low +idle_timeout+.
|
363
|
-
reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
|
364
|
-
@reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
|
410
|
+
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
365
411
|
@reaper.run
|
366
412
|
end
|
367
413
|
|
@@ -379,7 +425,7 @@ module ActiveRecord
|
|
379
425
|
# #connection can be called any number of times; the connection is
|
380
426
|
# held in a cache keyed by a thread.
|
381
427
|
def connection
|
382
|
-
@thread_cached_conns[connection_cache_key(
|
428
|
+
@thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
|
383
429
|
end
|
384
430
|
|
385
431
|
# Returns true if there is an open connection being used for the current thread.
|
@@ -388,7 +434,7 @@ module ActiveRecord
|
|
388
434
|
# #connection or #with_connection methods. Connections obtained through
|
389
435
|
# #checkout will not be detected by #active_connection?
|
390
436
|
def active_connection?
|
391
|
-
@thread_cached_conns[connection_cache_key(
|
437
|
+
@thread_cached_conns[connection_cache_key(current_thread)]
|
392
438
|
end
|
393
439
|
|
394
440
|
# Signal that the thread is finished with the current connection.
|
@@ -423,12 +469,27 @@ module ActiveRecord
|
|
423
469
|
synchronize { @connections.any? }
|
424
470
|
end
|
425
471
|
|
472
|
+
# Returns an array containing the connections currently in the pool.
|
473
|
+
# Access to the array does not require synchronization on the pool because
|
474
|
+
# the array is newly created and not retained by the pool.
|
475
|
+
#
|
476
|
+
# However; this method bypasses the ConnectionPool's thread-safe connection
|
477
|
+
# access pattern. A returned connection may be owned by another thread,
|
478
|
+
# unowned, or by happen-stance owned by the calling thread.
|
479
|
+
#
|
480
|
+
# Calling methods on a connection without ownership is subject to the
|
481
|
+
# thread-safety guarantees of the underlying method. Many of the methods
|
482
|
+
# on connection adapter classes are inherently multi-thread unsafe.
|
483
|
+
def connections
|
484
|
+
synchronize { @connections.dup }
|
485
|
+
end
|
486
|
+
|
426
487
|
# Disconnects all connections in the pool, and clears the pool.
|
427
488
|
#
|
428
489
|
# Raises:
|
429
490
|
# - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
|
430
491
|
# connections in the pool within a timeout interval (default duration is
|
431
|
-
# <tt>spec.
|
492
|
+
# <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
|
432
493
|
def disconnect(raise_on_acquisition_timeout = true)
|
433
494
|
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
|
434
495
|
synchronize do
|
@@ -449,7 +510,7 @@ module ActiveRecord
|
|
449
510
|
#
|
450
511
|
# The pool first tries to gain ownership of all connections. If unable to
|
451
512
|
# do so within a timeout interval (default duration is
|
452
|
-
# <tt>spec.
|
513
|
+
# <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool is forcefully
|
453
514
|
# disconnected without any regard for other connection owning threads.
|
454
515
|
def disconnect!
|
455
516
|
disconnect(false)
|
@@ -462,7 +523,7 @@ module ActiveRecord
|
|
462
523
|
# See AbstractAdapter#discard!
|
463
524
|
def discard! # :nodoc:
|
464
525
|
synchronize do
|
465
|
-
return if
|
526
|
+
return if self.discarded?
|
466
527
|
@connections.each do |conn|
|
467
528
|
conn.discard!
|
468
529
|
end
|
@@ -470,13 +531,17 @@ module ActiveRecord
|
|
470
531
|
end
|
471
532
|
end
|
472
533
|
|
534
|
+
def discarded? # :nodoc:
|
535
|
+
@connections.nil?
|
536
|
+
end
|
537
|
+
|
473
538
|
# Clears the cache which maps classes and re-connects connections that
|
474
539
|
# require reloading.
|
475
540
|
#
|
476
541
|
# Raises:
|
477
542
|
# - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
|
478
543
|
# connections in the pool within a timeout interval (default duration is
|
479
|
-
# <tt>spec.
|
544
|
+
# <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
|
480
545
|
def clear_reloadable_connections(raise_on_acquisition_timeout = true)
|
481
546
|
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
|
482
547
|
synchronize do
|
@@ -498,7 +563,7 @@ module ActiveRecord
|
|
498
563
|
#
|
499
564
|
# The pool first tries to gain ownership of all connections. If unable to
|
500
565
|
# do so within a timeout interval (default duration is
|
501
|
-
# <tt>spec.
|
566
|
+
# <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool forcefully
|
502
567
|
# clears the cache and reloads connections without any regard for other
|
503
568
|
# connection owning threads.
|
504
569
|
def clear_reloadable_connections!
|
@@ -578,6 +643,7 @@ module ActiveRecord
|
|
578
643
|
# or a thread dies unexpectedly.
|
579
644
|
def reap
|
580
645
|
stale_connections = synchronize do
|
646
|
+
return if self.discarded?
|
581
647
|
@connections.select do |conn|
|
582
648
|
conn.in_use? && !conn.owner.alive?
|
583
649
|
end.each do |conn|
|
@@ -602,6 +668,7 @@ module ActiveRecord
|
|
602
668
|
return if minimum_idle.nil?
|
603
669
|
|
604
670
|
idle_connections = synchronize do
|
671
|
+
return if self.discarded?
|
605
672
|
@connections.select do |conn|
|
606
673
|
!conn.in_use? && conn.seconds_idle >= minimum_idle
|
607
674
|
end.each do |conn|
|
@@ -668,6 +735,10 @@ module ActiveRecord
|
|
668
735
|
thread
|
669
736
|
end
|
670
737
|
|
738
|
+
def current_thread
|
739
|
+
@lock_thread || Thread.current
|
740
|
+
end
|
741
|
+
|
671
742
|
# Take control of all existing connections so a "group" action such as
|
672
743
|
# reload/disconnect can be performed safely. It is no longer enough to
|
673
744
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
@@ -686,13 +757,13 @@ module ActiveRecord
|
|
686
757
|
end
|
687
758
|
|
688
759
|
newly_checked_out = []
|
689
|
-
timeout_time =
|
760
|
+
timeout_time = Concurrent.monotonic_time + (@checkout_timeout * 2)
|
690
761
|
|
691
762
|
@available.with_a_bias_for(Thread.current) do
|
692
763
|
loop do
|
693
764
|
synchronize do
|
694
765
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
695
|
-
remaining_timeout = timeout_time -
|
766
|
+
remaining_timeout = timeout_time - Concurrent.monotonic_time
|
696
767
|
remaining_timeout = 0 if remaining_timeout < 0
|
697
768
|
conn = checkout_for_exclusive_access(remaining_timeout)
|
698
769
|
collected_conns << conn
|
@@ -731,7 +802,7 @@ module ActiveRecord
|
|
731
802
|
# this block can't be easily moved into attempt_to_checkout_all_existing_connections's
|
732
803
|
# rescue block, because doing so would put it outside of synchronize section, without
|
733
804
|
# being in a critical section thread_report might become inaccurate
|
734
|
-
msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
|
805
|
+
msg = +"could not obtain ownership of all database connections in #{checkout_timeout} seconds"
|
735
806
|
|
736
807
|
thread_report = []
|
737
808
|
@connections.each do |conn|
|
@@ -808,8 +879,8 @@ module ActiveRecord
|
|
808
879
|
alias_method :release, :remove_connection_from_thread_cache
|
809
880
|
|
810
881
|
def new_connection
|
811
|
-
Base.
|
812
|
-
conn.
|
882
|
+
Base.public_send(db_config.adapter_method, db_config.configuration_hash).tap do |conn|
|
883
|
+
conn.check_version
|
813
884
|
end
|
814
885
|
end
|
815
886
|
|
@@ -912,156 +983,246 @@ module ActiveRecord
|
|
912
983
|
# should use.
|
913
984
|
#
|
914
985
|
# The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
|
915
|
-
# about the model. The model needs to pass a specification name to the handler,
|
986
|
+
# about the model. The model needs to pass a connection specification name to the handler,
|
916
987
|
# in order to look up the correct connection pool.
|
917
988
|
class ConnectionHandler
|
918
|
-
|
919
|
-
|
920
|
-
# Discard the parent's connection pools immediately; we have no need
|
921
|
-
# of them
|
922
|
-
discard_unowned_pools(h)
|
989
|
+
FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
|
990
|
+
private_constant :FINALIZER
|
923
991
|
|
924
|
-
|
925
|
-
|
992
|
+
def initialize
|
993
|
+
# These caches are keyed by pool_config.connection_specification_name (PoolConfig#connection_specification_name).
|
994
|
+
@owner_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
|
995
|
+
|
996
|
+
# Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
|
997
|
+
ObjectSpace.define_finalizer self, FINALIZER
|
926
998
|
end
|
927
999
|
|
928
|
-
def
|
929
|
-
|
930
|
-
discard_unowned_pools(pid_map)
|
931
|
-
end
|
1000
|
+
def prevent_writes # :nodoc:
|
1001
|
+
Thread.current[:prevent_writes]
|
932
1002
|
end
|
933
1003
|
|
934
|
-
def
|
935
|
-
|
936
|
-
|
1004
|
+
def prevent_writes=(prevent_writes) # :nodoc:
|
1005
|
+
Thread.current[:prevent_writes] = prevent_writes
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
# Prevent writing to the database regardless of role.
|
1009
|
+
#
|
1010
|
+
# In some cases you may want to prevent writes to the database
|
1011
|
+
# even if you are on a database that can write. `while_preventing_writes`
|
1012
|
+
# will prevent writes to the database for the duration of the block.
|
1013
|
+
#
|
1014
|
+
# This method does not provide the same protection as a readonly
|
1015
|
+
# user and is meant to be a safeguard against accidental writes.
|
1016
|
+
#
|
1017
|
+
# See `READ_QUERY` for the queries that are blocked by this
|
1018
|
+
# method.
|
1019
|
+
def while_preventing_writes(enabled = true)
|
1020
|
+
unless ActiveRecord::Base.legacy_connection_handling
|
1021
|
+
raise NotImplementedError, "`while_preventing_writes` is only available on the connection_handler with legacy_connection_handling"
|
937
1022
|
end
|
1023
|
+
|
1024
|
+
original, self.prevent_writes = self.prevent_writes, enabled
|
1025
|
+
yield
|
1026
|
+
ensure
|
1027
|
+
self.prevent_writes = original
|
938
1028
|
end
|
939
1029
|
|
940
|
-
def
|
941
|
-
|
942
|
-
|
1030
|
+
def connection_pool_names # :nodoc:
|
1031
|
+
owner_to_pool_manager.keys
|
1032
|
+
end
|
943
1033
|
|
944
|
-
|
945
|
-
|
946
|
-
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
|
1034
|
+
def all_connection_pools
|
1035
|
+
owner_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
|
947
1036
|
end
|
948
1037
|
|
949
|
-
def connection_pool_list
|
950
|
-
|
1038
|
+
def connection_pool_list(role = ActiveRecord::Base.current_role)
|
1039
|
+
owner_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
|
951
1040
|
end
|
952
1041
|
alias :connection_pools :connection_pool_list
|
953
1042
|
|
954
|
-
def establish_connection(config)
|
955
|
-
|
956
|
-
spec = resolver.spec(config)
|
1043
|
+
def establish_connection(config, owner_name: Base.name, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
|
1044
|
+
owner_name = config.to_s if config.is_a?(Symbol)
|
957
1045
|
|
958
|
-
|
1046
|
+
pool_config = resolve_pool_config(config, owner_name)
|
1047
|
+
db_config = pool_config.db_config
|
1048
|
+
|
1049
|
+
# Protects the connection named `ActiveRecord::Base` from being removed
|
1050
|
+
# if the user calls `establish_connection :primary`.
|
1051
|
+
if owner_to_pool_manager.key?(pool_config.connection_specification_name)
|
1052
|
+
remove_connection_pool(pool_config.connection_specification_name, role: role, shard: shard)
|
1053
|
+
end
|
959
1054
|
|
960
1055
|
message_bus = ActiveSupport::Notifications.instrumenter
|
961
|
-
payload = {
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
payload[:
|
966
|
-
payload[:config] = spec.config
|
1056
|
+
payload = {}
|
1057
|
+
if pool_config
|
1058
|
+
payload[:spec_name] = pool_config.connection_specification_name
|
1059
|
+
payload[:shard] = shard
|
1060
|
+
payload[:config] = db_config.configuration_hash
|
967
1061
|
end
|
968
1062
|
|
969
|
-
|
970
|
-
|
1063
|
+
if ActiveRecord::Base.legacy_connection_handling
|
1064
|
+
owner_to_pool_manager[pool_config.connection_specification_name] ||= LegacyPoolManager.new
|
1065
|
+
else
|
1066
|
+
owner_to_pool_manager[pool_config.connection_specification_name] ||= PoolManager.new
|
971
1067
|
end
|
1068
|
+
pool_manager = get_pool_manager(pool_config.connection_specification_name)
|
1069
|
+
pool_manager.set_pool_config(role, shard, pool_config)
|
972
1070
|
|
973
|
-
|
1071
|
+
message_bus.instrument("!connection.active_record", payload) do
|
1072
|
+
pool_config.pool
|
1073
|
+
end
|
974
1074
|
end
|
975
1075
|
|
976
1076
|
# Returns true if there are any active connections among the connection
|
977
1077
|
# pools that the ConnectionHandler is managing.
|
978
|
-
def active_connections?
|
979
|
-
connection_pool_list.any?(&:active_connection?)
|
1078
|
+
def active_connections?(role = ActiveRecord::Base.current_role)
|
1079
|
+
connection_pool_list(role).any?(&:active_connection?)
|
980
1080
|
end
|
981
1081
|
|
982
1082
|
# Returns any connections in use by the current thread back to the pool,
|
983
1083
|
# and also returns connections to the pool cached by threads that are no
|
984
1084
|
# longer alive.
|
985
|
-
def clear_active_connections!
|
986
|
-
connection_pool_list.each(&:release_connection)
|
1085
|
+
def clear_active_connections!(role = ActiveRecord::Base.current_role)
|
1086
|
+
connection_pool_list(role).each(&:release_connection)
|
987
1087
|
end
|
988
1088
|
|
989
1089
|
# Clears the cache which maps classes.
|
990
1090
|
#
|
991
1091
|
# See ConnectionPool#clear_reloadable_connections! for details.
|
992
|
-
def clear_reloadable_connections!
|
993
|
-
connection_pool_list.each(&:clear_reloadable_connections!)
|
1092
|
+
def clear_reloadable_connections!(role = ActiveRecord::Base.current_role)
|
1093
|
+
connection_pool_list(role).each(&:clear_reloadable_connections!)
|
994
1094
|
end
|
995
1095
|
|
996
|
-
def clear_all_connections!
|
997
|
-
connection_pool_list.each(&:disconnect!)
|
1096
|
+
def clear_all_connections!(role = ActiveRecord::Base.current_role)
|
1097
|
+
connection_pool_list(role).each(&:disconnect!)
|
998
1098
|
end
|
999
1099
|
|
1000
1100
|
# Disconnects all currently idle connections.
|
1001
1101
|
#
|
1002
1102
|
# See ConnectionPool#flush! for details.
|
1003
|
-
def flush_idle_connections!
|
1004
|
-
connection_pool_list.each(&:flush!)
|
1103
|
+
def flush_idle_connections!(role = ActiveRecord::Base.current_role)
|
1104
|
+
connection_pool_list(role).each(&:flush!)
|
1005
1105
|
end
|
1006
1106
|
|
1007
1107
|
# Locate the connection of the nearest super class. This can be an
|
1008
1108
|
# active or defined connection: if it is the latter, it will be
|
1009
1109
|
# opened and set as the active connection for the class it was defined
|
1010
1110
|
# for (not necessarily the current class).
|
1011
|
-
def retrieve_connection(spec_name)
|
1012
|
-
pool = retrieve_connection_pool(spec_name)
|
1013
|
-
|
1111
|
+
def retrieve_connection(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) # :nodoc:
|
1112
|
+
pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
|
1113
|
+
|
1114
|
+
unless pool
|
1115
|
+
if shard != ActiveRecord::Base.default_shard
|
1116
|
+
message = "No connection pool for '#{spec_name}' found for the '#{shard}' shard."
|
1117
|
+
elsif ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
|
1118
|
+
message = "No connection pool for '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
|
1119
|
+
elsif role != ActiveRecord::Base.default_role
|
1120
|
+
message = "No connection pool for '#{spec_name}' found for the '#{role}' role."
|
1121
|
+
else
|
1122
|
+
message = "No connection pool for '#{spec_name}' found."
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
raise ConnectionNotEstablished, message
|
1126
|
+
end
|
1127
|
+
|
1014
1128
|
pool.connection
|
1015
1129
|
end
|
1016
1130
|
|
1017
1131
|
# Returns true if a connection that's accessible to this class has
|
1018
1132
|
# already been opened.
|
1019
|
-
def connected?(spec_name)
|
1020
|
-
|
1021
|
-
|
1133
|
+
def connected?(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
1134
|
+
pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
|
1135
|
+
pool && pool.connected?
|
1022
1136
|
end
|
1023
1137
|
|
1024
1138
|
# Remove the connection for this class. This will close the active
|
1025
1139
|
# connection and the defined connection (if they exist). The result
|
1026
1140
|
# can be used as an argument for #establish_connection, for easily
|
1027
1141
|
# re-establishing the connection.
|
1028
|
-
def remove_connection(
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1142
|
+
def remove_connection(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
1143
|
+
remove_connection_pool(owner, role: role, shard: shard)&.configuration_hash
|
1144
|
+
end
|
1145
|
+
deprecate remove_connection: "Use #remove_connection_pool, which now returns a DatabaseConfig object instead of a Hash"
|
1146
|
+
|
1147
|
+
def remove_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
1148
|
+
if pool_manager = get_pool_manager(owner)
|
1149
|
+
pool_config = pool_manager.remove_pool_config(role, shard)
|
1150
|
+
|
1151
|
+
if pool_config
|
1152
|
+
pool_config.disconnect!
|
1153
|
+
pool_config.db_config
|
1154
|
+
end
|
1033
1155
|
end
|
1034
1156
|
end
|
1035
1157
|
|
1036
|
-
# Retrieving the connection pool happens a lot, so we cache it in @
|
1158
|
+
# Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool_manager.
|
1037
1159
|
# This makes retrieving the connection pool O(1) once the process is warm.
|
1038
1160
|
# When a connection is established or removed, we invalidate the cache.
|
1039
|
-
def retrieve_connection_pool(
|
1040
|
-
|
1041
|
-
|
1042
|
-
# which may have been forked.
|
1043
|
-
if ancestor_pool = pool_from_any_process_for(spec_name)
|
1044
|
-
# A connection was established in an ancestor process that must have
|
1045
|
-
# subsequently forked. We can't reuse the connection, but we can copy
|
1046
|
-
# the specification and establish a new connection with it.
|
1047
|
-
establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
|
1048
|
-
pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
|
1049
|
-
end
|
1050
|
-
else
|
1051
|
-
owner_to_pool[spec_name] = nil
|
1052
|
-
end
|
1053
|
-
end
|
1161
|
+
def retrieve_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
1162
|
+
pool_config = get_pool_manager(owner)&.get_pool_config(role, shard)
|
1163
|
+
pool_config&.pool
|
1054
1164
|
end
|
1055
1165
|
|
1056
1166
|
private
|
1167
|
+
attr_reader :owner_to_pool_manager
|
1057
1168
|
|
1058
|
-
|
1059
|
-
|
1169
|
+
# Returns the pool manager for an owner.
|
1170
|
+
#
|
1171
|
+
# Using `"primary"` to look up the pool manager for `ActiveRecord::Base` is
|
1172
|
+
# deprecated in favor of looking it up by `"ActiveRecord::Base"`.
|
1173
|
+
#
|
1174
|
+
# During the deprecation period, if `"primary"` is passed, the pool manager
|
1175
|
+
# for `ActiveRecord::Base` will still be returned.
|
1176
|
+
def get_pool_manager(owner)
|
1177
|
+
return owner_to_pool_manager[owner] if owner_to_pool_manager.key?(owner)
|
1178
|
+
|
1179
|
+
if owner == "primary"
|
1180
|
+
ActiveSupport::Deprecation.warn("Using `\"primary\"` as a `connection_specification_name` is deprecated and will be removed in Rails 6.2.0. Please use `ActiveRecord::Base`.")
|
1181
|
+
owner_to_pool_manager[Base.name]
|
1182
|
+
end
|
1060
1183
|
end
|
1061
1184
|
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1185
|
+
# Returns an instance of PoolConfig for a given adapter.
|
1186
|
+
# Accepts a hash one layer deep that contains all connection information.
|
1187
|
+
#
|
1188
|
+
# == Example
|
1189
|
+
#
|
1190
|
+
# config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
|
1191
|
+
# pool_config = Base.configurations.resolve_pool_config(:production)
|
1192
|
+
# pool_config.db_config.configuration_hash
|
1193
|
+
# # => { host: "localhost", database: "foo", adapter: "sqlite3" }
|
1194
|
+
#
|
1195
|
+
def resolve_pool_config(config, owner_name)
|
1196
|
+
db_config = Base.configurations.resolve(config)
|
1197
|
+
|
1198
|
+
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
|
1199
|
+
|
1200
|
+
# Require the adapter itself and give useful feedback about
|
1201
|
+
# 1. Missing adapter gems and
|
1202
|
+
# 2. Adapter gems' missing dependencies.
|
1203
|
+
path_to_adapter = "active_record/connection_adapters/#{db_config.adapter}_adapter"
|
1204
|
+
begin
|
1205
|
+
require path_to_adapter
|
1206
|
+
rescue LoadError => e
|
1207
|
+
# We couldn't require the adapter itself. Raise an exception that
|
1208
|
+
# points out config typos and missing gems.
|
1209
|
+
if e.path == path_to_adapter
|
1210
|
+
# We can assume that a non-builtin adapter was specified, so it's
|
1211
|
+
# either misspelled or missing from Gemfile.
|
1212
|
+
raise LoadError, "Could not load the '#{db_config.adapter}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
|
1213
|
+
|
1214
|
+
# Bubbled up from the adapter require. Prefix the exception message
|
1215
|
+
# with some guidance about how to address it and reraise.
|
1216
|
+
else
|
1217
|
+
raise LoadError, "Error loading the '#{db_config.adapter}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
unless ActiveRecord::Base.respond_to?(db_config.adapter_method)
|
1222
|
+
raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
|
1223
|
+
end
|
1224
|
+
|
1225
|
+
ConnectionAdapters::PoolConfig.new(owner_name, db_config)
|
1065
1226
|
end
|
1066
1227
|
end
|
1067
1228
|
end
|