activerecord 7.0.0 → 7.2.1
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 +515 -1268
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +28 -17
- data/lib/active_record/associations/collection_proxy.rb +36 -13
- 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 +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +18 -14
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +2 -4
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +378 -491
- data/lib/active_record/attribute_assignment.rb +1 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +153 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -40
- data/lib/active_record/attributes.rb +63 -48
- data/lib/active_record/autosave_association.rb +70 -38
- data/lib/active_record/base.rb +12 -8
- data/lib/active_record/callbacks.rb +16 -32
- 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 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +124 -132
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +297 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +215 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +83 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +319 -135
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +512 -126
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +282 -119
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +27 -140
- data/lib/active_record/connection_adapters/mysql/quoting.rb +64 -52
- 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 +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +101 -48
- 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/hstore.rb +2 -2
- 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 +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +94 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +379 -66
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +370 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +61 -46
- 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 +64 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +321 -110
- 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 +124 -1
- data/lib/active_record/connection_handling.rb +98 -106
- data/lib/active_record/core.rb +220 -177
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -2
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +88 -35
- data/lib/active_record/delegated_type.rb +40 -11
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +47 -25
- data/lib/active_record/encryption/encrypted_attribute_type.rb +49 -14
- data/lib/active_record/encryption/encryptor.rb +25 -10
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +23 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +131 -27
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_subscriber.rb +1 -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 +29 -8
- data/lib/active_record/fixtures.rb +169 -99
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +13 -10
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +39 -24
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +28 -27
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +110 -13
- data/lib/active_record/migration/compatibility.rb +174 -64
- 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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +292 -125
- data/lib/active_record/model_schema.rb +113 -112
- data/lib/active_record/nested_attributes.rb +35 -9
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +177 -345
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +102 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +34 -9
- data/lib/active_record/railtie.rb +153 -100
- data/lib/active_record/railties/controller_runtime.rb +24 -10
- data/lib/active_record/railties/databases.rake +148 -152
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +278 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +293 -108
- data/lib/active_record/relation/delegation.rb +31 -20
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- 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 +28 -16
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +625 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +602 -96
- data/lib/active_record/result.rb +55 -52
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +76 -30
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +82 -30
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +29 -8
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +191 -121
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +174 -152
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- 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 +109 -27
- data/lib/active_record/translation.rb +1 -3
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- 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 +12 -6
- 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 +63 -14
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +266 -30
- data/lib/arel/alias_predication.rb +1 -1
- 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/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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
- metadata +59 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -10,35 +10,49 @@ require "active_record/connection_adapters/abstract/connection_pool/reaper"
|
|
|
10
10
|
module ActiveRecord
|
|
11
11
|
module ConnectionAdapters
|
|
12
12
|
module AbstractPool # :nodoc:
|
|
13
|
-
|
|
14
|
-
self.schema_cache ||= SchemaCache.new(connection)
|
|
15
|
-
schema_cache.connection = connection
|
|
16
|
-
schema_cache
|
|
17
|
-
end
|
|
13
|
+
end
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
end
|
|
15
|
+
class NullPool # :nodoc:
|
|
16
|
+
include ConnectionAdapters::AbstractPool
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
class NullConfig # :nodoc:
|
|
19
|
+
def method_missing(...)
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
NULL_CONFIG = NullConfig.new # :nodoc:
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
def initialize
|
|
26
|
+
super()
|
|
27
|
+
@mutex = Mutex.new
|
|
28
|
+
@server_version = nil
|
|
28
29
|
end
|
|
29
|
-
end
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
def server_version(connection) # :nodoc:
|
|
32
|
+
@server_version || @mutex.synchronize { @server_version ||= connection.get_database_version }
|
|
33
|
+
end
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
def schema_reflection
|
|
36
|
+
SchemaReflection.new(nil)
|
|
37
|
+
end
|
|
35
38
|
|
|
39
|
+
def schema_cache; end
|
|
36
40
|
def connection_class; end
|
|
37
41
|
def checkin(_); end
|
|
38
42
|
def remove(_); end
|
|
39
43
|
def async_executor; end
|
|
44
|
+
|
|
45
|
+
def db_config
|
|
46
|
+
NULL_CONFIG
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def dirties_query_cache
|
|
50
|
+
true
|
|
51
|
+
end
|
|
40
52
|
end
|
|
41
53
|
|
|
54
|
+
# = Active Record Connection Pool
|
|
55
|
+
#
|
|
42
56
|
# Connection pool base class for managing Active Record database
|
|
43
57
|
# connections.
|
|
44
58
|
#
|
|
@@ -53,19 +67,17 @@ module ActiveRecord
|
|
|
53
67
|
# handle cases in which there are more threads than connections: if all
|
|
54
68
|
# connections have been checked out, and a thread tries to checkout a
|
|
55
69
|
# connection anyway, then ConnectionPool will wait until some other thread
|
|
56
|
-
# has checked in a connection.
|
|
70
|
+
# has checked in a connection, or the +checkout_timeout+ has expired.
|
|
57
71
|
#
|
|
58
72
|
# == Obtaining (checking out) a connection
|
|
59
73
|
#
|
|
60
74
|
# Connections can be obtained and used from a connection pool in several
|
|
61
75
|
# ways:
|
|
62
76
|
#
|
|
63
|
-
# 1. Simply use {ActiveRecord::Base.
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
# {ActiveRecord::Base.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
|
|
68
|
-
# This will be the default behavior for Active Record when used in conjunction with
|
|
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
|
|
69
81
|
# Action Pack's request handling cycle.
|
|
70
82
|
# 2. Manually check out a connection from the pool with
|
|
71
83
|
# {ActiveRecord::Base.connection_pool.checkout}[rdoc-ref:#checkout]. You are responsible for
|
|
@@ -78,6 +90,12 @@ module ActiveRecord
|
|
|
78
90
|
# Connections in the pool are actually AbstractAdapter objects (or objects
|
|
79
91
|
# compatible with AbstractAdapter's interface).
|
|
80
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
|
+
#
|
|
81
99
|
# == Options
|
|
82
100
|
#
|
|
83
101
|
# There are several connection-pooling-related options that you can add to
|
|
@@ -100,16 +118,80 @@ module ActiveRecord
|
|
|
100
118
|
# * private methods that require being called in a +synchronize+ blocks
|
|
101
119
|
# are now explicitly documented
|
|
102
120
|
class ConnectionPool
|
|
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 = {}
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def clear
|
|
129
|
+
@map.clear
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def [](key)
|
|
133
|
+
@map[key]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def []=(key, value)
|
|
137
|
+
@map.select! { |c, _| c.alive? }
|
|
138
|
+
@map[key] = value
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
class Lease # :nodoc:
|
|
143
|
+
attr_accessor :connection, :sticky
|
|
144
|
+
|
|
145
|
+
def initialize
|
|
146
|
+
@connection = nil
|
|
147
|
+
@sticky = nil
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def release
|
|
151
|
+
conn = @connection
|
|
152
|
+
@connection = nil
|
|
153
|
+
@sticky = nil
|
|
154
|
+
conn
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def clear(connection)
|
|
158
|
+
if @connection == connection
|
|
159
|
+
@connection = nil
|
|
160
|
+
@sticky = nil
|
|
161
|
+
true
|
|
162
|
+
else
|
|
163
|
+
false
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
class LeaseRegistry # :nodoc:
|
|
169
|
+
def initialize
|
|
170
|
+
@mutex = Mutex.new
|
|
171
|
+
@map = WeakThreadKeyMap.new
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def [](context)
|
|
175
|
+
@mutex.synchronize do
|
|
176
|
+
@map[context] ||= Lease.new
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def clear
|
|
181
|
+
@mutex.synchronize do
|
|
182
|
+
@map.clear
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
103
187
|
include MonitorMixin
|
|
104
|
-
|
|
188
|
+
prepend QueryCache::ConnectionPoolConfiguration
|
|
105
189
|
include ConnectionAdapters::AbstractPool
|
|
106
190
|
|
|
107
191
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
|
108
|
-
attr_reader :db_config, :size, :reaper, :pool_config, :
|
|
192
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
|
|
109
193
|
|
|
110
|
-
|
|
111
|
-
deprecate :connection_klass
|
|
112
|
-
delegate :schema_cache, :schema_cache=, to: :pool_config
|
|
194
|
+
delegate :schema_reflection, :server_version, to: :pool_config
|
|
113
195
|
|
|
114
196
|
# Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
|
|
115
197
|
# object which describes database connection information (e.g. adapter,
|
|
@@ -122,7 +204,6 @@ module ActiveRecord
|
|
|
122
204
|
|
|
123
205
|
@pool_config = pool_config
|
|
124
206
|
@db_config = pool_config.db_config
|
|
125
|
-
@connection_class = pool_config.connection_class
|
|
126
207
|
@role = pool_config.role
|
|
127
208
|
@shard = pool_config.shard
|
|
128
209
|
|
|
@@ -138,9 +219,9 @@ module ActiveRecord
|
|
|
138
219
|
# then that +thread+ does indeed own that +conn+. However, an absence of such
|
|
139
220
|
# mapping does not mean that the +thread+ doesn't own the said connection. In
|
|
140
221
|
# that case +conn.owner+ attr should be consulted.
|
|
141
|
-
# Access and modification of <tt>@
|
|
222
|
+
# Access and modification of <tt>@leases</tt> does not require
|
|
142
223
|
# synchronization.
|
|
143
|
-
@
|
|
224
|
+
@leases = LeaseRegistry.new
|
|
144
225
|
|
|
145
226
|
@connections = []
|
|
146
227
|
@automatic_reconnect = true
|
|
@@ -153,73 +234,175 @@ module ActiveRecord
|
|
|
153
234
|
@threads_blocking_new_connections = 0
|
|
154
235
|
|
|
155
236
|
@available = ConnectionLeasingQueue.new self
|
|
156
|
-
|
|
157
|
-
@
|
|
237
|
+
@pinned_connection = nil
|
|
238
|
+
@pinned_connections_depth = 0
|
|
158
239
|
|
|
159
240
|
@async_executor = build_async_executor
|
|
160
241
|
|
|
161
|
-
|
|
242
|
+
@schema_cache = nil
|
|
162
243
|
|
|
163
244
|
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
|
164
245
|
@reaper.run
|
|
165
246
|
end
|
|
166
247
|
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
248
|
+
def inspect # :nodoc:
|
|
249
|
+
name_field = " name=#{db_config.name.inspect}" unless db_config.name == "primary"
|
|
250
|
+
shard_field = " shard=#{@shard.inspect}" unless @shard == :default
|
|
251
|
+
|
|
252
|
+
"#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def schema_cache
|
|
256
|
+
@schema_cache ||= BoundSchemaReflection.new(schema_reflection, self)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def schema_reflection=(schema_reflection)
|
|
260
|
+
pool_config.schema_reflection = schema_reflection
|
|
261
|
+
@schema_cache = nil
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def migration_context # :nodoc:
|
|
265
|
+
MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def migrations_paths # :nodoc:
|
|
269
|
+
db_config.migrations_paths || Migrator.migrations_paths
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def schema_migration # :nodoc:
|
|
273
|
+
SchemaMigration.new(self)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def internal_metadata # :nodoc:
|
|
277
|
+
InternalMetadata.new(self)
|
|
173
278
|
end
|
|
174
279
|
|
|
175
280
|
# Retrieve the connection associated with the current thread, or call
|
|
176
281
|
# #checkout to obtain one if necessary.
|
|
177
282
|
#
|
|
178
|
-
# #
|
|
283
|
+
# #lease_connection can be called any number of times; the connection is
|
|
179
284
|
# held in a cache keyed by a thread.
|
|
285
|
+
def lease_connection
|
|
286
|
+
lease = connection_lease
|
|
287
|
+
lease.sticky = true
|
|
288
|
+
lease.connection ||= checkout
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def permanent_lease? # :nodoc:
|
|
292
|
+
connection_lease.sticky.nil?
|
|
293
|
+
end
|
|
294
|
+
|
|
180
295
|
def connection
|
|
181
|
-
|
|
296
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
|
297
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
|
|
298
|
+
and will be removed in Rails 8.0. Use #lease_connection instead.
|
|
299
|
+
MSG
|
|
300
|
+
lease_connection
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def pin_connection!(lock_thread) # :nodoc:
|
|
304
|
+
@pinned_connection ||= (connection_lease&.connection || checkout)
|
|
305
|
+
@pinned_connections_depth += 1
|
|
306
|
+
|
|
307
|
+
# Any leased connection must be in @connections otherwise
|
|
308
|
+
# some methods like #connected? won't behave correctly
|
|
309
|
+
unless @connections.include?(@pinned_connection)
|
|
310
|
+
@connections << @pinned_connection
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
@pinned_connection.lock_thread = ActiveSupport::IsolatedExecutionState.context if lock_thread
|
|
314
|
+
@pinned_connection.verify! # eagerly validate the connection
|
|
315
|
+
@pinned_connection.begin_transaction joinable: false, _lazy: false
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def unpin_connection! # :nodoc:
|
|
319
|
+
raise "There isn't a pinned connection #{object_id}" unless @pinned_connection
|
|
320
|
+
|
|
321
|
+
clean = true
|
|
322
|
+
@pinned_connection.lock.synchronize do
|
|
323
|
+
@pinned_connections_depth -= 1
|
|
324
|
+
connection = @pinned_connection
|
|
325
|
+
@pinned_connection = nil if @pinned_connections_depth.zero?
|
|
326
|
+
|
|
327
|
+
if connection.transaction_open?
|
|
328
|
+
connection.rollback_transaction
|
|
329
|
+
else
|
|
330
|
+
# Something committed or rolled back the transaction
|
|
331
|
+
clean = false
|
|
332
|
+
connection.reset!
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
if @pinned_connection.nil?
|
|
336
|
+
connection.lock_thread = nil
|
|
337
|
+
checkin(connection)
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
clean
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def connection_class # :nodoc:
|
|
345
|
+
pool_config.connection_class
|
|
182
346
|
end
|
|
183
347
|
|
|
184
348
|
# Returns true if there is an open connection being used for the current thread.
|
|
185
349
|
#
|
|
186
350
|
# This method only works for connections that have been obtained through
|
|
187
|
-
# #
|
|
351
|
+
# #lease_connection or #with_connection methods. Connections obtained through
|
|
188
352
|
# #checkout will not be detected by #active_connection?
|
|
189
353
|
def active_connection?
|
|
190
|
-
|
|
354
|
+
connection_lease.connection
|
|
191
355
|
end
|
|
356
|
+
alias_method :active_connection, :active_connection? # :nodoc:
|
|
192
357
|
|
|
193
358
|
# Signal that the thread is finished with the current connection.
|
|
194
359
|
# #release_connection releases the connection-thread association
|
|
195
360
|
# and returns the connection to the pool.
|
|
196
361
|
#
|
|
197
362
|
# This method only works for connections that have been obtained through
|
|
198
|
-
# #
|
|
363
|
+
# #lease_connection or #with_connection methods, connections obtained through
|
|
199
364
|
# #checkout will not be automatically released.
|
|
200
|
-
def release_connection(
|
|
201
|
-
if conn =
|
|
365
|
+
def release_connection(existing_lease = nil)
|
|
366
|
+
if conn = connection_lease.release
|
|
202
367
|
checkin conn
|
|
368
|
+
return true
|
|
203
369
|
end
|
|
370
|
+
false
|
|
204
371
|
end
|
|
205
372
|
|
|
206
|
-
#
|
|
207
|
-
# already
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
373
|
+
# Yields a connection from the connection pool to the block. If no connection
|
|
374
|
+
# is already checked out by the current thread, a connection will be checked
|
|
375
|
+
# out from the pool, yielded to the block, and then returned to the pool when
|
|
376
|
+
# the block is finished. If a connection has already been checked out on the
|
|
377
|
+
# current thread, such as via #lease_connection or #with_connection, that existing
|
|
378
|
+
# connection will be the one yielded and it will not be returned to the pool
|
|
379
|
+
# automatically at the end of the block; it is expected that such an existing
|
|
380
|
+
# connection will be properly returned to the pool by the code that checked
|
|
381
|
+
# it out.
|
|
382
|
+
def with_connection(prevent_permanent_checkout: false)
|
|
383
|
+
lease = connection_lease
|
|
384
|
+
sticky_was = lease.sticky
|
|
385
|
+
lease.sticky = false if prevent_permanent_checkout
|
|
386
|
+
|
|
387
|
+
if lease.connection
|
|
388
|
+
begin
|
|
389
|
+
yield lease.connection
|
|
390
|
+
ensure
|
|
391
|
+
lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
|
|
392
|
+
end
|
|
393
|
+
else
|
|
394
|
+
begin
|
|
395
|
+
yield lease.connection = checkout
|
|
396
|
+
ensure
|
|
397
|
+
lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
|
|
398
|
+
release_connection(lease) unless lease.sticky
|
|
399
|
+
end
|
|
214
400
|
end
|
|
215
|
-
yield conn
|
|
216
|
-
ensure
|
|
217
|
-
release_connection if fresh_connection
|
|
218
401
|
end
|
|
219
402
|
|
|
220
403
|
# Returns true if a connection has already been opened.
|
|
221
404
|
def connected?
|
|
222
|
-
synchronize { @connections.any? }
|
|
405
|
+
synchronize { @connections.any?(&:connected?) }
|
|
223
406
|
end
|
|
224
407
|
|
|
225
408
|
# Returns an array containing the connections currently in the pool.
|
|
@@ -254,6 +437,7 @@ module ActiveRecord
|
|
|
254
437
|
conn.disconnect!
|
|
255
438
|
end
|
|
256
439
|
@connections = []
|
|
440
|
+
@leases.clear
|
|
257
441
|
@available.clear
|
|
258
442
|
end
|
|
259
443
|
end
|
|
@@ -280,7 +464,7 @@ module ActiveRecord
|
|
|
280
464
|
@connections.each do |conn|
|
|
281
465
|
conn.discard!
|
|
282
466
|
end
|
|
283
|
-
@connections = @available = @
|
|
467
|
+
@connections = @available = @leases = nil
|
|
284
468
|
end
|
|
285
469
|
end
|
|
286
470
|
|
|
@@ -338,7 +522,21 @@ module ActiveRecord
|
|
|
338
522
|
# Raises:
|
|
339
523
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
|
340
524
|
def checkout(checkout_timeout = @checkout_timeout)
|
|
341
|
-
|
|
525
|
+
if @pinned_connection
|
|
526
|
+
@pinned_connection.lock.synchronize do
|
|
527
|
+
synchronize do
|
|
528
|
+
@pinned_connection.verify!
|
|
529
|
+
# Any leased connection must be in @connections otherwise
|
|
530
|
+
# some methods like #connected? won't behave correctly
|
|
531
|
+
unless @connections.include?(@pinned_connection)
|
|
532
|
+
@connections << @pinned_connection
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
@pinned_connection
|
|
537
|
+
else
|
|
538
|
+
checkout_and_verify(acquire_connection(checkout_timeout))
|
|
539
|
+
end
|
|
342
540
|
end
|
|
343
541
|
|
|
344
542
|
# Check-in a database connection back into the pool, indicating that you
|
|
@@ -347,9 +545,11 @@ module ActiveRecord
|
|
|
347
545
|
# +conn+: an AbstractAdapter object, which was obtained by earlier by
|
|
348
546
|
# calling #checkout on this pool.
|
|
349
547
|
def checkin(conn)
|
|
548
|
+
return if @pinned_connection.equal?(conn)
|
|
549
|
+
|
|
350
550
|
conn.lock.synchronize do
|
|
351
551
|
synchronize do
|
|
352
|
-
|
|
552
|
+
connection_lease.clear(conn)
|
|
353
553
|
|
|
354
554
|
conn._run_checkin_callbacks do
|
|
355
555
|
conn.expire
|
|
@@ -448,8 +648,7 @@ module ActiveRecord
|
|
|
448
648
|
@available.num_waiting
|
|
449
649
|
end
|
|
450
650
|
|
|
451
|
-
#
|
|
452
|
-
# Example:
|
|
651
|
+
# Returns the connection pool's usage statistic.
|
|
453
652
|
#
|
|
454
653
|
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
|
455
654
|
def stat
|
|
@@ -472,6 +671,10 @@ module ActiveRecord
|
|
|
472
671
|
end
|
|
473
672
|
|
|
474
673
|
private
|
|
674
|
+
def connection_lease
|
|
675
|
+
@leases[ActiveSupport::IsolatedExecutionState.context]
|
|
676
|
+
end
|
|
677
|
+
|
|
475
678
|
def build_async_executor
|
|
476
679
|
case ActiveRecord.async_query_executor
|
|
477
680
|
when :multi_thread_pool
|
|
@@ -500,19 +703,6 @@ module ActiveRecord
|
|
|
500
703
|
end
|
|
501
704
|
end
|
|
502
705
|
|
|
503
|
-
#--
|
|
504
|
-
# From the discussion on GitHub:
|
|
505
|
-
# https://github.com/rails/rails/pull/14938#commitcomment-6601951
|
|
506
|
-
# This hook-in method allows for easier monkey-patching fixes needed by
|
|
507
|
-
# JRuby users that use Fibers.
|
|
508
|
-
def connection_cache_key(thread)
|
|
509
|
-
thread
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
def current_thread
|
|
513
|
-
@lock_thread || Thread.current
|
|
514
|
-
end
|
|
515
|
-
|
|
516
706
|
# Take control of all existing connections so a "group" action such as
|
|
517
707
|
# reload/disconnect can be performed safely. It is no longer enough to
|
|
518
708
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
|
@@ -526,17 +716,20 @@ module ActiveRecord
|
|
|
526
716
|
|
|
527
717
|
def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
|
|
528
718
|
collected_conns = synchronize do
|
|
719
|
+
reap # No need to wait for dead owners
|
|
720
|
+
|
|
529
721
|
# account for our own connections
|
|
530
|
-
@connections.select { |conn| conn.owner ==
|
|
722
|
+
@connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
|
|
531
723
|
end
|
|
532
724
|
|
|
533
725
|
newly_checked_out = []
|
|
534
726
|
timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
|
|
535
727
|
|
|
536
|
-
@available.with_a_bias_for(
|
|
728
|
+
@available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
|
|
537
729
|
loop do
|
|
538
730
|
synchronize do
|
|
539
731
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
|
732
|
+
|
|
540
733
|
remaining_timeout = timeout_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
541
734
|
remaining_timeout = 0 if remaining_timeout < 0
|
|
542
735
|
conn = checkout_for_exclusive_access(remaining_timeout)
|
|
@@ -580,14 +773,14 @@ module ActiveRecord
|
|
|
580
773
|
|
|
581
774
|
thread_report = []
|
|
582
775
|
@connections.each do |conn|
|
|
583
|
-
unless conn.owner ==
|
|
776
|
+
unless conn.owner == ActiveSupport::IsolatedExecutionState.context
|
|
584
777
|
thread_report << "#{conn} is owned by #{conn.owner}"
|
|
585
778
|
end
|
|
586
779
|
end
|
|
587
780
|
|
|
588
781
|
msg << " (#{thread_report.join(', ')})" if thread_report.any?
|
|
589
782
|
|
|
590
|
-
raise ExclusiveConnectionTimeoutError,
|
|
783
|
+
raise ExclusiveConnectionTimeoutError.new(msg, connection_pool: self)
|
|
591
784
|
end
|
|
592
785
|
|
|
593
786
|
def with_new_connections_blocked
|
|
@@ -641,21 +834,31 @@ module ActiveRecord
|
|
|
641
834
|
conn
|
|
642
835
|
else
|
|
643
836
|
reap
|
|
644
|
-
|
|
837
|
+
# Retry after reaping, which may return an available connection,
|
|
838
|
+
# remove an inactive connection, or both
|
|
839
|
+
if conn = @available.poll || try_to_checkout_new_connection
|
|
840
|
+
conn
|
|
841
|
+
else
|
|
842
|
+
@available.poll(checkout_timeout)
|
|
843
|
+
end
|
|
645
844
|
end
|
|
845
|
+
rescue ConnectionTimeoutError => ex
|
|
846
|
+
raise ex.set_pool(self)
|
|
646
847
|
end
|
|
647
848
|
|
|
648
849
|
#--
|
|
649
850
|
# if owner_thread param is omitted, this must be called in synchronize block
|
|
650
851
|
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
|
651
|
-
@
|
|
852
|
+
@leases[owner_thread].clear(conn)
|
|
652
853
|
end
|
|
653
854
|
alias_method :release, :remove_connection_from_thread_cache
|
|
654
855
|
|
|
655
856
|
def new_connection
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
857
|
+
connection = db_config.new_connection
|
|
858
|
+
connection.pool = self
|
|
859
|
+
connection
|
|
860
|
+
rescue ConnectionNotEstablished => ex
|
|
861
|
+
raise ex.set_pool(self)
|
|
659
862
|
end
|
|
660
863
|
|
|
661
864
|
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
|
@@ -693,6 +896,12 @@ module ActiveRecord
|
|
|
693
896
|
def adopt_connection(conn)
|
|
694
897
|
conn.pool = self
|
|
695
898
|
@connections << conn
|
|
899
|
+
|
|
900
|
+
# We just created the first connection, it's time to load the schema
|
|
901
|
+
# cache if that wasn't eagerly done before
|
|
902
|
+
if @schema_cache.nil? && ActiveRecord.lazily_load_schema_cache
|
|
903
|
+
schema_cache.load!
|
|
904
|
+
end
|
|
696
905
|
end
|
|
697
906
|
|
|
698
907
|
def checkout_new_connection
|
|
@@ -702,10 +911,10 @@ module ActiveRecord
|
|
|
702
911
|
|
|
703
912
|
def checkout_and_verify(c)
|
|
704
913
|
c._run_checkout_callbacks do
|
|
705
|
-
c.
|
|
914
|
+
c.clean!
|
|
706
915
|
end
|
|
707
916
|
c
|
|
708
|
-
rescue
|
|
917
|
+
rescue Exception
|
|
709
918
|
remove c
|
|
710
919
|
c.disconnect!
|
|
711
920
|
raise
|