activerecord 7.0.8.1 → 7.2.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 +642 -1925
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- 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 +26 -14
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- 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 +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +10 -10
- 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 +1 -3
- 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 +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- 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 +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +11 -25
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- 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 +323 -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 +217 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- 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 +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- 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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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 +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- 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 +45 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -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 +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- 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 +87 -34
- data/lib/active_record/delegated_type.rb +39 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -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 +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/key_provider.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 +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +129 -28
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- 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 +167 -97
- 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 +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- 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 +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- 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 +234 -117
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- 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 +267 -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 +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- 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 +18 -3
- 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 +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -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 +580 -90
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +63 -14
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- 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/signed_id.rb +27 -6
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- 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 +106 -24
- data/lib/active_record/translation.rb +0 -2
- 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 +1 -3
- 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 +9 -3
- 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 +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- 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/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/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} +5 -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/sqlite.rb +25 -0
- 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,105 @@ 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
|
+
|
187
|
+
module ExecutorHooks # :nodoc:
|
188
|
+
class << self
|
189
|
+
def run
|
190
|
+
# noop
|
191
|
+
end
|
192
|
+
|
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
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class << self
|
207
|
+
def install_executor_hooks(executor = ActiveSupport::Executor)
|
208
|
+
executor.register_hook(ExecutorHooks)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
103
212
|
include MonitorMixin
|
104
|
-
|
213
|
+
prepend QueryCache::ConnectionPoolConfiguration
|
105
214
|
include ConnectionAdapters::AbstractPool
|
106
215
|
|
107
216
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
108
|
-
attr_reader :db_config, :size, :reaper, :pool_config, :
|
217
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
|
109
218
|
|
110
|
-
|
111
|
-
deprecate :connection_klass
|
112
|
-
delegate :schema_cache, :schema_cache=, to: :pool_config
|
219
|
+
delegate :schema_reflection, :server_version, to: :pool_config
|
113
220
|
|
114
221
|
# Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
|
115
222
|
# object which describes database connection information (e.g. adapter,
|
@@ -122,7 +229,6 @@ module ActiveRecord
|
|
122
229
|
|
123
230
|
@pool_config = pool_config
|
124
231
|
@db_config = pool_config.db_config
|
125
|
-
@connection_class = pool_config.connection_class
|
126
232
|
@role = pool_config.role
|
127
233
|
@shard = pool_config.shard
|
128
234
|
|
@@ -138,9 +244,9 @@ module ActiveRecord
|
|
138
244
|
# then that +thread+ does indeed own that +conn+. However, an absence of such
|
139
245
|
# mapping does not mean that the +thread+ doesn't own the said connection. In
|
140
246
|
# that case +conn.owner+ attr should be consulted.
|
141
|
-
# Access and modification of <tt>@
|
247
|
+
# Access and modification of <tt>@leases</tt> does not require
|
142
248
|
# synchronization.
|
143
|
-
@
|
249
|
+
@leases = LeaseRegistry.new
|
144
250
|
|
145
251
|
@connections = []
|
146
252
|
@automatic_reconnect = true
|
@@ -153,73 +259,176 @@ module ActiveRecord
|
|
153
259
|
@threads_blocking_new_connections = 0
|
154
260
|
|
155
261
|
@available = ConnectionLeasingQueue.new self
|
156
|
-
|
157
|
-
@
|
262
|
+
@pinned_connection = nil
|
263
|
+
@pinned_connections_depth = 0
|
158
264
|
|
159
265
|
@async_executor = build_async_executor
|
160
266
|
|
161
|
-
|
267
|
+
@schema_cache = nil
|
162
268
|
|
163
269
|
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
164
270
|
@reaper.run
|
165
271
|
end
|
166
272
|
|
167
|
-
def
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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)
|
173
303
|
end
|
174
304
|
|
175
305
|
# Retrieve the connection associated with the current thread, or call
|
176
306
|
# #checkout to obtain one if necessary.
|
177
307
|
#
|
178
|
-
# #
|
308
|
+
# #lease_connection can be called any number of times; the connection is
|
179
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
|
+
|
180
320
|
def connection
|
181
|
-
|
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
|
182
372
|
end
|
183
373
|
|
184
374
|
# Returns true if there is an open connection being used for the current thread.
|
185
375
|
#
|
186
376
|
# This method only works for connections that have been obtained through
|
187
|
-
# #
|
377
|
+
# #lease_connection or #with_connection methods. Connections obtained through
|
188
378
|
# #checkout will not be detected by #active_connection?
|
189
379
|
def active_connection?
|
190
|
-
|
380
|
+
connection_lease.connection
|
191
381
|
end
|
382
|
+
alias_method :active_connection, :active_connection? # :nodoc:
|
192
383
|
|
193
384
|
# Signal that the thread is finished with the current connection.
|
194
385
|
# #release_connection releases the connection-thread association
|
195
386
|
# and returns the connection to the pool.
|
196
387
|
#
|
197
388
|
# This method only works for connections that have been obtained through
|
198
|
-
# #
|
389
|
+
# #lease_connection or #with_connection methods, connections obtained through
|
199
390
|
# #checkout will not be automatically released.
|
200
|
-
def release_connection(
|
201
|
-
if conn =
|
391
|
+
def release_connection(existing_lease = nil)
|
392
|
+
if conn = connection_lease.release
|
202
393
|
checkin conn
|
394
|
+
return true
|
203
395
|
end
|
396
|
+
false
|
204
397
|
end
|
205
398
|
|
206
|
-
#
|
207
|
-
# already
|
208
|
-
#
|
209
|
-
#
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
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
|
214
426
|
end
|
215
|
-
yield conn
|
216
|
-
ensure
|
217
|
-
release_connection if fresh_connection
|
218
427
|
end
|
219
428
|
|
220
429
|
# Returns true if a connection has already been opened.
|
221
430
|
def connected?
|
222
|
-
synchronize { @connections.any? }
|
431
|
+
synchronize { @connections.any?(&:connected?) }
|
223
432
|
end
|
224
433
|
|
225
434
|
# Returns an array containing the connections currently in the pool.
|
@@ -254,6 +463,7 @@ module ActiveRecord
|
|
254
463
|
conn.disconnect!
|
255
464
|
end
|
256
465
|
@connections = []
|
466
|
+
@leases.clear
|
257
467
|
@available.clear
|
258
468
|
end
|
259
469
|
end
|
@@ -280,7 +490,7 @@ module ActiveRecord
|
|
280
490
|
@connections.each do |conn|
|
281
491
|
conn.discard!
|
282
492
|
end
|
283
|
-
@connections = @available = @
|
493
|
+
@connections = @available = @leases = nil
|
284
494
|
end
|
285
495
|
end
|
286
496
|
|
@@ -338,7 +548,21 @@ module ActiveRecord
|
|
338
548
|
# Raises:
|
339
549
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
340
550
|
def checkout(checkout_timeout = @checkout_timeout)
|
341
|
-
|
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
|
342
566
|
end
|
343
567
|
|
344
568
|
# Check-in a database connection back into the pool, indicating that you
|
@@ -347,9 +571,11 @@ module ActiveRecord
|
|
347
571
|
# +conn+: an AbstractAdapter object, which was obtained by earlier by
|
348
572
|
# calling #checkout on this pool.
|
349
573
|
def checkin(conn)
|
574
|
+
return if @pinned_connection.equal?(conn)
|
575
|
+
|
350
576
|
conn.lock.synchronize do
|
351
577
|
synchronize do
|
352
|
-
|
578
|
+
connection_lease.clear(conn)
|
353
579
|
|
354
580
|
conn._run_checkin_callbacks do
|
355
581
|
conn.expire
|
@@ -448,8 +674,7 @@ module ActiveRecord
|
|
448
674
|
@available.num_waiting
|
449
675
|
end
|
450
676
|
|
451
|
-
#
|
452
|
-
# Example:
|
677
|
+
# Returns the connection pool's usage statistic.
|
453
678
|
#
|
454
679
|
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
455
680
|
def stat
|
@@ -472,6 +697,10 @@ module ActiveRecord
|
|
472
697
|
end
|
473
698
|
|
474
699
|
private
|
700
|
+
def connection_lease
|
701
|
+
@leases[ActiveSupport::IsolatedExecutionState.context]
|
702
|
+
end
|
703
|
+
|
475
704
|
def build_async_executor
|
476
705
|
case ActiveRecord.async_query_executor
|
477
706
|
when :multi_thread_pool
|
@@ -500,19 +729,6 @@ module ActiveRecord
|
|
500
729
|
end
|
501
730
|
end
|
502
731
|
|
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
732
|
# Take control of all existing connections so a "group" action such as
|
517
733
|
# reload/disconnect can be performed safely. It is no longer enough to
|
518
734
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
@@ -526,17 +742,20 @@ module ActiveRecord
|
|
526
742
|
|
527
743
|
def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
|
528
744
|
collected_conns = synchronize do
|
745
|
+
reap # No need to wait for dead owners
|
746
|
+
|
529
747
|
# account for our own connections
|
530
|
-
@connections.select { |conn| conn.owner ==
|
748
|
+
@connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
|
531
749
|
end
|
532
750
|
|
533
751
|
newly_checked_out = []
|
534
752
|
timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
|
535
753
|
|
536
|
-
@available.with_a_bias_for(
|
754
|
+
@available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
|
537
755
|
loop do
|
538
756
|
synchronize do
|
539
757
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
758
|
+
|
540
759
|
remaining_timeout = timeout_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
541
760
|
remaining_timeout = 0 if remaining_timeout < 0
|
542
761
|
conn = checkout_for_exclusive_access(remaining_timeout)
|
@@ -580,14 +799,14 @@ module ActiveRecord
|
|
580
799
|
|
581
800
|
thread_report = []
|
582
801
|
@connections.each do |conn|
|
583
|
-
unless conn.owner ==
|
802
|
+
unless conn.owner == ActiveSupport::IsolatedExecutionState.context
|
584
803
|
thread_report << "#{conn} is owned by #{conn.owner}"
|
585
804
|
end
|
586
805
|
end
|
587
806
|
|
588
807
|
msg << " (#{thread_report.join(', ')})" if thread_report.any?
|
589
808
|
|
590
|
-
raise ExclusiveConnectionTimeoutError,
|
809
|
+
raise ExclusiveConnectionTimeoutError.new(msg, connection_pool: self)
|
591
810
|
end
|
592
811
|
|
593
812
|
def with_new_connections_blocked
|
@@ -641,21 +860,31 @@ module ActiveRecord
|
|
641
860
|
conn
|
642
861
|
else
|
643
862
|
reap
|
644
|
-
|
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
|
645
870
|
end
|
871
|
+
rescue ConnectionTimeoutError => ex
|
872
|
+
raise ex.set_pool(self)
|
646
873
|
end
|
647
874
|
|
648
875
|
#--
|
649
876
|
# if owner_thread param is omitted, this must be called in synchronize block
|
650
877
|
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
651
|
-
@
|
878
|
+
@leases[owner_thread].clear(conn)
|
652
879
|
end
|
653
880
|
alias_method :release, :remove_connection_from_thread_cache
|
654
881
|
|
655
882
|
def new_connection
|
656
|
-
|
657
|
-
|
658
|
-
|
883
|
+
connection = db_config.new_connection
|
884
|
+
connection.pool = self
|
885
|
+
connection
|
886
|
+
rescue ConnectionNotEstablished => ex
|
887
|
+
raise ex.set_pool(self)
|
659
888
|
end
|
660
889
|
|
661
890
|
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
@@ -693,6 +922,12 @@ module ActiveRecord
|
|
693
922
|
def adopt_connection(conn)
|
694
923
|
conn.pool = self
|
695
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
|
696
931
|
end
|
697
932
|
|
698
933
|
def checkout_new_connection
|
@@ -702,10 +937,10 @@ module ActiveRecord
|
|
702
937
|
|
703
938
|
def checkout_and_verify(c)
|
704
939
|
c._run_checkout_callbacks do
|
705
|
-
c.
|
940
|
+
c.clean!
|
706
941
|
end
|
707
942
|
c
|
708
|
-
rescue
|
943
|
+
rescue Exception
|
709
944
|
remove c
|
710
945
|
c.disconnect!
|
711
946
|
raise
|