activerecord 7.0.8 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +530 -2004
- 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 +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 +5 -5
- 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 +328 -471
- 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 +131 -32
- 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 +148 -33
- data/lib/active_record/attributes.rb +58 -45
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +10 -24
- 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 +317 -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 +188 -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 +306 -128
- 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 +274 -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 +368 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +364 -198
- 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 +217 -174
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -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 +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 +44 -20
- data/lib/active_record/encryption/encrypted_attribute_type.rb +45 -10
- data/lib/active_record/encryption/encryptor.rb +17 -2
- 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/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 +1 -0
- data/lib/active_record/enum.rb +122 -29
- 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 +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 +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 +88 -103
- data/lib/active_record/nested_attributes.rb +35 -9
- 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 +19 -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 +135 -86
- 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 +259 -68
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +196 -61
- data/lib/active_record/relation/calculations.rb +249 -92
- 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 +548 -94
- 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 +2 -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 +180 -119
- 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 +60 -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/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 +56 -14
- 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,98 @@ 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 Lease # :nodoc:
|
|
122
|
+
attr_accessor :connection, :sticky
|
|
123
|
+
|
|
124
|
+
def initialize
|
|
125
|
+
@connection = nil
|
|
126
|
+
@sticky = nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def release
|
|
130
|
+
conn = @connection
|
|
131
|
+
@connection = nil
|
|
132
|
+
@sticky = nil
|
|
133
|
+
conn
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def clear(connection)
|
|
137
|
+
if @connection == connection
|
|
138
|
+
@connection = nil
|
|
139
|
+
@sticky = nil
|
|
140
|
+
true
|
|
141
|
+
else
|
|
142
|
+
false
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
class LeaseRegistry # :nodoc:
|
|
148
|
+
if ObjectSpace.const_defined?(:WeakKeyMap) # RUBY_VERSION >= 3.3
|
|
149
|
+
WeakKeyMap = ::ObjectSpace::WeakKeyMap # :nodoc:
|
|
150
|
+
else
|
|
151
|
+
class WeakKeyMap # :nodoc:
|
|
152
|
+
def initialize
|
|
153
|
+
@map = ObjectSpace::WeakMap.new
|
|
154
|
+
@values = nil
|
|
155
|
+
@size = 0
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
alias_method :clear, :initialize
|
|
159
|
+
|
|
160
|
+
def [](key)
|
|
161
|
+
prune if @map.size != @size
|
|
162
|
+
@map[key]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def []=(key, value)
|
|
166
|
+
@map[key] = value
|
|
167
|
+
prune if @map.size != @size
|
|
168
|
+
value
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def delete(key)
|
|
172
|
+
if value = self[key]
|
|
173
|
+
self[key] = nil
|
|
174
|
+
prune
|
|
175
|
+
end
|
|
176
|
+
value
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
private
|
|
180
|
+
def prune(force = false)
|
|
181
|
+
@values = @map.values
|
|
182
|
+
@size = @map.size
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def initialize
|
|
188
|
+
@mutex = Mutex.new
|
|
189
|
+
@map = WeakKeyMap.new
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def [](context)
|
|
193
|
+
@mutex.synchronize do
|
|
194
|
+
@map[context] ||= Lease.new
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def clear
|
|
199
|
+
@mutex.synchronize do
|
|
200
|
+
@map = WeakKeyMap.new
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
103
205
|
include MonitorMixin
|
|
104
|
-
|
|
206
|
+
prepend QueryCache::ConnectionPoolConfiguration
|
|
105
207
|
include ConnectionAdapters::AbstractPool
|
|
106
208
|
|
|
107
209
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
|
108
|
-
attr_reader :db_config, :size, :reaper, :pool_config, :
|
|
210
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
|
|
109
211
|
|
|
110
|
-
|
|
111
|
-
deprecate :connection_klass
|
|
112
|
-
delegate :schema_cache, :schema_cache=, to: :pool_config
|
|
212
|
+
delegate :schema_reflection, :server_version, to: :pool_config
|
|
113
213
|
|
|
114
214
|
# Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
|
|
115
215
|
# object which describes database connection information (e.g. adapter,
|
|
@@ -122,7 +222,6 @@ module ActiveRecord
|
|
|
122
222
|
|
|
123
223
|
@pool_config = pool_config
|
|
124
224
|
@db_config = pool_config.db_config
|
|
125
|
-
@connection_class = pool_config.connection_class
|
|
126
225
|
@role = pool_config.role
|
|
127
226
|
@shard = pool_config.shard
|
|
128
227
|
|
|
@@ -138,9 +237,9 @@ module ActiveRecord
|
|
|
138
237
|
# then that +thread+ does indeed own that +conn+. However, an absence of such
|
|
139
238
|
# mapping does not mean that the +thread+ doesn't own the said connection. In
|
|
140
239
|
# that case +conn.owner+ attr should be consulted.
|
|
141
|
-
# Access and modification of <tt>@
|
|
240
|
+
# Access and modification of <tt>@leases</tt> does not require
|
|
142
241
|
# synchronization.
|
|
143
|
-
@
|
|
242
|
+
@leases = LeaseRegistry.new
|
|
144
243
|
|
|
145
244
|
@connections = []
|
|
146
245
|
@automatic_reconnect = true
|
|
@@ -153,73 +252,175 @@ module ActiveRecord
|
|
|
153
252
|
@threads_blocking_new_connections = 0
|
|
154
253
|
|
|
155
254
|
@available = ConnectionLeasingQueue.new self
|
|
156
|
-
|
|
157
|
-
@
|
|
255
|
+
@pinned_connection = nil
|
|
256
|
+
@pinned_connections_depth = 0
|
|
158
257
|
|
|
159
258
|
@async_executor = build_async_executor
|
|
160
259
|
|
|
161
|
-
|
|
260
|
+
@schema_cache = nil
|
|
162
261
|
|
|
163
262
|
@reaper = Reaper.new(self, db_config.reaping_frequency)
|
|
164
263
|
@reaper.run
|
|
165
264
|
end
|
|
166
265
|
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
266
|
+
def inspect # :nodoc:
|
|
267
|
+
name_field = " name=#{db_config.name.inspect}" unless db_config.name == "primary"
|
|
268
|
+
shard_field = " shard=#{@shard.inspect}" unless @shard == :default
|
|
269
|
+
|
|
270
|
+
"#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def schema_cache
|
|
274
|
+
@schema_cache ||= BoundSchemaReflection.new(schema_reflection, self)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def schema_reflection=(schema_reflection)
|
|
278
|
+
pool_config.schema_reflection = schema_reflection
|
|
279
|
+
@schema_cache = nil
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def migration_context # :nodoc:
|
|
283
|
+
MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def migrations_paths # :nodoc:
|
|
287
|
+
db_config.migrations_paths || Migrator.migrations_paths
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def schema_migration # :nodoc:
|
|
291
|
+
SchemaMigration.new(self)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def internal_metadata # :nodoc:
|
|
295
|
+
InternalMetadata.new(self)
|
|
173
296
|
end
|
|
174
297
|
|
|
175
298
|
# Retrieve the connection associated with the current thread, or call
|
|
176
299
|
# #checkout to obtain one if necessary.
|
|
177
300
|
#
|
|
178
|
-
# #
|
|
301
|
+
# #lease_connection can be called any number of times; the connection is
|
|
179
302
|
# held in a cache keyed by a thread.
|
|
303
|
+
def lease_connection
|
|
304
|
+
lease = connection_lease
|
|
305
|
+
lease.sticky = true
|
|
306
|
+
lease.connection ||= checkout
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def permanent_lease? # :nodoc:
|
|
310
|
+
connection_lease.sticky.nil?
|
|
311
|
+
end
|
|
312
|
+
|
|
180
313
|
def connection
|
|
181
|
-
|
|
314
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
|
315
|
+
ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
|
|
316
|
+
and will be removed in Rails 8.0. Use #lease_connection instead.
|
|
317
|
+
MSG
|
|
318
|
+
lease_connection
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def pin_connection!(lock_thread) # :nodoc:
|
|
322
|
+
@pinned_connection ||= (connection_lease&.connection || checkout)
|
|
323
|
+
@pinned_connections_depth += 1
|
|
324
|
+
|
|
325
|
+
# Any leased connection must be in @connections otherwise
|
|
326
|
+
# some methods like #connected? won't behave correctly
|
|
327
|
+
unless @connections.include?(@pinned_connection)
|
|
328
|
+
@connections << @pinned_connection
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
@pinned_connection.lock_thread = ActiveSupport::IsolatedExecutionState.context if lock_thread
|
|
332
|
+
@pinned_connection.verify! # eagerly validate the connection
|
|
333
|
+
@pinned_connection.begin_transaction joinable: false, _lazy: false
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def unpin_connection! # :nodoc:
|
|
337
|
+
raise "There isn't a pinned connection #{object_id}" unless @pinned_connection
|
|
338
|
+
|
|
339
|
+
clean = true
|
|
340
|
+
@pinned_connection.lock.synchronize do
|
|
341
|
+
@pinned_connections_depth -= 1
|
|
342
|
+
connection = @pinned_connection
|
|
343
|
+
@pinned_connection = nil if @pinned_connections_depth.zero?
|
|
344
|
+
|
|
345
|
+
if connection.transaction_open?
|
|
346
|
+
connection.rollback_transaction
|
|
347
|
+
else
|
|
348
|
+
# Something committed or rolled back the transaction
|
|
349
|
+
clean = false
|
|
350
|
+
connection.reset!
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
if @pinned_connection.nil?
|
|
354
|
+
connection.lock_thread = nil
|
|
355
|
+
checkin(connection)
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
clean
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def connection_class # :nodoc:
|
|
363
|
+
pool_config.connection_class
|
|
182
364
|
end
|
|
183
365
|
|
|
184
366
|
# Returns true if there is an open connection being used for the current thread.
|
|
185
367
|
#
|
|
186
368
|
# This method only works for connections that have been obtained through
|
|
187
|
-
# #
|
|
369
|
+
# #lease_connection or #with_connection methods. Connections obtained through
|
|
188
370
|
# #checkout will not be detected by #active_connection?
|
|
189
371
|
def active_connection?
|
|
190
|
-
|
|
372
|
+
connection_lease.connection
|
|
191
373
|
end
|
|
374
|
+
alias_method :active_connection, :active_connection? # :nodoc:
|
|
192
375
|
|
|
193
376
|
# Signal that the thread is finished with the current connection.
|
|
194
377
|
# #release_connection releases the connection-thread association
|
|
195
378
|
# and returns the connection to the pool.
|
|
196
379
|
#
|
|
197
380
|
# This method only works for connections that have been obtained through
|
|
198
|
-
# #
|
|
381
|
+
# #lease_connection or #with_connection methods, connections obtained through
|
|
199
382
|
# #checkout will not be automatically released.
|
|
200
|
-
def release_connection(
|
|
201
|
-
if conn =
|
|
383
|
+
def release_connection(existing_lease = nil)
|
|
384
|
+
if conn = connection_lease.release
|
|
202
385
|
checkin conn
|
|
386
|
+
return true
|
|
203
387
|
end
|
|
388
|
+
false
|
|
204
389
|
end
|
|
205
390
|
|
|
206
|
-
#
|
|
207
|
-
# already
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
391
|
+
# Yields a connection from the connection pool to the block. If no connection
|
|
392
|
+
# is already checked out by the current thread, a connection will be checked
|
|
393
|
+
# out from the pool, yielded to the block, and then returned to the pool when
|
|
394
|
+
# the block is finished. If a connection has already been checked out on the
|
|
395
|
+
# current thread, such as via #lease_connection or #with_connection, that existing
|
|
396
|
+
# connection will be the one yielded and it will not be returned to the pool
|
|
397
|
+
# automatically at the end of the block; it is expected that such an existing
|
|
398
|
+
# connection will be properly returned to the pool by the code that checked
|
|
399
|
+
# it out.
|
|
400
|
+
def with_connection(prevent_permanent_checkout: false)
|
|
401
|
+
lease = connection_lease
|
|
402
|
+
sticky_was = lease.sticky
|
|
403
|
+
lease.sticky = false if prevent_permanent_checkout
|
|
404
|
+
|
|
405
|
+
if lease.connection
|
|
406
|
+
begin
|
|
407
|
+
yield lease.connection
|
|
408
|
+
ensure
|
|
409
|
+
lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
|
|
410
|
+
end
|
|
411
|
+
else
|
|
412
|
+
begin
|
|
413
|
+
yield lease.connection = checkout
|
|
414
|
+
ensure
|
|
415
|
+
lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
|
|
416
|
+
release_connection(lease) unless lease.sticky
|
|
417
|
+
end
|
|
214
418
|
end
|
|
215
|
-
yield conn
|
|
216
|
-
ensure
|
|
217
|
-
release_connection if fresh_connection
|
|
218
419
|
end
|
|
219
420
|
|
|
220
421
|
# Returns true if a connection has already been opened.
|
|
221
422
|
def connected?
|
|
222
|
-
synchronize { @connections.any? }
|
|
423
|
+
synchronize { @connections.any?(&:connected?) }
|
|
223
424
|
end
|
|
224
425
|
|
|
225
426
|
# Returns an array containing the connections currently in the pool.
|
|
@@ -254,6 +455,7 @@ module ActiveRecord
|
|
|
254
455
|
conn.disconnect!
|
|
255
456
|
end
|
|
256
457
|
@connections = []
|
|
458
|
+
@leases.clear
|
|
257
459
|
@available.clear
|
|
258
460
|
end
|
|
259
461
|
end
|
|
@@ -280,7 +482,7 @@ module ActiveRecord
|
|
|
280
482
|
@connections.each do |conn|
|
|
281
483
|
conn.discard!
|
|
282
484
|
end
|
|
283
|
-
@connections = @available = @
|
|
485
|
+
@connections = @available = @leases = nil
|
|
284
486
|
end
|
|
285
487
|
end
|
|
286
488
|
|
|
@@ -338,7 +540,21 @@ module ActiveRecord
|
|
|
338
540
|
# Raises:
|
|
339
541
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
|
340
542
|
def checkout(checkout_timeout = @checkout_timeout)
|
|
341
|
-
|
|
543
|
+
if @pinned_connection
|
|
544
|
+
@pinned_connection.lock.synchronize do
|
|
545
|
+
synchronize do
|
|
546
|
+
@pinned_connection.verify!
|
|
547
|
+
# Any leased connection must be in @connections otherwise
|
|
548
|
+
# some methods like #connected? won't behave correctly
|
|
549
|
+
unless @connections.include?(@pinned_connection)
|
|
550
|
+
@connections << @pinned_connection
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
@pinned_connection
|
|
555
|
+
else
|
|
556
|
+
checkout_and_verify(acquire_connection(checkout_timeout))
|
|
557
|
+
end
|
|
342
558
|
end
|
|
343
559
|
|
|
344
560
|
# Check-in a database connection back into the pool, indicating that you
|
|
@@ -347,9 +563,11 @@ module ActiveRecord
|
|
|
347
563
|
# +conn+: an AbstractAdapter object, which was obtained by earlier by
|
|
348
564
|
# calling #checkout on this pool.
|
|
349
565
|
def checkin(conn)
|
|
566
|
+
return if @pinned_connection.equal?(conn)
|
|
567
|
+
|
|
350
568
|
conn.lock.synchronize do
|
|
351
569
|
synchronize do
|
|
352
|
-
|
|
570
|
+
connection_lease.clear(conn)
|
|
353
571
|
|
|
354
572
|
conn._run_checkin_callbacks do
|
|
355
573
|
conn.expire
|
|
@@ -412,6 +630,8 @@ module ActiveRecord
|
|
|
412
630
|
remove conn
|
|
413
631
|
end
|
|
414
632
|
end
|
|
633
|
+
|
|
634
|
+
prune_thread_cache
|
|
415
635
|
end
|
|
416
636
|
|
|
417
637
|
# Disconnect all connections that have been idle for at least
|
|
@@ -448,8 +668,7 @@ module ActiveRecord
|
|
|
448
668
|
@available.num_waiting
|
|
449
669
|
end
|
|
450
670
|
|
|
451
|
-
#
|
|
452
|
-
# Example:
|
|
671
|
+
# Returns the connection pool's usage statistic.
|
|
453
672
|
#
|
|
454
673
|
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
|
455
674
|
def stat
|
|
@@ -472,6 +691,10 @@ module ActiveRecord
|
|
|
472
691
|
end
|
|
473
692
|
|
|
474
693
|
private
|
|
694
|
+
def connection_lease
|
|
695
|
+
@leases[ActiveSupport::IsolatedExecutionState.context]
|
|
696
|
+
end
|
|
697
|
+
|
|
475
698
|
def build_async_executor
|
|
476
699
|
case ActiveRecord.async_query_executor
|
|
477
700
|
when :multi_thread_pool
|
|
@@ -500,19 +723,6 @@ module ActiveRecord
|
|
|
500
723
|
end
|
|
501
724
|
end
|
|
502
725
|
|
|
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
726
|
# Take control of all existing connections so a "group" action such as
|
|
517
727
|
# reload/disconnect can be performed safely. It is no longer enough to
|
|
518
728
|
# wrap it in +synchronize+ because some pool's actions are allowed
|
|
@@ -526,17 +736,20 @@ module ActiveRecord
|
|
|
526
736
|
|
|
527
737
|
def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
|
|
528
738
|
collected_conns = synchronize do
|
|
739
|
+
reap # No need to wait for dead owners
|
|
740
|
+
|
|
529
741
|
# account for our own connections
|
|
530
|
-
@connections.select { |conn| conn.owner ==
|
|
742
|
+
@connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
|
|
531
743
|
end
|
|
532
744
|
|
|
533
745
|
newly_checked_out = []
|
|
534
746
|
timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
|
|
535
747
|
|
|
536
|
-
@available.with_a_bias_for(
|
|
748
|
+
@available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
|
|
537
749
|
loop do
|
|
538
750
|
synchronize do
|
|
539
751
|
return if collected_conns.size == @connections.size && @now_connecting == 0
|
|
752
|
+
|
|
540
753
|
remaining_timeout = timeout_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
541
754
|
remaining_timeout = 0 if remaining_timeout < 0
|
|
542
755
|
conn = checkout_for_exclusive_access(remaining_timeout)
|
|
@@ -580,14 +793,14 @@ module ActiveRecord
|
|
|
580
793
|
|
|
581
794
|
thread_report = []
|
|
582
795
|
@connections.each do |conn|
|
|
583
|
-
unless conn.owner ==
|
|
796
|
+
unless conn.owner == ActiveSupport::IsolatedExecutionState.context
|
|
584
797
|
thread_report << "#{conn} is owned by #{conn.owner}"
|
|
585
798
|
end
|
|
586
799
|
end
|
|
587
800
|
|
|
588
801
|
msg << " (#{thread_report.join(', ')})" if thread_report.any?
|
|
589
802
|
|
|
590
|
-
raise ExclusiveConnectionTimeoutError,
|
|
803
|
+
raise ExclusiveConnectionTimeoutError.new(msg, connection_pool: self)
|
|
591
804
|
end
|
|
592
805
|
|
|
593
806
|
def with_new_connections_blocked
|
|
@@ -641,21 +854,31 @@ module ActiveRecord
|
|
|
641
854
|
conn
|
|
642
855
|
else
|
|
643
856
|
reap
|
|
644
|
-
|
|
857
|
+
# Retry after reaping, which may return an available connection,
|
|
858
|
+
# remove an inactive connection, or both
|
|
859
|
+
if conn = @available.poll || try_to_checkout_new_connection
|
|
860
|
+
conn
|
|
861
|
+
else
|
|
862
|
+
@available.poll(checkout_timeout)
|
|
863
|
+
end
|
|
645
864
|
end
|
|
865
|
+
rescue ConnectionTimeoutError => ex
|
|
866
|
+
raise ex.set_pool(self)
|
|
646
867
|
end
|
|
647
868
|
|
|
648
869
|
#--
|
|
649
870
|
# if owner_thread param is omitted, this must be called in synchronize block
|
|
650
871
|
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
|
651
|
-
@
|
|
872
|
+
@leases[owner_thread].clear(conn)
|
|
652
873
|
end
|
|
653
874
|
alias_method :release, :remove_connection_from_thread_cache
|
|
654
875
|
|
|
655
876
|
def new_connection
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
877
|
+
connection = db_config.new_connection
|
|
878
|
+
connection.pool = self
|
|
879
|
+
connection
|
|
880
|
+
rescue ConnectionNotEstablished => ex
|
|
881
|
+
raise ex.set_pool(self)
|
|
659
882
|
end
|
|
660
883
|
|
|
661
884
|
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
|
@@ -693,6 +916,12 @@ module ActiveRecord
|
|
|
693
916
|
def adopt_connection(conn)
|
|
694
917
|
conn.pool = self
|
|
695
918
|
@connections << conn
|
|
919
|
+
|
|
920
|
+
# We just created the first connection, it's time to load the schema
|
|
921
|
+
# cache if that wasn't eagerly done before
|
|
922
|
+
if @schema_cache.nil? && ActiveRecord.lazily_load_schema_cache
|
|
923
|
+
schema_cache.load!
|
|
924
|
+
end
|
|
696
925
|
end
|
|
697
926
|
|
|
698
927
|
def checkout_new_connection
|
|
@@ -702,10 +931,10 @@ module ActiveRecord
|
|
|
702
931
|
|
|
703
932
|
def checkout_and_verify(c)
|
|
704
933
|
c._run_checkout_callbacks do
|
|
705
|
-
c.
|
|
934
|
+
c.clean!
|
|
706
935
|
end
|
|
707
936
|
c
|
|
708
|
-
rescue
|
|
937
|
+
rescue Exception
|
|
709
938
|
remove c
|
|
710
939
|
c.disconnect!
|
|
711
940
|
raise
|