activerecord 6.1.7 → 7.2.2
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 +616 -1290
- 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 +19 -8
- 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 +30 -27
- data/lib/active_record/associations/join_dependency.rb +28 -20
- 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 +429 -522
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -5
- 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 +15 -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 +57 -54
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +19 -35
- 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 +325 -604
- 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 +230 -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 +378 -143
- 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 +348 -165
- 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 +403 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
- 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 +310 -253
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
- 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 +58 -0
- data/lib/active_record/enum.rb +170 -62
- 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 +59 -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 +145 -158
- data/lib/active_record/nested_attributes.rb +61 -23
- 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 +18 -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 +229 -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 +332 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +200 -65
- data/lib/active_record/relation/calculations.rb +301 -112
- 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 +870 -163
- 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 +6 -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 +288 -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 +65 -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/sqlite.rb +25 -0
- 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 +103 -17
- 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:
|
26
24
|
|
27
|
-
def
|
28
|
-
|
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
|
48
|
+
|
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,105 @@ 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
|
-
|
96
|
-
def initialize
|
97
|
-
@
|
98
|
-
@cond = @lock.new_cond
|
99
|
-
@num_waiting = 0
|
100
|
-
@queue = []
|
121
|
+
class WeakThreadKeyMap # :nodoc:
|
122
|
+
# FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
|
123
|
+
# but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
|
124
|
+
def initialize
|
125
|
+
@map = {}
|
101
126
|
end
|
102
127
|
|
103
|
-
|
104
|
-
|
105
|
-
synchronize do
|
106
|
-
@num_waiting > 0
|
107
|
-
end
|
128
|
+
def clear
|
129
|
+
@map.clear
|
108
130
|
end
|
109
131
|
|
110
|
-
|
111
|
-
|
112
|
-
def num_waiting
|
113
|
-
synchronize do
|
114
|
-
@num_waiting
|
115
|
-
end
|
132
|
+
def [](key)
|
133
|
+
@map[key]
|
116
134
|
end
|
117
135
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
@queue.push element
|
122
|
-
@cond.signal
|
123
|
-
end
|
136
|
+
def []=(key, value)
|
137
|
+
@map.select! { |c, _| c.alive? }
|
138
|
+
@map[key] = value
|
124
139
|
end
|
140
|
+
end
|
125
141
|
|
126
|
-
|
127
|
-
|
128
|
-
synchronize do
|
129
|
-
@queue.delete(element)
|
130
|
-
end
|
131
|
-
end
|
142
|
+
class Lease # :nodoc:
|
143
|
+
attr_accessor :connection, :sticky
|
132
144
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
@queue.clear
|
137
|
-
end
|
145
|
+
def initialize
|
146
|
+
@connection = nil
|
147
|
+
@sticky = nil
|
138
148
|
end
|
139
149
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
# is, don't jump ahead in line). Otherwise, return +nil+.
|
146
|
-
#
|
147
|
-
# If +timeout+ is given, block if there is no element
|
148
|
-
# available, waiting up to +timeout+ seconds for an element to
|
149
|
-
# become available.
|
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) }
|
150
|
+
def release
|
151
|
+
conn = @connection
|
152
|
+
@connection = nil
|
153
|
+
@sticky = nil
|
154
|
+
conn
|
156
155
|
end
|
157
156
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
191
|
-
|
192
|
-
# Waits on the queue up to +timeout+ seconds, then removes and
|
193
|
-
# returns the head of the queue.
|
194
|
-
def wait_poll(timeout)
|
195
|
-
@num_waiting += 1
|
196
|
-
|
197
|
-
t0 = Concurrent.monotonic_time
|
198
|
-
elapsed = 0
|
199
|
-
loop do
|
200
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
201
|
-
@cond.wait(timeout - elapsed)
|
202
|
-
end
|
203
|
-
|
204
|
-
return remove if any?
|
205
|
-
|
206
|
-
elapsed = Concurrent.monotonic_time - t0
|
207
|
-
if elapsed >= timeout
|
208
|
-
msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
|
209
|
-
[timeout, elapsed]
|
210
|
-
raise ConnectionTimeoutError, msg
|
211
|
-
end
|
212
|
-
end
|
213
|
-
ensure
|
214
|
-
@num_waiting -= 1
|
157
|
+
def clear(connection)
|
158
|
+
if @connection == connection
|
159
|
+
@connection = nil
|
160
|
+
@sticky = nil
|
161
|
+
true
|
162
|
+
else
|
163
|
+
false
|
215
164
|
end
|
165
|
+
end
|
216
166
|
end
|
217
167
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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
|
-
|
236
|
-
def broadcast_on_biased
|
237
|
-
@num_waiting_on_real_cond = 0
|
238
|
-
@real_cond.broadcast
|
239
|
-
end
|
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)
|
257
|
-
end
|
168
|
+
class LeaseRegistry # :nodoc:
|
169
|
+
def initialize
|
170
|
+
@mutex = Mutex.new
|
171
|
+
@map = WeakThreadKeyMap.new
|
258
172
|
end
|
259
173
|
|
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
|
174
|
+
def [](context)
|
175
|
+
@mutex.synchronize do
|
176
|
+
@map[context] ||= Lease.new
|
272
177
|
end
|
273
178
|
end
|
274
|
-
end
|
275
179
|
|
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
|
180
|
+
def clear
|
181
|
+
@mutex.synchronize do
|
182
|
+
@map.clear
|
289
183
|
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
184
|
end
|
185
|
+
end
|
305
186
|
|
306
|
-
|
307
|
-
@pools = {}
|
308
|
-
@threads = {}
|
309
|
-
|
187
|
+
module ExecutorHooks # :nodoc:
|
310
188
|
class << self
|
311
|
-
def
|
312
|
-
|
313
|
-
unless @threads[frequency]&.alive?
|
314
|
-
@threads[frequency] = spawn_thread(frequency)
|
315
|
-
end
|
316
|
-
@pools[frequency] ||= []
|
317
|
-
@pools[frequency] << WeakRef.new(pool)
|
318
|
-
end
|
189
|
+
def run
|
190
|
+
# noop
|
319
191
|
end
|
320
192
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
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
|
193
|
+
def complete(_)
|
194
|
+
ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
|
195
|
+
if (connection = pool.active_connection?)
|
196
|
+
transaction = connection.current_transaction
|
197
|
+
if transaction.closed? || !transaction.joinable?
|
198
|
+
pool.release_connection
|
347
199
|
end
|
348
200
|
end
|
349
201
|
end
|
202
|
+
end
|
350
203
|
end
|
204
|
+
end
|
351
205
|
|
352
|
-
|
353
|
-
|
354
|
-
|
206
|
+
class << self
|
207
|
+
def install_executor_hooks(executor = ActiveSupport::Executor)
|
208
|
+
executor.register_hook(ExecutorHooks)
|
355
209
|
end
|
356
210
|
end
|
357
211
|
|
358
212
|
include MonitorMixin
|
359
|
-
|
213
|
+
prepend QueryCache::ConnectionPoolConfiguration
|
360
214
|
include ConnectionAdapters::AbstractPool
|
361
215
|
|
362
216
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
363
|
-
attr_reader :db_config, :size, :reaper, :pool_config, :
|
217
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
|
364
218
|
|
365
|
-
delegate :
|
219
|
+
delegate :schema_reflection, :server_version, to: :pool_config
|
366
220
|
|
367
221
|
# Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
|
368
222
|
# object which describes database connection information (e.g. adapter,
|
@@ -375,7 +229,8 @@ module ActiveRecord
|
|
375
229
|
|
376
230
|
@pool_config = pool_config
|
377
231
|
@db_config = pool_config.db_config
|
378
|
-
@
|
232
|
+
@role = pool_config.role
|
233
|
+
@shard = pool_config.shard
|
379
234
|
|
380
235
|
@checkout_timeout = db_config.checkout_timeout
|
381
236
|
@idle_timeout = db_config.idle_timeout
|
@@ -389,9 +244,9 @@ module ActiveRecord
|
|
389
244
|
# then that +thread+ does indeed own that +conn+. However, an absence of such
|
390
245
|
# mapping does not mean that the +thread+ doesn't own the said connection. In
|
391
246
|
# that case +conn.owner+ attr should be consulted.
|
392
|
-
# Access and modification of <tt>@
|
247
|
+
# Access and modification of <tt>@leases</tt> does not require
|
393
248
|
# synchronization.
|
394
|
-
@
|
249
|
+
@leases = LeaseRegistry.new
|
395
250
|
|
396
251
|
@connections = []
|
397
252
|
@automatic_reconnect = true
|
@@ -404,69 +259,176 @@ module ActiveRecord
|
|
404
259
|
@threads_blocking_new_connections = 0
|
405
260
|
|
406
261
|
@available = ConnectionLeasingQueue.new self
|
262
|
+
@pinned_connection = nil
|
263
|
+
@pinned_connections_depth = 0
|
264
|
+
|
265
|
+
@async_executor = build_async_executor
|
407
266
|
|
408
|
-
@
|
267
|
+
@schema_cache = nil
|
409
268
|
|
410
269
|
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
411
270
|
@reaper.run
|
412
271
|
end
|
413
272
|
|
414
|
-
def
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
273
|
+
def inspect # :nodoc:
|
274
|
+
name_field = " name=#{db_config.name.inspect}" unless db_config.name == "primary"
|
275
|
+
shard_field = " shard=#{@shard.inspect}" unless @shard == :default
|
276
|
+
|
277
|
+
"#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
278
|
+
end
|
279
|
+
|
280
|
+
def schema_cache
|
281
|
+
@schema_cache ||= BoundSchemaReflection.new(schema_reflection, self)
|
282
|
+
end
|
283
|
+
|
284
|
+
def schema_reflection=(schema_reflection)
|
285
|
+
pool_config.schema_reflection = schema_reflection
|
286
|
+
@schema_cache = nil
|
287
|
+
end
|
288
|
+
|
289
|
+
def migration_context # :nodoc:
|
290
|
+
MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
|
291
|
+
end
|
292
|
+
|
293
|
+
def migrations_paths # :nodoc:
|
294
|
+
db_config.migrations_paths || Migrator.migrations_paths
|
295
|
+
end
|
296
|
+
|
297
|
+
def schema_migration # :nodoc:
|
298
|
+
SchemaMigration.new(self)
|
299
|
+
end
|
300
|
+
|
301
|
+
def internal_metadata # :nodoc:
|
302
|
+
InternalMetadata.new(self)
|
420
303
|
end
|
421
304
|
|
422
305
|
# Retrieve the connection associated with the current thread, or call
|
423
306
|
# #checkout to obtain one if necessary.
|
424
307
|
#
|
425
|
-
# #
|
308
|
+
# #lease_connection can be called any number of times; the connection is
|
426
309
|
# held in a cache keyed by a thread.
|
310
|
+
def lease_connection
|
311
|
+
lease = connection_lease
|
312
|
+
lease.sticky = true
|
313
|
+
lease.connection ||= checkout
|
314
|
+
end
|
315
|
+
|
316
|
+
def permanent_lease? # :nodoc:
|
317
|
+
connection_lease.sticky.nil?
|
318
|
+
end
|
319
|
+
|
427
320
|
def connection
|
428
|
-
|
321
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
322
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
|
323
|
+
and will be removed in Rails 8.0. Use #lease_connection instead.
|
324
|
+
MSG
|
325
|
+
lease_connection
|
326
|
+
end
|
327
|
+
|
328
|
+
def pin_connection!(lock_thread) # :nodoc:
|
329
|
+
@pinned_connection ||= (connection_lease&.connection || checkout)
|
330
|
+
@pinned_connections_depth += 1
|
331
|
+
|
332
|
+
# Any leased connection must be in @connections otherwise
|
333
|
+
# some methods like #connected? won't behave correctly
|
334
|
+
unless @connections.include?(@pinned_connection)
|
335
|
+
@connections << @pinned_connection
|
336
|
+
end
|
337
|
+
|
338
|
+
@pinned_connection.lock_thread = ActiveSupport::IsolatedExecutionState.context if lock_thread
|
339
|
+
@pinned_connection.verify! # eagerly validate the connection
|
340
|
+
@pinned_connection.begin_transaction joinable: false, _lazy: false
|
341
|
+
end
|
342
|
+
|
343
|
+
def unpin_connection! # :nodoc:
|
344
|
+
raise "There isn't a pinned connection #{object_id}" unless @pinned_connection
|
345
|
+
|
346
|
+
clean = true
|
347
|
+
@pinned_connection.lock.synchronize do
|
348
|
+
@pinned_connections_depth -= 1
|
349
|
+
connection = @pinned_connection
|
350
|
+
@pinned_connection = nil if @pinned_connections_depth.zero?
|
351
|
+
|
352
|
+
if connection.transaction_open?
|
353
|
+
connection.rollback_transaction
|
354
|
+
else
|
355
|
+
# Something committed or rolled back the transaction
|
356
|
+
clean = false
|
357
|
+
connection.reset!
|
358
|
+
end
|
359
|
+
|
360
|
+
if @pinned_connection.nil?
|
361
|
+
connection.steal!
|
362
|
+
connection.lock_thread = nil
|
363
|
+
checkin(connection)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
clean
|
368
|
+
end
|
369
|
+
|
370
|
+
def connection_class # :nodoc:
|
371
|
+
pool_config.connection_class
|
429
372
|
end
|
430
373
|
|
431
374
|
# Returns true if there is an open connection being used for the current thread.
|
432
375
|
#
|
433
376
|
# This method only works for connections that have been obtained through
|
434
|
-
# #
|
377
|
+
# #lease_connection or #with_connection methods. Connections obtained through
|
435
378
|
# #checkout will not be detected by #active_connection?
|
436
379
|
def active_connection?
|
437
|
-
|
380
|
+
connection_lease.connection
|
438
381
|
end
|
382
|
+
alias_method :active_connection, :active_connection? # :nodoc:
|
439
383
|
|
440
384
|
# Signal that the thread is finished with the current connection.
|
441
385
|
# #release_connection releases the connection-thread association
|
442
386
|
# and returns the connection to the pool.
|
443
387
|
#
|
444
388
|
# This method only works for connections that have been obtained through
|
445
|
-
# #
|
389
|
+
# #lease_connection or #with_connection methods, connections obtained through
|
446
390
|
# #checkout will not be automatically released.
|
447
|
-
def release_connection(
|
448
|
-
if conn =
|
391
|
+
def release_connection(existing_lease = nil)
|
392
|
+
if conn = connection_lease.release
|
449
393
|
checkin conn
|
394
|
+
return true
|
395
|
+
end
|
396
|
+
false
|
397
|
+
end
|
398
|
+
|
399
|
+
# Yields a connection from the connection pool to the block. If no connection
|
400
|
+
# is already checked out by the current thread, a connection will be checked
|
401
|
+
# out from the pool, yielded to the block, and then returned to the pool when
|
402
|
+
# the block is finished. If a connection has already been checked out on the
|
403
|
+
# current thread, such as via #lease_connection or #with_connection, that existing
|
404
|
+
# connection will be the one yielded and it will not be returned to the pool
|
405
|
+
# automatically at the end of the block; it is expected that such an existing
|
406
|
+
# connection will be properly returned to the pool by the code that checked
|
407
|
+
# it out.
|
408
|
+
def with_connection(prevent_permanent_checkout: false)
|
409
|
+
lease = connection_lease
|
410
|
+
sticky_was = lease.sticky
|
411
|
+
lease.sticky = false if prevent_permanent_checkout
|
412
|
+
|
413
|
+
if lease.connection
|
414
|
+
begin
|
415
|
+
yield lease.connection
|
416
|
+
ensure
|
417
|
+
lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
|
418
|
+
end
|
419
|
+
else
|
420
|
+
begin
|
421
|
+
yield lease.connection = checkout
|
422
|
+
ensure
|
423
|
+
lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
|
424
|
+
release_connection(lease) unless lease.sticky
|
425
|
+
end
|
450
426
|
end
|
451
427
|
end
|
452
428
|
|
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
429
|
# Returns true if a connection has already been opened.
|
468
430
|
def connected?
|
469
|
-
synchronize { @connections.any? }
|
431
|
+
synchronize { @connections.any?(&:connected?) }
|
470
432
|
end
|
471
433
|
|
472
434
|
# Returns an array containing the connections currently in the pool.
|
@@ -501,6 +463,7 @@ module ActiveRecord
|
|
501
463
|
conn.disconnect!
|
502
464
|
end
|
503
465
|
@connections = []
|
466
|
+
@leases.clear
|
504
467
|
@available.clear
|
505
468
|
end
|
506
469
|
end
|
@@ -527,7 +490,7 @@ module ActiveRecord
|
|
527
490
|
@connections.each do |conn|
|
528
491
|
conn.discard!
|
529
492
|
end
|
530
|
-
@connections = @available = @
|
493
|
+
@connections = @available = @leases = nil
|
531
494
|
end
|
532
495
|
end
|
533
496
|
|
@@ -585,7 +548,21 @@ module ActiveRecord
|
|
585
548
|
# Raises:
|
586
549
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
587
550
|
def checkout(checkout_timeout = @checkout_timeout)
|
588
|
-
|
551
|
+
if @pinned_connection
|
552
|
+
@pinned_connection.lock.synchronize do
|
553
|
+
synchronize do
|
554
|
+
@pinned_connection.verify!
|
555
|
+
# Any leased connection must be in @connections otherwise
|
556
|
+
# some methods like #connected? won't behave correctly
|
557
|
+
unless @connections.include?(@pinned_connection)
|
558
|
+
@connections << @pinned_connection
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
@pinned_connection
|
563
|
+
else
|
564
|
+
checkout_and_verify(acquire_connection(checkout_timeout))
|
565
|
+
end
|
589
566
|
end
|
590
567
|
|
591
568
|
# Check-in a database connection back into the pool, indicating that you
|
@@ -594,9 +571,11 @@ module ActiveRecord
|
|
594
571
|
# +conn+: an AbstractAdapter object, which was obtained by earlier by
|
595
572
|
# calling #checkout on this pool.
|
596
573
|
def checkin(conn)
|
574
|
+
return if @pinned_connection.equal?(conn)
|
575
|
+
|
597
576
|
conn.lock.synchronize do
|
598
577
|
synchronize do
|
599
|
-
|
578
|
+
connection_lease.clear(conn)
|
600
579
|
|
601
580
|
conn._run_checkin_callbacks do
|
602
581
|
conn.expire
|
@@ -695,8 +674,7 @@ module ActiveRecord
|
|
695
674
|
@available.num_waiting
|
696
675
|
end
|
697
676
|
|
698
|
-
#
|
699
|
-
# Example:
|
677
|
+
# Returns the connection pool's usage statistic.
|
700
678
|
#
|
701
679
|
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
702
680
|
def stat
|
@@ -713,7 +691,32 @@ module ActiveRecord
|
|
713
691
|
end
|
714
692
|
end
|
715
693
|
|
694
|
+
def schedule_query(future_result) # :nodoc:
|
695
|
+
@async_executor.post { future_result.execute_or_skip }
|
696
|
+
Thread.pass
|
697
|
+
end
|
698
|
+
|
716
699
|
private
|
700
|
+
def connection_lease
|
701
|
+
@leases[ActiveSupport::IsolatedExecutionState.context]
|
702
|
+
end
|
703
|
+
|
704
|
+
def build_async_executor
|
705
|
+
case ActiveRecord.async_query_executor
|
706
|
+
when :multi_thread_pool
|
707
|
+
if @db_config.max_threads > 0
|
708
|
+
Concurrent::ThreadPoolExecutor.new(
|
709
|
+
min_threads: @db_config.min_threads,
|
710
|
+
max_threads: @db_config.max_threads,
|
711
|
+
max_queue: @db_config.max_queue,
|
712
|
+
fallback_policy: :caller_runs
|
713
|
+
)
|
714
|
+
end
|
715
|
+
when :global_thread_pool
|
716
|
+
ActiveRecord.global_thread_pool_async_query_executor
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
717
720
|
#--
|
718
721
|
# this is unfortunately not concurrent
|
719
722
|
def bulk_make_new_connections(num_new_conns_needed)
|
@@ -726,19 +729,6 @@ module ActiveRecord
|
|
726
729
|
end
|
727
730
|
end
|
728
731
|
|
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
732
|
# Take control of all existing connections so a "group" action such as
|
743
733
|
# reload/disconnect can be performed safely. It is no longer enough to
|
744
734
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
@@ -752,18 +742,21 @@ module ActiveRecord
|
|
752
742
|
|
753
743
|
def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
|
754
744
|
collected_conns = synchronize do
|
745
|
+
reap # No need to wait for dead owners
|
746
|
+
|
755
747
|
# account for our own connections
|
756
|
-
@connections.select { |conn| conn.owner ==
|
748
|
+
@connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
|
757
749
|
end
|
758
750
|
|
759
751
|
newly_checked_out = []
|
760
|
-
timeout_time =
|
752
|
+
timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
|
761
753
|
|
762
|
-
@available.with_a_bias_for(
|
754
|
+
@available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
|
763
755
|
loop do
|
764
756
|
synchronize do
|
765
757
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
766
|
-
|
758
|
+
|
759
|
+
remaining_timeout = timeout_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
767
760
|
remaining_timeout = 0 if remaining_timeout < 0
|
768
761
|
conn = checkout_for_exclusive_access(remaining_timeout)
|
769
762
|
collected_conns << conn
|
@@ -806,14 +799,14 @@ module ActiveRecord
|
|
806
799
|
|
807
800
|
thread_report = []
|
808
801
|
@connections.each do |conn|
|
809
|
-
unless conn.owner ==
|
802
|
+
unless conn.owner == ActiveSupport::IsolatedExecutionState.context
|
810
803
|
thread_report << "#{conn} is owned by #{conn.owner}"
|
811
804
|
end
|
812
805
|
end
|
813
806
|
|
814
807
|
msg << " (#{thread_report.join(', ')})" if thread_report.any?
|
815
808
|
|
816
|
-
raise ExclusiveConnectionTimeoutError,
|
809
|
+
raise ExclusiveConnectionTimeoutError.new(msg, connection_pool: self)
|
817
810
|
end
|
818
811
|
|
819
812
|
def with_new_connections_blocked
|
@@ -867,21 +860,31 @@ module ActiveRecord
|
|
867
860
|
conn
|
868
861
|
else
|
869
862
|
reap
|
870
|
-
|
863
|
+
# Retry after reaping, which may return an available connection,
|
864
|
+
# remove an inactive connection, or both
|
865
|
+
if conn = @available.poll || try_to_checkout_new_connection
|
866
|
+
conn
|
867
|
+
else
|
868
|
+
@available.poll(checkout_timeout)
|
869
|
+
end
|
871
870
|
end
|
871
|
+
rescue ConnectionTimeoutError => ex
|
872
|
+
raise ex.set_pool(self)
|
872
873
|
end
|
873
874
|
|
874
875
|
#--
|
875
876
|
# if owner_thread param is omitted, this must be called in synchronize block
|
876
877
|
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
877
|
-
@
|
878
|
+
@leases[owner_thread].clear(conn)
|
878
879
|
end
|
879
880
|
alias_method :release, :remove_connection_from_thread_cache
|
880
881
|
|
881
882
|
def new_connection
|
882
|
-
|
883
|
-
|
884
|
-
|
883
|
+
connection = db_config.new_connection
|
884
|
+
connection.pool = self
|
885
|
+
connection
|
886
|
+
rescue ConnectionNotEstablished => ex
|
887
|
+
raise ex.set_pool(self)
|
885
888
|
end
|
886
889
|
|
887
890
|
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
@@ -919,6 +922,12 @@ module ActiveRecord
|
|
919
922
|
def adopt_connection(conn)
|
920
923
|
conn.pool = self
|
921
924
|
@connections << conn
|
925
|
+
|
926
|
+
# We just created the first connection, it's time to load the schema
|
927
|
+
# cache if that wasn't eagerly done before
|
928
|
+
if @schema_cache.nil? && ActiveRecord.lazily_load_schema_cache
|
929
|
+
schema_cache.load!
|
930
|
+
end
|
922
931
|
end
|
923
932
|
|
924
933
|
def checkout_new_connection
|
@@ -928,302 +937,14 @@ module ActiveRecord
|
|
928
937
|
|
929
938
|
def checkout_and_verify(c)
|
930
939
|
c._run_checkout_callbacks do
|
931
|
-
c.
|
940
|
+
c.clean!
|
932
941
|
end
|
933
942
|
c
|
934
|
-
rescue
|
943
|
+
rescue Exception
|
935
944
|
remove c
|
936
945
|
c.disconnect!
|
937
946
|
raise
|
938
947
|
end
|
939
948
|
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
949
|
end
|
1229
950
|
end
|