activerecord 6.1.7 → 7.2.0
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 +520 -1385
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +404 -509
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +2 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +51 -49
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +18 -34
- 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 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- 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 +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +276 -251
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +170 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +163 -63
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- 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 +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- 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 +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +143 -159
- data/lib/active_record/nested_attributes.rb +48 -21
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +234 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +325 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +300 -111
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +842 -150
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +277 -149
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- 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 +64 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- 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
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +100 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -3,32 +3,56 @@
|
|
3
3
|
require "thread"
|
4
4
|
require "concurrent/map"
|
5
5
|
require "monitor"
|
6
|
-
|
6
|
+
|
7
|
+
require "active_record/connection_adapters/abstract/connection_pool/queue"
|
8
|
+
require "active_record/connection_adapters/abstract/connection_pool/reaper"
|
7
9
|
|
8
10
|
module ActiveRecord
|
9
11
|
module ConnectionAdapters
|
10
12
|
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
|
16
|
-
|
17
|
-
def set_schema_cache(cache)
|
18
|
-
self.schema_cache = cache
|
19
|
-
end
|
20
13
|
end
|
21
14
|
|
22
15
|
class NullPool # :nodoc:
|
23
16
|
include ConnectionAdapters::AbstractPool
|
24
17
|
|
25
|
-
|
18
|
+
class NullConfig # :nodoc:
|
19
|
+
def method_missing(...)
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
NULL_CONFIG = NullConfig.new # :nodoc:
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
super()
|
27
|
+
@mutex = Mutex.new
|
28
|
+
@server_version = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def server_version(connection) # :nodoc:
|
32
|
+
@server_version || @mutex.synchronize { @server_version ||= connection.get_database_version }
|
33
|
+
end
|
34
|
+
|
35
|
+
def schema_reflection
|
36
|
+
SchemaReflection.new(nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
def schema_cache; end
|
40
|
+
def connection_class; end
|
41
|
+
def checkin(_); end
|
42
|
+
def remove(_); end
|
43
|
+
def async_executor; end
|
44
|
+
|
45
|
+
def db_config
|
46
|
+
NULL_CONFIG
|
47
|
+
end
|
26
48
|
|
27
|
-
def
|
28
|
-
|
49
|
+
def dirties_query_cache
|
50
|
+
true
|
29
51
|
end
|
30
52
|
end
|
31
53
|
|
54
|
+
# = Active Record Connection Pool
|
55
|
+
#
|
32
56
|
# Connection pool base class for managing Active Record database
|
33
57
|
# connections.
|
34
58
|
#
|
@@ -43,19 +67,17 @@ module ActiveRecord
|
|
43
67
|
# handle cases in which there are more threads than connections: if all
|
44
68
|
# connections have been checked out, and a thread tries to checkout a
|
45
69
|
# connection anyway, then ConnectionPool will wait until some other thread
|
46
|
-
# has checked in a connection.
|
70
|
+
# has checked in a connection, or the +checkout_timeout+ has expired.
|
47
71
|
#
|
48
72
|
# == Obtaining (checking out) a connection
|
49
73
|
#
|
50
74
|
# Connections can be obtained and used from a connection pool in several
|
51
75
|
# ways:
|
52
76
|
#
|
53
|
-
# 1. Simply use {ActiveRecord::Base.
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# {ActiveRecord::Base.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
|
58
|
-
# This will be the default behavior for Active Record when used in conjunction with
|
77
|
+
# 1. Simply use {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection].
|
78
|
+
# When you're done with the connection(s) and wish it to be returned to the pool, you call
|
79
|
+
# {ActiveRecord::Base.connection_handler.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
|
80
|
+
# This is the default behavior for Active Record when used in conjunction with
|
59
81
|
# Action Pack's request handling cycle.
|
60
82
|
# 2. Manually check out a connection from the pool with
|
61
83
|
# {ActiveRecord::Base.connection_pool.checkout}[rdoc-ref:#checkout]. You are responsible for
|
@@ -68,6 +90,12 @@ module ActiveRecord
|
|
68
90
|
# Connections in the pool are actually AbstractAdapter objects (or objects
|
69
91
|
# compatible with AbstractAdapter's interface).
|
70
92
|
#
|
93
|
+
# While a thread has a connection checked out from the pool using one of the
|
94
|
+
# above three methods, that connection will automatically be the one used
|
95
|
+
# by ActiveRecord queries executing on that thread. It is not required to
|
96
|
+
# explicitly pass the checked out connection to \Rails models or queries, for
|
97
|
+
# example.
|
98
|
+
#
|
71
99
|
# == Options
|
72
100
|
#
|
73
101
|
# There are several connection-pooling-related options that you can add to
|
@@ -90,279 +118,98 @@ module ActiveRecord
|
|
90
118
|
# * private methods that require being called in a +synchronize+ blocks
|
91
119
|
# are now explicitly documented
|
92
120
|
class ConnectionPool
|
93
|
-
|
94
|
-
|
95
|
-
class Queue
|
96
|
-
def initialize(lock = Monitor.new)
|
97
|
-
@lock = lock
|
98
|
-
@cond = @lock.new_cond
|
99
|
-
@num_waiting = 0
|
100
|
-
@queue = []
|
101
|
-
end
|
121
|
+
class Lease # :nodoc:
|
122
|
+
attr_accessor :connection, :sticky
|
102
123
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@num_waiting > 0
|
107
|
-
end
|
124
|
+
def initialize
|
125
|
+
@connection = nil
|
126
|
+
@sticky = nil
|
108
127
|
end
|
109
128
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
129
|
+
def release
|
130
|
+
conn = @connection
|
131
|
+
@connection = nil
|
132
|
+
@sticky = nil
|
133
|
+
conn
|
116
134
|
end
|
117
135
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
@
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
# If +element+ is in the queue, remove and return it, or +nil+.
|
127
|
-
def delete(element)
|
128
|
-
synchronize do
|
129
|
-
@queue.delete(element)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
# Remove all elements from the queue.
|
134
|
-
def clear
|
135
|
-
synchronize do
|
136
|
-
@queue.clear
|
136
|
+
def clear(connection)
|
137
|
+
if @connection == connection
|
138
|
+
@connection = nil
|
139
|
+
@sticky = nil
|
140
|
+
true
|
141
|
+
else
|
142
|
+
false
|
137
143
|
end
|
138
144
|
end
|
145
|
+
end
|
139
146
|
|
140
|
-
|
141
|
-
#
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
#
|
151
|
-
# Raises:
|
152
|
-
# - ActiveRecord::ConnectionTimeoutError if +timeout+ is given and no element
|
153
|
-
# becomes available within +timeout+ seconds,
|
154
|
-
def poll(timeout = nil)
|
155
|
-
synchronize { internal_poll(timeout) }
|
156
|
-
end
|
157
|
-
|
158
|
-
private
|
159
|
-
def internal_poll(timeout)
|
160
|
-
no_wait_poll || (timeout && wait_poll(timeout))
|
161
|
-
end
|
162
|
-
|
163
|
-
def synchronize(&block)
|
164
|
-
@lock.synchronize(&block)
|
165
|
-
end
|
166
|
-
|
167
|
-
# Test if the queue currently contains any elements.
|
168
|
-
def any?
|
169
|
-
!@queue.empty?
|
170
|
-
end
|
171
|
-
|
172
|
-
# A thread can remove an element from the queue without
|
173
|
-
# waiting if and only if the number of currently available
|
174
|
-
# connections is strictly greater than the number of waiting
|
175
|
-
# threads.
|
176
|
-
def can_remove_no_wait?
|
177
|
-
@queue.size > @num_waiting
|
178
|
-
end
|
179
|
-
|
180
|
-
# Removes and returns the head of the queue if possible, or +nil+.
|
181
|
-
def remove
|
182
|
-
@queue.pop
|
183
|
-
end
|
184
|
-
|
185
|
-
# Remove and return the head of the queue if the number of
|
186
|
-
# available elements is strictly greater than the number of
|
187
|
-
# threads currently waiting. Otherwise, return +nil+.
|
188
|
-
def no_wait_poll
|
189
|
-
remove if can_remove_no_wait?
|
190
|
-
end
|
147
|
+
class LeaseRegistry # :nodoc:
|
148
|
+
if ObjectSpace.const_defined?(:WeakKeyMap) # RUBY_VERSION >= 3.3
|
149
|
+
WeakKeyMap = ::ObjectSpace::WeakKeyMap # :nodoc:
|
150
|
+
else
|
151
|
+
class WeakKeyMap # :nodoc:
|
152
|
+
def initialize
|
153
|
+
@map = ObjectSpace::WeakMap.new
|
154
|
+
@values = nil
|
155
|
+
@size = 0
|
156
|
+
end
|
191
157
|
|
192
|
-
|
193
|
-
# returns the head of the queue.
|
194
|
-
def wait_poll(timeout)
|
195
|
-
@num_waiting += 1
|
158
|
+
alias_method :clear, :initialize
|
196
159
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
@cond.wait(timeout - elapsed)
|
202
|
-
end
|
160
|
+
def [](key)
|
161
|
+
prune if @map.size != @size
|
162
|
+
@map[key]
|
163
|
+
end
|
203
164
|
|
204
|
-
|
165
|
+
def []=(key, value)
|
166
|
+
@map[key] = value
|
167
|
+
prune if @map.size != @size
|
168
|
+
value
|
169
|
+
end
|
205
170
|
|
206
|
-
|
207
|
-
if
|
208
|
-
|
209
|
-
|
210
|
-
raise ConnectionTimeoutError, msg
|
171
|
+
def delete(key)
|
172
|
+
if value = self[key]
|
173
|
+
self[key] = nil
|
174
|
+
prune
|
211
175
|
end
|
176
|
+
value
|
212
177
|
end
|
213
|
-
ensure
|
214
|
-
@num_waiting -= 1
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
# Adds the ability to turn a basic fair FIFO queue into one
|
219
|
-
# biased to some thread.
|
220
|
-
module BiasableQueue # :nodoc:
|
221
|
-
class BiasedConditionVariable # :nodoc:
|
222
|
-
# semantics of condition variables guarantee that +broadcast+, +broadcast_on_biased+,
|
223
|
-
# +signal+ and +wait+ methods are only called while holding a lock
|
224
|
-
def initialize(lock, other_cond, preferred_thread)
|
225
|
-
@real_cond = lock.new_cond
|
226
|
-
@other_cond = other_cond
|
227
|
-
@preferred_thread = preferred_thread
|
228
|
-
@num_waiting_on_real_cond = 0
|
229
|
-
end
|
230
|
-
|
231
|
-
def broadcast
|
232
|
-
broadcast_on_biased
|
233
|
-
@other_cond.broadcast
|
234
|
-
end
|
235
178
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
def signal
|
242
|
-
if @num_waiting_on_real_cond > 0
|
243
|
-
@num_waiting_on_real_cond -= 1
|
244
|
-
@real_cond
|
245
|
-
else
|
246
|
-
@other_cond
|
247
|
-
end.signal
|
248
|
-
end
|
249
|
-
|
250
|
-
def wait(timeout)
|
251
|
-
if Thread.current == @preferred_thread
|
252
|
-
@num_waiting_on_real_cond += 1
|
253
|
-
@real_cond
|
254
|
-
else
|
255
|
-
@other_cond
|
256
|
-
end.wait(timeout)
|
179
|
+
private
|
180
|
+
def prune(force = false)
|
181
|
+
@values = @map.values
|
182
|
+
@size = @map.size
|
183
|
+
end
|
257
184
|
end
|
258
185
|
end
|
259
186
|
|
260
|
-
def
|
261
|
-
|
262
|
-
|
263
|
-
synchronize do
|
264
|
-
previous_cond = @cond
|
265
|
-
@cond = new_cond = BiasedConditionVariable.new(@lock, @cond, thread)
|
266
|
-
end
|
267
|
-
yield
|
268
|
-
ensure
|
269
|
-
synchronize do
|
270
|
-
@cond = previous_cond if previous_cond
|
271
|
-
new_cond.broadcast_on_biased if new_cond # wake up any remaining sleepers
|
272
|
-
end
|
187
|
+
def initialize
|
188
|
+
@mutex = Mutex.new
|
189
|
+
@map = WeakKeyMap.new
|
273
190
|
end
|
274
|
-
end
|
275
191
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
# <tt>@lock</tt> as the main pool) so that a returned connection is already
|
280
|
-
# leased and there is no need to re-enter synchronized block.
|
281
|
-
class ConnectionLeasingQueue < Queue # :nodoc:
|
282
|
-
include BiasableQueue
|
283
|
-
|
284
|
-
private
|
285
|
-
def internal_poll(timeout)
|
286
|
-
conn = super
|
287
|
-
conn.lease if conn
|
288
|
-
conn
|
192
|
+
def [](context)
|
193
|
+
@mutex.synchronize do
|
194
|
+
@map[context] ||= Lease.new
|
289
195
|
end
|
290
|
-
end
|
291
|
-
|
292
|
-
# Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
|
293
|
-
# +pool+. A reaper instantiated with a zero frequency will never reap
|
294
|
-
# the connection pool.
|
295
|
-
#
|
296
|
-
# Configure the frequency by setting +reaping_frequency+ in your database
|
297
|
-
# yaml file (default 60 seconds).
|
298
|
-
class Reaper
|
299
|
-
attr_reader :pool, :frequency
|
300
|
-
|
301
|
-
def initialize(pool, frequency)
|
302
|
-
@pool = pool
|
303
|
-
@frequency = frequency
|
304
196
|
end
|
305
197
|
|
306
|
-
|
307
|
-
|
308
|
-
|
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
|
198
|
+
def clear
|
199
|
+
@mutex.synchronize do
|
200
|
+
@map = WeakKeyMap.new
|
319
201
|
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
|
-
|
352
|
-
def run
|
353
|
-
return unless frequency && frequency > 0
|
354
|
-
self.class.register_pool(pool, frequency)
|
355
202
|
end
|
356
203
|
end
|
357
204
|
|
358
205
|
include MonitorMixin
|
359
|
-
|
206
|
+
prepend QueryCache::ConnectionPoolConfiguration
|
360
207
|
include ConnectionAdapters::AbstractPool
|
361
208
|
|
362
209
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
363
|
-
attr_reader :db_config, :size, :reaper, :pool_config, :
|
210
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
|
364
211
|
|
365
|
-
delegate :
|
212
|
+
delegate :schema_reflection, :server_version, to: :pool_config
|
366
213
|
|
367
214
|
# Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
|
368
215
|
# object which describes database connection information (e.g. adapter,
|
@@ -375,7 +222,8 @@ module ActiveRecord
|
|
375
222
|
|
376
223
|
@pool_config = pool_config
|
377
224
|
@db_config = pool_config.db_config
|
378
|
-
@
|
225
|
+
@role = pool_config.role
|
226
|
+
@shard = pool_config.shard
|
379
227
|
|
380
228
|
@checkout_timeout = db_config.checkout_timeout
|
381
229
|
@idle_timeout = db_config.idle_timeout
|
@@ -389,9 +237,9 @@ module ActiveRecord
|
|
389
237
|
# then that +thread+ does indeed own that +conn+. However, an absence of such
|
390
238
|
# mapping does not mean that the +thread+ doesn't own the said connection. In
|
391
239
|
# that case +conn.owner+ attr should be consulted.
|
392
|
-
# Access and modification of <tt>@
|
240
|
+
# Access and modification of <tt>@leases</tt> does not require
|
393
241
|
# synchronization.
|
394
|
-
@
|
242
|
+
@leases = LeaseRegistry.new
|
395
243
|
|
396
244
|
@connections = []
|
397
245
|
@automatic_reconnect = true
|
@@ -404,69 +252,175 @@ module ActiveRecord
|
|
404
252
|
@threads_blocking_new_connections = 0
|
405
253
|
|
406
254
|
@available = ConnectionLeasingQueue.new self
|
255
|
+
@pinned_connection = nil
|
256
|
+
@pinned_connections_depth = 0
|
257
|
+
|
258
|
+
@async_executor = build_async_executor
|
407
259
|
|
408
|
-
@
|
260
|
+
@schema_cache = nil
|
409
261
|
|
410
262
|
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
411
263
|
@reaper.run
|
412
264
|
end
|
413
265
|
|
414
|
-
def
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
266
|
+
def inspect # :nodoc:
|
267
|
+
name_field = " name=#{db_config.name.inspect}" unless db_config.name == "primary"
|
268
|
+
shard_field = " shard=#{@shard.inspect}" unless @shard == :default
|
269
|
+
|
270
|
+
"#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
271
|
+
end
|
272
|
+
|
273
|
+
def schema_cache
|
274
|
+
@schema_cache ||= BoundSchemaReflection.new(schema_reflection, self)
|
275
|
+
end
|
276
|
+
|
277
|
+
def schema_reflection=(schema_reflection)
|
278
|
+
pool_config.schema_reflection = schema_reflection
|
279
|
+
@schema_cache = nil
|
280
|
+
end
|
281
|
+
|
282
|
+
def migration_context # :nodoc:
|
283
|
+
MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
|
284
|
+
end
|
285
|
+
|
286
|
+
def migrations_paths # :nodoc:
|
287
|
+
db_config.migrations_paths || Migrator.migrations_paths
|
288
|
+
end
|
289
|
+
|
290
|
+
def schema_migration # :nodoc:
|
291
|
+
SchemaMigration.new(self)
|
292
|
+
end
|
293
|
+
|
294
|
+
def internal_metadata # :nodoc:
|
295
|
+
InternalMetadata.new(self)
|
420
296
|
end
|
421
297
|
|
422
298
|
# Retrieve the connection associated with the current thread, or call
|
423
299
|
# #checkout to obtain one if necessary.
|
424
300
|
#
|
425
|
-
# #
|
301
|
+
# #lease_connection can be called any number of times; the connection is
|
426
302
|
# held in a cache keyed by a thread.
|
303
|
+
def lease_connection
|
304
|
+
lease = connection_lease
|
305
|
+
lease.sticky = true
|
306
|
+
lease.connection ||= checkout
|
307
|
+
end
|
308
|
+
|
309
|
+
def permanent_lease? # :nodoc:
|
310
|
+
connection_lease.sticky.nil?
|
311
|
+
end
|
312
|
+
|
427
313
|
def connection
|
428
|
-
|
314
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
315
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
|
316
|
+
and will be removed in Rails 8.0. Use #lease_connection instead.
|
317
|
+
MSG
|
318
|
+
lease_connection
|
319
|
+
end
|
320
|
+
|
321
|
+
def pin_connection!(lock_thread) # :nodoc:
|
322
|
+
@pinned_connection ||= (connection_lease&.connection || checkout)
|
323
|
+
@pinned_connections_depth += 1
|
324
|
+
|
325
|
+
# Any leased connection must be in @connections otherwise
|
326
|
+
# some methods like #connected? won't behave correctly
|
327
|
+
unless @connections.include?(@pinned_connection)
|
328
|
+
@connections << @pinned_connection
|
329
|
+
end
|
330
|
+
|
331
|
+
@pinned_connection.lock_thread = ActiveSupport::IsolatedExecutionState.context if lock_thread
|
332
|
+
@pinned_connection.verify! # eagerly validate the connection
|
333
|
+
@pinned_connection.begin_transaction joinable: false, _lazy: false
|
334
|
+
end
|
335
|
+
|
336
|
+
def unpin_connection! # :nodoc:
|
337
|
+
raise "There isn't a pinned connection #{object_id}" unless @pinned_connection
|
338
|
+
|
339
|
+
clean = true
|
340
|
+
@pinned_connection.lock.synchronize do
|
341
|
+
@pinned_connections_depth -= 1
|
342
|
+
connection = @pinned_connection
|
343
|
+
@pinned_connection = nil if @pinned_connections_depth.zero?
|
344
|
+
|
345
|
+
if connection.transaction_open?
|
346
|
+
connection.rollback_transaction
|
347
|
+
else
|
348
|
+
# Something committed or rolled back the transaction
|
349
|
+
clean = false
|
350
|
+
connection.reset!
|
351
|
+
end
|
352
|
+
|
353
|
+
if @pinned_connection.nil?
|
354
|
+
connection.lock_thread = nil
|
355
|
+
checkin(connection)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
clean
|
360
|
+
end
|
361
|
+
|
362
|
+
def connection_class # :nodoc:
|
363
|
+
pool_config.connection_class
|
429
364
|
end
|
430
365
|
|
431
366
|
# Returns true if there is an open connection being used for the current thread.
|
432
367
|
#
|
433
368
|
# This method only works for connections that have been obtained through
|
434
|
-
# #
|
369
|
+
# #lease_connection or #with_connection methods. Connections obtained through
|
435
370
|
# #checkout will not be detected by #active_connection?
|
436
371
|
def active_connection?
|
437
|
-
|
372
|
+
connection_lease.connection
|
438
373
|
end
|
374
|
+
alias_method :active_connection, :active_connection? # :nodoc:
|
439
375
|
|
440
376
|
# Signal that the thread is finished with the current connection.
|
441
377
|
# #release_connection releases the connection-thread association
|
442
378
|
# and returns the connection to the pool.
|
443
379
|
#
|
444
380
|
# This method only works for connections that have been obtained through
|
445
|
-
# #
|
381
|
+
# #lease_connection or #with_connection methods, connections obtained through
|
446
382
|
# #checkout will not be automatically released.
|
447
|
-
def release_connection(
|
448
|
-
if conn =
|
383
|
+
def release_connection(existing_lease = nil)
|
384
|
+
if conn = connection_lease.release
|
449
385
|
checkin conn
|
386
|
+
return true
|
387
|
+
end
|
388
|
+
false
|
389
|
+
end
|
390
|
+
|
391
|
+
# Yields a connection from the connection pool to the block. If no connection
|
392
|
+
# is already checked out by the current thread, a connection will be checked
|
393
|
+
# out from the pool, yielded to the block, and then returned to the pool when
|
394
|
+
# the block is finished. If a connection has already been checked out on the
|
395
|
+
# current thread, such as via #lease_connection or #with_connection, that existing
|
396
|
+
# connection will be the one yielded and it will not be returned to the pool
|
397
|
+
# automatically at the end of the block; it is expected that such an existing
|
398
|
+
# connection will be properly returned to the pool by the code that checked
|
399
|
+
# it out.
|
400
|
+
def with_connection(prevent_permanent_checkout: false)
|
401
|
+
lease = connection_lease
|
402
|
+
sticky_was = lease.sticky
|
403
|
+
lease.sticky = false if prevent_permanent_checkout
|
404
|
+
|
405
|
+
if lease.connection
|
406
|
+
begin
|
407
|
+
yield lease.connection
|
408
|
+
ensure
|
409
|
+
lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
|
410
|
+
end
|
411
|
+
else
|
412
|
+
begin
|
413
|
+
yield lease.connection = checkout
|
414
|
+
ensure
|
415
|
+
lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
|
416
|
+
release_connection(lease) unless lease.sticky
|
417
|
+
end
|
450
418
|
end
|
451
419
|
end
|
452
420
|
|
453
|
-
# If a connection obtained through #connection or #with_connection methods
|
454
|
-
# already exists yield it to the block. If no such connection
|
455
|
-
# exists checkout a connection, yield it to the block, and checkin the
|
456
|
-
# connection when finished.
|
457
|
-
def with_connection
|
458
|
-
unless conn = @thread_cached_conns[connection_cache_key(Thread.current)]
|
459
|
-
conn = connection
|
460
|
-
fresh_connection = true
|
461
|
-
end
|
462
|
-
yield conn
|
463
|
-
ensure
|
464
|
-
release_connection if fresh_connection
|
465
|
-
end
|
466
|
-
|
467
421
|
# Returns true if a connection has already been opened.
|
468
422
|
def connected?
|
469
|
-
synchronize { @connections.any? }
|
423
|
+
synchronize { @connections.any?(&:connected?) }
|
470
424
|
end
|
471
425
|
|
472
426
|
# Returns an array containing the connections currently in the pool.
|
@@ -501,6 +455,7 @@ module ActiveRecord
|
|
501
455
|
conn.disconnect!
|
502
456
|
end
|
503
457
|
@connections = []
|
458
|
+
@leases.clear
|
504
459
|
@available.clear
|
505
460
|
end
|
506
461
|
end
|
@@ -527,7 +482,7 @@ module ActiveRecord
|
|
527
482
|
@connections.each do |conn|
|
528
483
|
conn.discard!
|
529
484
|
end
|
530
|
-
@connections = @available = @
|
485
|
+
@connections = @available = @leases = nil
|
531
486
|
end
|
532
487
|
end
|
533
488
|
|
@@ -585,7 +540,21 @@ module ActiveRecord
|
|
585
540
|
# Raises:
|
586
541
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
587
542
|
def checkout(checkout_timeout = @checkout_timeout)
|
588
|
-
|
543
|
+
if @pinned_connection
|
544
|
+
@pinned_connection.lock.synchronize do
|
545
|
+
synchronize do
|
546
|
+
@pinned_connection.verify!
|
547
|
+
# Any leased connection must be in @connections otherwise
|
548
|
+
# some methods like #connected? won't behave correctly
|
549
|
+
unless @connections.include?(@pinned_connection)
|
550
|
+
@connections << @pinned_connection
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
@pinned_connection
|
555
|
+
else
|
556
|
+
checkout_and_verify(acquire_connection(checkout_timeout))
|
557
|
+
end
|
589
558
|
end
|
590
559
|
|
591
560
|
# Check-in a database connection back into the pool, indicating that you
|
@@ -594,9 +563,11 @@ module ActiveRecord
|
|
594
563
|
# +conn+: an AbstractAdapter object, which was obtained by earlier by
|
595
564
|
# calling #checkout on this pool.
|
596
565
|
def checkin(conn)
|
566
|
+
return if @pinned_connection.equal?(conn)
|
567
|
+
|
597
568
|
conn.lock.synchronize do
|
598
569
|
synchronize do
|
599
|
-
|
570
|
+
connection_lease.clear(conn)
|
600
571
|
|
601
572
|
conn._run_checkin_callbacks do
|
602
573
|
conn.expire
|
@@ -659,6 +630,8 @@ module ActiveRecord
|
|
659
630
|
remove conn
|
660
631
|
end
|
661
632
|
end
|
633
|
+
|
634
|
+
prune_thread_cache
|
662
635
|
end
|
663
636
|
|
664
637
|
# Disconnect all connections that have been idle for at least
|
@@ -695,8 +668,7 @@ module ActiveRecord
|
|
695
668
|
@available.num_waiting
|
696
669
|
end
|
697
670
|
|
698
|
-
#
|
699
|
-
# Example:
|
671
|
+
# Returns the connection pool's usage statistic.
|
700
672
|
#
|
701
673
|
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
702
674
|
def stat
|
@@ -713,7 +685,32 @@ module ActiveRecord
|
|
713
685
|
end
|
714
686
|
end
|
715
687
|
|
688
|
+
def schedule_query(future_result) # :nodoc:
|
689
|
+
@async_executor.post { future_result.execute_or_skip }
|
690
|
+
Thread.pass
|
691
|
+
end
|
692
|
+
|
716
693
|
private
|
694
|
+
def connection_lease
|
695
|
+
@leases[ActiveSupport::IsolatedExecutionState.context]
|
696
|
+
end
|
697
|
+
|
698
|
+
def build_async_executor
|
699
|
+
case ActiveRecord.async_query_executor
|
700
|
+
when :multi_thread_pool
|
701
|
+
if @db_config.max_threads > 0
|
702
|
+
Concurrent::ThreadPoolExecutor.new(
|
703
|
+
min_threads: @db_config.min_threads,
|
704
|
+
max_threads: @db_config.max_threads,
|
705
|
+
max_queue: @db_config.max_queue,
|
706
|
+
fallback_policy: :caller_runs
|
707
|
+
)
|
708
|
+
end
|
709
|
+
when :global_thread_pool
|
710
|
+
ActiveRecord.global_thread_pool_async_query_executor
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
717
714
|
#--
|
718
715
|
# this is unfortunately not concurrent
|
719
716
|
def bulk_make_new_connections(num_new_conns_needed)
|
@@ -726,19 +723,6 @@ module ActiveRecord
|
|
726
723
|
end
|
727
724
|
end
|
728
725
|
|
729
|
-
#--
|
730
|
-
# From the discussion on GitHub:
|
731
|
-
# https://github.com/rails/rails/pull/14938#commitcomment-6601951
|
732
|
-
# This hook-in method allows for easier monkey-patching fixes needed by
|
733
|
-
# JRuby users that use Fibers.
|
734
|
-
def connection_cache_key(thread)
|
735
|
-
thread
|
736
|
-
end
|
737
|
-
|
738
|
-
def current_thread
|
739
|
-
@lock_thread || Thread.current
|
740
|
-
end
|
741
|
-
|
742
726
|
# Take control of all existing connections so a "group" action such as
|
743
727
|
# reload/disconnect can be performed safely. It is no longer enough to
|
744
728
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
@@ -752,18 +736,21 @@ module ActiveRecord
|
|
752
736
|
|
753
737
|
def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
|
754
738
|
collected_conns = synchronize do
|
739
|
+
reap # No need to wait for dead owners
|
740
|
+
|
755
741
|
# account for our own connections
|
756
|
-
@connections.select { |conn| conn.owner ==
|
742
|
+
@connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
|
757
743
|
end
|
758
744
|
|
759
745
|
newly_checked_out = []
|
760
|
-
timeout_time =
|
746
|
+
timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
|
761
747
|
|
762
|
-
@available.with_a_bias_for(
|
748
|
+
@available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
|
763
749
|
loop do
|
764
750
|
synchronize do
|
765
751
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
766
|
-
|
752
|
+
|
753
|
+
remaining_timeout = timeout_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
767
754
|
remaining_timeout = 0 if remaining_timeout < 0
|
768
755
|
conn = checkout_for_exclusive_access(remaining_timeout)
|
769
756
|
collected_conns << conn
|
@@ -806,14 +793,14 @@ module ActiveRecord
|
|
806
793
|
|
807
794
|
thread_report = []
|
808
795
|
@connections.each do |conn|
|
809
|
-
unless conn.owner ==
|
796
|
+
unless conn.owner == ActiveSupport::IsolatedExecutionState.context
|
810
797
|
thread_report << "#{conn} is owned by #{conn.owner}"
|
811
798
|
end
|
812
799
|
end
|
813
800
|
|
814
801
|
msg << " (#{thread_report.join(', ')})" if thread_report.any?
|
815
802
|
|
816
|
-
raise ExclusiveConnectionTimeoutError,
|
803
|
+
raise ExclusiveConnectionTimeoutError.new(msg, connection_pool: self)
|
817
804
|
end
|
818
805
|
|
819
806
|
def with_new_connections_blocked
|
@@ -867,21 +854,31 @@ module ActiveRecord
|
|
867
854
|
conn
|
868
855
|
else
|
869
856
|
reap
|
870
|
-
|
857
|
+
# Retry after reaping, which may return an available connection,
|
858
|
+
# remove an inactive connection, or both
|
859
|
+
if conn = @available.poll || try_to_checkout_new_connection
|
860
|
+
conn
|
861
|
+
else
|
862
|
+
@available.poll(checkout_timeout)
|
863
|
+
end
|
871
864
|
end
|
865
|
+
rescue ConnectionTimeoutError => ex
|
866
|
+
raise ex.set_pool(self)
|
872
867
|
end
|
873
868
|
|
874
869
|
#--
|
875
870
|
# if owner_thread param is omitted, this must be called in synchronize block
|
876
871
|
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
877
|
-
@
|
872
|
+
@leases[owner_thread].clear(conn)
|
878
873
|
end
|
879
874
|
alias_method :release, :remove_connection_from_thread_cache
|
880
875
|
|
881
876
|
def new_connection
|
882
|
-
|
883
|
-
|
884
|
-
|
877
|
+
connection = db_config.new_connection
|
878
|
+
connection.pool = self
|
879
|
+
connection
|
880
|
+
rescue ConnectionNotEstablished => ex
|
881
|
+
raise ex.set_pool(self)
|
885
882
|
end
|
886
883
|
|
887
884
|
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
@@ -919,6 +916,12 @@ module ActiveRecord
|
|
919
916
|
def adopt_connection(conn)
|
920
917
|
conn.pool = self
|
921
918
|
@connections << conn
|
919
|
+
|
920
|
+
# We just created the first connection, it's time to load the schema
|
921
|
+
# cache if that wasn't eagerly done before
|
922
|
+
if @schema_cache.nil? && ActiveRecord.lazily_load_schema_cache
|
923
|
+
schema_cache.load!
|
924
|
+
end
|
922
925
|
end
|
923
926
|
|
924
927
|
def checkout_new_connection
|
@@ -928,302 +931,14 @@ module ActiveRecord
|
|
928
931
|
|
929
932
|
def checkout_and_verify(c)
|
930
933
|
c._run_checkout_callbacks do
|
931
|
-
c.
|
934
|
+
c.clean!
|
932
935
|
end
|
933
936
|
c
|
934
|
-
rescue
|
937
|
+
rescue Exception
|
935
938
|
remove c
|
936
939
|
c.disconnect!
|
937
940
|
raise
|
938
941
|
end
|
939
942
|
end
|
940
|
-
|
941
|
-
# ConnectionHandler is a collection of ConnectionPool objects. It is used
|
942
|
-
# for keeping separate connection pools that connect to different databases.
|
943
|
-
#
|
944
|
-
# For example, suppose that you have 5 models, with the following hierarchy:
|
945
|
-
#
|
946
|
-
# class Author < ActiveRecord::Base
|
947
|
-
# end
|
948
|
-
#
|
949
|
-
# class BankAccount < ActiveRecord::Base
|
950
|
-
# end
|
951
|
-
#
|
952
|
-
# class Book < ActiveRecord::Base
|
953
|
-
# establish_connection :library_db
|
954
|
-
# end
|
955
|
-
#
|
956
|
-
# class ScaryBook < Book
|
957
|
-
# end
|
958
|
-
#
|
959
|
-
# class GoodBook < Book
|
960
|
-
# end
|
961
|
-
#
|
962
|
-
# And a database.yml that looked like this:
|
963
|
-
#
|
964
|
-
# development:
|
965
|
-
# database: my_application
|
966
|
-
# host: localhost
|
967
|
-
#
|
968
|
-
# library_db:
|
969
|
-
# database: library
|
970
|
-
# host: some.library.org
|
971
|
-
#
|
972
|
-
# Your primary database in the development environment is "my_application"
|
973
|
-
# but the Book model connects to a separate database called "library_db"
|
974
|
-
# (this can even be a database on a different machine).
|
975
|
-
#
|
976
|
-
# Book, ScaryBook and GoodBook will all use the same connection pool to
|
977
|
-
# "library_db" while Author, BankAccount, and any other models you create
|
978
|
-
# will use the default connection pool to "my_application".
|
979
|
-
#
|
980
|
-
# The various connection pools are managed by a single instance of
|
981
|
-
# ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
|
982
|
-
# All Active Record models use this handler to determine the connection pool that they
|
983
|
-
# should use.
|
984
|
-
#
|
985
|
-
# The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
|
986
|
-
# about the model. The model needs to pass a connection specification name to the handler,
|
987
|
-
# in order to look up the correct connection pool.
|
988
|
-
class ConnectionHandler
|
989
|
-
FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
|
990
|
-
private_constant :FINALIZER
|
991
|
-
|
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
|
998
|
-
end
|
999
|
-
|
1000
|
-
def prevent_writes # :nodoc:
|
1001
|
-
Thread.current[:prevent_writes]
|
1002
|
-
end
|
1003
|
-
|
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"
|
1022
|
-
end
|
1023
|
-
|
1024
|
-
original, self.prevent_writes = self.prevent_writes, enabled
|
1025
|
-
yield
|
1026
|
-
ensure
|
1027
|
-
self.prevent_writes = original
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
def connection_pool_names # :nodoc:
|
1031
|
-
owner_to_pool_manager.keys
|
1032
|
-
end
|
1033
|
-
|
1034
|
-
def all_connection_pools
|
1035
|
-
owner_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
|
1036
|
-
end
|
1037
|
-
|
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) }
|
1040
|
-
end
|
1041
|
-
alias :connection_pools :connection_pool_list
|
1042
|
-
|
1043
|
-
def establish_connection(config, owner_name: Base, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
|
1044
|
-
owner_name = config.to_s if config.is_a?(Symbol)
|
1045
|
-
|
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
|
1054
|
-
|
1055
|
-
message_bus = ActiveSupport::Notifications.instrumenter
|
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
|
1061
|
-
end
|
1062
|
-
|
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
|
1067
|
-
end
|
1068
|
-
pool_manager = get_pool_manager(pool_config.connection_specification_name)
|
1069
|
-
pool_manager.set_pool_config(role, shard, pool_config)
|
1070
|
-
|
1071
|
-
message_bus.instrument("!connection.active_record", payload) do
|
1072
|
-
pool_config.pool
|
1073
|
-
end
|
1074
|
-
end
|
1075
|
-
|
1076
|
-
# Returns true if there are any active connections among the connection
|
1077
|
-
# pools that the ConnectionHandler is managing.
|
1078
|
-
def active_connections?(role = ActiveRecord::Base.current_role)
|
1079
|
-
connection_pool_list(role).any?(&:active_connection?)
|
1080
|
-
end
|
1081
|
-
|
1082
|
-
# Returns any connections in use by the current thread back to the pool,
|
1083
|
-
# and also returns connections to the pool cached by threads that are no
|
1084
|
-
# longer alive.
|
1085
|
-
def clear_active_connections!(role = ActiveRecord::Base.current_role)
|
1086
|
-
connection_pool_list(role).each(&:release_connection)
|
1087
|
-
end
|
1088
|
-
|
1089
|
-
# Clears the cache which maps classes.
|
1090
|
-
#
|
1091
|
-
# See ConnectionPool#clear_reloadable_connections! for details.
|
1092
|
-
def clear_reloadable_connections!(role = ActiveRecord::Base.current_role)
|
1093
|
-
connection_pool_list(role).each(&:clear_reloadable_connections!)
|
1094
|
-
end
|
1095
|
-
|
1096
|
-
def clear_all_connections!(role = ActiveRecord::Base.current_role)
|
1097
|
-
connection_pool_list(role).each(&:disconnect!)
|
1098
|
-
end
|
1099
|
-
|
1100
|
-
# Disconnects all currently idle connections.
|
1101
|
-
#
|
1102
|
-
# See ConnectionPool#flush! for details.
|
1103
|
-
def flush_idle_connections!(role = ActiveRecord::Base.current_role)
|
1104
|
-
connection_pool_list(role).each(&:flush!)
|
1105
|
-
end
|
1106
|
-
|
1107
|
-
# Locate the connection of the nearest super class. This can be an
|
1108
|
-
# active or defined connection: if it is the latter, it will be
|
1109
|
-
# opened and set as the active connection for the class it was defined
|
1110
|
-
# for (not necessarily the current class).
|
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
|
-
|
1128
|
-
pool.connection
|
1129
|
-
end
|
1130
|
-
|
1131
|
-
# Returns true if a connection that's accessible to this class has
|
1132
|
-
# already been opened.
|
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?
|
1136
|
-
end
|
1137
|
-
|
1138
|
-
# Remove the connection for this class. This will close the active
|
1139
|
-
# connection and the defined connection (if they exist). The result
|
1140
|
-
# can be used as an argument for #establish_connection, for easily
|
1141
|
-
# re-establishing the connection.
|
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
|
1155
|
-
end
|
1156
|
-
end
|
1157
|
-
|
1158
|
-
# Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool_manager.
|
1159
|
-
# This makes retrieving the connection pool O(1) once the process is warm.
|
1160
|
-
# When a connection is established or removed, we invalidate the cache.
|
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
|
1164
|
-
end
|
1165
|
-
|
1166
|
-
private
|
1167
|
-
attr_reader :owner_to_pool_manager
|
1168
|
-
|
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 7.0.0. Please use `ActiveRecord::Base`.")
|
1181
|
-
owner_to_pool_manager[Base.name]
|
1182
|
-
end
|
1183
|
-
end
|
1184
|
-
|
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)
|
1226
|
-
end
|
1227
|
-
end
|
1228
943
|
end
|
1229
944
|
end
|