Commit Graph

88522 Commits

Author SHA1 Message Date
Jean Boussier b22439ed64
Merge pull request #48665 from Shopify/fix-update-counter-cache-same-column-name
Fix counter_cache create/concat with overlapping counter_cache_column
2023-07-13 13:57:45 +02:00
Matthew Draper b36f9186b0 Refactor Active Record Schema Cache to not hold a connection
A big problem with the current `SchemaCache` implementation is that it holds
a reference to a connection, and uses it to lazily query the schema when the
information is missing.

This cause issues in multi-threaded contexts because all the connections
of a pool share the same schema, so they are constantly setting themselves
as the connection the schema cache should use, and you can end up in
situation where Thread#1 query the schema cache, which end up using the
connection currently attributed to Thread#2.

For a very long time this worked more or less by accident, because all
connection adapters would have an internal Monitor.

However in https://github.com/rails/rails/pull/46519 we got rid of this
synchronization, revealing the pre-existing issue. Previously it would
work™,  but still wasn't ideal because of unexpected the connection
sharing between threads.

The idea here is to refactor the schema cache so that it doesn't hold a
reference onto any connection.

And instead of a `SchemaCache`, connections now have access to a
`SchemaReflection`. Now the connection that needs a schema information
is always provided as an argument, so that in case of a miss, it can be
used to populate the cache.

That should fix the database thread safety issues that were witnessed.

However note that at this stage, the underlying `SchemaCache` isn't
synchronized, so in case of a race condition, the same schema query
can be performed more than once. It could make sense to add synchronization
in a followup.

This refactoring also open the door to query the cache without a connection,
making it easier to eagerly define model attribute methods on boot without
establishing a connection to the database.

Co-Authored-By: Jean Boussier <jean.boussier@gmail.com>
Co-Authored-By: zzak <zzakscott@gmail.com>
2023-07-13 12:34:13 +02:00
Jacopo 186474f273 Fix counter_cache create/concat with overlapping counter_cache_column
Fix when multiple `belongs_to` maps to the same counter_cache column.
In such situation `inverse_which_updates_counter_cache` may find the
wrong relation which leads into an invalid increment of the
counter_cache.

This is done by releying on the `inverse_of` property of the relation
as well as comparing the models the association points two.

Note however that this second check doesn't work for polymorphic
associations.

Fixes #41250

Co-Authored-By: Jean Boussier <jean.boussier@gmail.com>
2023-07-13 12:06:08 +02:00
Jean Boussier f043f74cdb
Merge pull request #48730 from Shopify/eagerly-cast-serialized-query-attributes
Eagerly cast serialized query attributes
2023-07-13 11:58:33 +02:00
Jean Boussier 29205d7928 Eagerly cast serialized query attributes
Fix: https://github.com/rails/rails/issues/48652
Ref: https://github.com/rails/rails/pull/46048
Ref: https://github.com/rails/rails/issues/46044
Ref: https://github.com/rails/rails/pull/34303
Ref: https://github.com/rails/rails/pull/39160
Close: https://github.com/rails/rails/pull/48705

This deep_dup was introduced to prevent the value stored in the query
cache to be later mutated.

The problem is that `ActiveRecord::Base#dup` will return a copy
of the record but with the primary key set to nil. One could
argue that `#dup` shouldn't behave this way, but I think this ship
has sailed (or has it?).

My initial fix was to instead always call `type.cast` eagerly so that we'd dup
serialized types in a more correct way. However there is a test
that explictly ensure this doesn't happen: https://github.com/rails/rails/pull/39160

The reason isn't 100% clear to me, but if I get it correctly, it's to avoid
a potentially costly operation upfront.

So instead we only eagerly cast serialized attributes only, so protect against
future mutations.

Mutable types are still deep duped.
2023-07-13 11:44:33 +02:00
Guillermo Iguaran 7d49d7bd75
Merge pull request #48719 from henrik/doc-and-spec-cookies-delete-rval
Doc and spec cookies.delete returning the cookie value
2023-07-13 00:13:46 -07:00
Alex Ghiculescu cdebf39e6c
Improve AR Encryption docs 2023-07-13 16:30:22 +10:00
Eileen M. Uchitelle a8e653fdd0
Merge pull request #48724 from gmcgibbon/improve_cpk_validation_check
Improve cpk validation check
2023-07-12 18:12:01 -04:00
Eileen M. Uchitelle ecd65126fa
Merge pull request #48725 from gmcgibbon/remove_cpk_habtm_suppression
Remove unused suppress_composite_primary_key on HABTM join model class
2023-07-12 18:11:20 -04:00
Gannon McGibbon 410fc35055 Remove unused suppress_composite_primary_key on HABTM join model class
This method appears to be dead code. Since it was intended to be
private, it should be safe to remove without deprecation.
2023-07-12 16:12:13 -05:00
Gannon McGibbon ab0451132c Improve CPK error message
A developer may not always want to specify query constraints. If, for
example they would like to use a non-composite primary key in an
asscoaition, they need to use primary_key/foreign_key options instead.

Co-authored-by: Nikita Vasilevsky <nikita.vasilevsky@shopify.com>
2023-07-12 16:02:46 -05:00
Gannon McGibbon c18d1293b3 Allow assocaition primary keys to be derived with query contraints
Support assignment of belongs_to associations with query constraints.
This improves errors messages for composite key mismatches, and some
edge cases with query constraint lists.

Co-authored-by: Nikita Vasilevsky <nikita.vasilevsky@shopify.com>
2023-07-12 16:02:45 -05:00
Gannon McGibbon b2c0f3e5c1 Validate composite key length when owner record class has composite key
Catches more bugs in CPK assocaitions by validating the shape of keys on
both ends of the association.

Co-authored-by: Nikita Vasilevsky <nikita.vasilevsky@shopify.com>
2023-07-12 15:40:15 -05:00
Henrik Nyh cf166fb13c Doc and spec cookies.delete returning value 2023-07-12 12:08:35 +01:00
Jean Boussier 50e3ae0836
Merge pull request #48693 from zzak/bigdecimal-to_s
Use a consistent format when testing BigDecimal.to_s
2023-07-12 09:13:22 +02:00
Guillermo Iguaran a7ef1250aa
Merge pull request #48679 from p8/activerecord/aes256gm-inspect
Don't show secrets for Active Record's `Cipher::Aes256Gcm#inspect`.
2023-07-11 13:55:48 -07:00
Petrik de Heus f768944bdf
Merge branch 'main' into activerecord/aes256gm-inspect 2023-07-11 21:10:42 +02:00
Eileen M. Uchitelle 6758d3e6d8
Merge pull request #48717 from rails/fix-48710
Re-establish connection at end of test
2023-07-11 13:09:54 -04:00
eileencodes e1415a7737
Re-establish connection at end of test
Fix flaky tests where we needed to re-establish the connection after
removing it at the end of the test.

Fixes #48710
2023-07-11 12:43:08 -04:00
Justin Coyne 68ba7582fb
Clarify comment about debug log level.
I believe the previous phrasing could be interpreted ambiguously. See discussion in #48713
2023-07-11 10:26:58 -05:00
Guillermo Iguaran 66676ce499
Merge pull request #48680 from p8/activesupport/message-verifier-inspect
Don't show secrets for `MessageVerifier#inspect` and `KeyGenerator#inspect`
2023-07-10 10:56:18 -07:00
Eileen M. Uchitelle 8cc23bfa84
Merge pull request #48708 from eileencodes/remove-connected-check-from-db_runtime
Remove connected? check from db_runtime payload
2023-07-10 11:44:34 -04:00
eileencodes 95f7feaf74
Remove connected? check from db_runtime payload
The check for `Base.connected?` was added in
4ecdf24bde.
At the time this change was made, we were calling
`Base.connection.reset_runtime` so it made sense to check if the
database was connected. Since
dd61a817de
we have stored that information in a thread instead, negating the need
to check `Base.connected?`.

The problem with checking `Base.connected?` is that in a multi-db
application this will return `false` for any model running a query not
inheriting from `Base`, leaving the `db_runtime` payload `nil`. Since
the information we need is stored in a thread rather than on the
connection directly we can remove the `Base.connected?` check.

Fixes #48687
2023-07-10 11:21:59 -04:00
Jean Boussier 0a06c1e7a4
Merge pull request #48707 from Shopify/improve-query-cache-leak-test
Improve QueryCacheMutableParamTest
2023-07-10 17:03:43 +02:00
Jean Boussier 98cdd2d91f Improve QueryCacheMutableParamTest
By using a Hash subclass, we hit a branch that dup the value
in PredicateBuilder, rendering the test useless.

Ref: c5a99e044e/activerecord/lib/active_record/relation/predicate_builder.rb (L153-L158)
2023-07-10 16:54:33 +02:00
Eileen M. Uchitelle 56c784d808
Merge pull request #48690 from adrianna-chang-shopify/ac-merge-target-list-cpk-fix
Prevent `#merge_target_lists` from clearing id column on composite primary key records
2023-07-10 09:06:07 -04:00
Jean Boussier 7b52f569c1
Merge pull request #48600 from Shopify/transaction-commit-on-return
Active Record commit transaction on `return`, `break` and `throw`
2023-07-10 10:55:41 +02:00
Guillermo Iguaran 5515d1e524
Merge pull request #48698 from basecamp/active-model-load-hook
Add a load hook for `ActiveModel::Model`
2023-07-10 01:43:52 -07:00
Jean Boussier 5fbaa524b9 Active Record commit transaction on `return`, `break` and `throw`
Fix: https://github.com/rails/rails/issues/45017
Ref: https://github.com/rails/rails/pull/29333
Ref: https://github.com/ruby/timeout/pull/30

Historically only raised errors would trigger a rollback, but in Ruby `2.3`, the `timeout` library
started using `throw` to interupt execution which had the adverse effect of committing open transactions.

To solve this, in Active Record 6.1 the behavior was changed to instead rollback the transaction as it was safer
than to potentially commit an incomplete transaction.

Using `return`, `break` or `throw` inside a `transaction` block was essentially deprecated from Rails 6.1 onwards.

However with the release of `timeout 0.4.0`, `Timeout.timeout` now raises an error again, and Active Record is able
to return to its original, less surprising, behavior.
2023-07-10 10:37:42 +02:00
Lewis Buckley 08a79ce284
Add a load hook for `ActiveModel::Model`
ActiveRecord::Base has a dedicated ActiveSupport load hook. This adds an
additional hook for ActiveModel::Model, so that when ActiveModel is
being used without ActiveRecord, it can still be modified.
2023-07-09 13:08:34 +01:00
zzak cbe2a4c52c
Use a consistent format when testing BigDecimal.to_s
The previous examples were causing failures in CI, after ruby/bigdecimal#264 was merged.

For example:

```
Failure:
NumericExtFormattingTest#test_default_to_fs [/rails/activesupport/test/core_ext/numeric_ext_test.rb:446]:
--- expected
+++ actual
@@ -1 +1,3 @@
-"10000 10.0"
+# encoding: US-ASCII
+#    valid: true
+"10 00010.0"
```

The recommendation was to adjust the test to deal with the upstream change more flexibly.
2023-07-09 17:22:21 +09:00
Adrianna Chang c5abbbae59
Prevent #merge_target_lists from clearing id column on CPK records
Previously, when merging persisted and in-memory records, we were using
`record[name]` to get the value of the attribute. For records with a
composite primary key, the full CPK would be returned, which was
incompatible with `#_write_attribute`: we'd attempt to assign an Array
to the `id` column, which would result in the column being set to nil.

When dealing with a record with a composite primary key, we need to
write each part of the primary key individually via `#_write_attribute`.
2023-07-07 16:52:37 -04:00
Guillermo Iguaran a5fc471b3f
Merge pull request #48667 from igor-drozdov/fix-ruby-keywords-for-action-view-test-case
Add ruby2_keywords to ActionView::TestCase#method_missing
2023-07-06 14:24:49 -07:00
Eileen M. Uchitelle e79250cb26
Merge pull request #48681 from eileencodes/deprecate-name-argument-in-remove_connection
Deprecate `name` argument in `remove_connection`
2023-07-06 16:15:20 -04:00
Petrik de Heus 2e597fa423
Merge pull request #48661 from p8/activesupport/document-cache-multiple-return-value
Document return value of `Rails.cache.delete_multi`
2023-07-06 21:58:42 +02:00
eileencodes b69fa80b66
Deprecate `name` argument in `remove_connection`
The `name` argument is not useful as `remove_connection` should be called
on the class that established the connection. Allowing `name` to be
passed here doesn't match how any of the other methods behave on the
connection classes. While this behavior has been around for a long time,
I'm not sure anyone is using this as it's not documented when to use
name, nor are there any tests.
2023-07-06 15:55:35 -04:00
Petrik 5117da2b65 Don't show secrets for `MessageVerifier#inspect` and `KeyGenerator#inspect`
Before:

```ruby
ActiveSupport::MessageVerifier.new(secret).inspect
"#<ActiveSupport::MessageVerifier:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"
ActiveSupport::KeyGenerator.new(secret).inspect
"#<ActiveSupport::KeyGenerator:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"
```

After:

```ruby
ActiveSupport::MessageVerifier::Aes256Gcm(secret).inspect
"#<ActiveSupport::MessageVerifier:0x0000000104888038>"
ActiveSupport::KeyGenerator::Aes256Gcm(secret).inspect
"#<ActiveSupport::KeyGenerator:0x0000000104888038>"
```
2023-07-06 21:51:22 +02:00
Petrik 7dd38cfa16 Don't show secrets for Active Record's `Cipher::Aes256Gcm#inspect`.
If anyone calls a cypher in the console it will show the secret of the
encryptor.

By overriding the `inspect` method to only show the class name we can
avoid accidentally outputting sensitive information.

Before:

```ruby
ActiveRecord::Encryption::Cipher::Aes256Gcm.new(secret).inspect
"#<ActiveRecord::Encryption::Cipher::Aes256Gcm:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"
```

After:

```ruby
ActiveRecord::Encryption::Cipher::Aes256Gcm(secret).inspect
"#<ActiveRecord::Encryption::Cipher::Aes256Gcm:0x0000000104888038>"
```
2023-07-06 21:41:27 +02:00
Eileen M. Uchitelle 1cbd88f918
Merge pull request #48662 from skipkayhil/hm-fix-memcache-6-1-deprecation
Fix MemCacheStore not warning on 6.1 cache format
2023-07-06 09:10:51 -04:00
Eileen M. Uchitelle fc1886757d
Merge pull request #48674 from gmcgibbon/hmt_singular_fix
Fix has_one through singular building with inverse.
2023-07-06 09:09:28 -04:00
Jean Boussier 249345b7cd
Merge pull request #48678 from jdelStrother/redis-options
Remove unused RedisCacheStore#redis_options
2023-07-06 15:08:33 +02:00
Jonathan del Strother 9e31b19444
Remove unused RedisCacheStore#redis_options
The underlying ivar was removed in c07812cee2
2023-07-06 12:59:01 +01:00
Jean Boussier 80603461f6
Merge pull request #48663 from Shopify/as-cache-handle-deserialization-issues
Make Active Support Cache treat deserialization errors like cache misses
2023-07-06 11:03:07 +02:00
Jean Boussier 55a852a63f Make Active Support Cache treat deserialization errors like cache misses
This help treating caches entries as expandable.

Because Marshal will hapily serialize almost everything, It's not uncommon to
inadvertently cache a class that's not particularly stable, and cause deserialization
errors on rollout when the implementation changes.

E.g. https://github.com/sporkmonger/addressable/pull/508

With this change, in case of such event, the hit rate will suffer for a
bit, but the application won't return 500s responses.
2023-07-06 10:51:27 +02:00
Guillermo Iguaran d540abd31b
Add parentheses to ruby2_keywords method call
For consistency with all the other usages of the method in the code base.
2023-07-05 23:35:23 -07:00
Ryuta Kamizono e552fca4a6
Merge pull request #48657 from alpaca-tc/fix-association-with-has-many-inversing
Fix de-duplication of unsaved records for `ActiveRecord::Associations::CollectionProxy#<<`
2023-07-06 14:37:07 +09:00
Gannon McGibbon 68e0dd2c83 Fix has_one through singular building with inverse.
Allows building of records from an association with a has_one through a
singular association with inverse. For belongs_to through associations,
linking the foreign key to the primary key model isn't needed.
For has_one, we cannot build records due to the association not being mutable.
2023-07-05 23:22:51 -05:00
alpaca-tc d413d36de2 Fix de-duplication of unsaved records for `ActiveRecord::Associations::CollectionProxy#<<`
The existing process was attempting to detect duplicates by storing added records in `Set.new`.
When a record is added to `Set.new`, the identity is calculated using `ActiveRecord::Core#hash`, but this value changes before and after saving, so duplicates were not detected before and after saving even for the same object.

This PR fixed the problem by using the `#object_id` of the record to detect duplicates.
Note that when storing a new object obtained by `ActiveRecord::Base.find` etc., duplicates are not eliminated because the `#object_id` is different. This is the same behavior as the current `ActiveRecord::Associations::CollectionProxy#<<`.
2023-07-06 09:51:55 +09:00
Jonathan Hefner 1d4c28858e
Merge pull request #48673 from jonathanhefner/cache-anticipate-replaceable-compressor
Lay groundwork for replaceable cache compressor
2023-07-05 17:35:27 -05:00
Jonathan Hefner 04bca8b345 Test signature for each cache format version 2023-07-05 17:11:26 -05:00