This commit introduces a performance optimization for cache entries with
bare string values such as view fragments.
A new `7.1` cache format has been added which includes the optimization,
and the `:message_pack` cache format now includes the optimization as
well. (A new cache format is necessary because, during a rolling
deploy, unupgraded servers must be able to read cache entries from
upgraded servers, which means the optimization cannot be enabled for
existing apps by default.)
New apps will use the `7.1` cache format by default, and existing apps
can enable the format by setting `config.load_defaults 7.1`. Cache
entries written using the `6.1` or `7.0` cache formats can be read when
using the `7.1` format.
**Benchmark**
```ruby
# frozen_string_literal: true
require "benchmark/ips"
serializer_7_0 = ActiveSupport::Cache::SerializerWithFallback[:marshal_7_0]
serializer_7_1 = ActiveSupport::Cache::SerializerWithFallback[:marshal_7_1]
entry = ActiveSupport::Cache::Entry.new(Random.bytes(10_000), version: "123")
Benchmark.ips do |x|
x.report("dump 7.0") do
$dumped_7_0 = serializer_7_0.dump(entry)
end
x.report("dump 7.1") do
$dumped_7_1 = serializer_7_1.dump(entry)
end
x.compare!
end
Benchmark.ips do |x|
x.report("load 7.0") do
serializer_7_0.load($dumped_7_0)
end
x.report("load 7.1") do
serializer_7_1.load($dumped_7_1)
end
x.compare!
end
```
```
Warming up --------------------------------------
dump 7.0 5.482k i/100ms
dump 7.1 10.987k i/100ms
Calculating -------------------------------------
dump 7.0 73.966k (± 6.9%) i/s - 367.294k in 5.005176s
dump 7.1 127.193k (±17.8%) i/s - 615.272k in 5.081387s
Comparison:
dump 7.1: 127192.9 i/s
dump 7.0: 73966.5 i/s - 1.72x (± 0.00) slower
Warming up --------------------------------------
load 7.0 7.425k i/100ms
load 7.1 26.237k i/100ms
Calculating -------------------------------------
load 7.0 85.574k (± 1.7%) i/s - 430.650k in 5.034065s
load 7.1 264.877k (± 1.6%) i/s - 1.338M in 5.052976s
Comparison:
load 7.1: 264876.7 i/s
load 7.0: 85573.7 i/s - 3.10x (± 0.00) slower
```
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
At GitHub we're trying to switch over from the GitHub
activerecord-trilogy-adapter to the one in Rails. One difference between
the adapters is that the GitHub one did not have the `#select_all`
method that abandons multi results.
This wouldn't be a problem, except we also have more aggressive query
retries. This led to an issue because the `super` call in `#select_all`
leads to a nested `#with_raw_connection` call. If we experienced
transient connection errors during the query, the inner
`#with_raw_connection` could reconnect leaving the outer block with a
closed connection. In that case, calling `#more_results_exist?` results
in a `Trilogy::ConnectionClosed Attempted to use closed connection`.
That's the scenario described in this comment
7e735451a7/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb (L981-L983)
Moving the call to `super` out of the `#with_raw_connection` block will
avoid the nesting and fix the issue. We prefer that solution over
detecting nested `#with_raw_connections` and preventing reconnects when
nested, since in our case we actually do want to reconnect and retry.
We're hoping this change is ok even though it's not strictly necessary
for general Rails users. We don't believe it has any downsides.
We did not add a test for this case since it's not possible to occur in
Rails itself. It's specific to custom GitHub code.
Co-authored-by: Daniel Colson <composerinteralia@github.com>
This silences a "ActiveSupport::MessagePack requires the msgpack gem"
warning that can occur when using a non-`:message_pack` serializer and
trying to detect the format of a serialized cache entry.
`silence_warnings` is a blunt instrument, but this `require` is only for
decoding legacy cache entries that were encoded using `:message_pack`,
if any. (i.e. If `:message_pack` is currently being used as a
serializer, then `SerializerWithFallback::[]` should have already loaded
`active_support/message_pack`.) Therefore, it seems less hazardous to
inadvertently silence other warnings that may occur when loading the
file.
Co-authored-by: Alex Ghiculescu <alex@tanda.co>
This commit adds support for `:message_pack` as an option for
`config.active_support.cache_format_version`.
Cache entries written using the `6.1` or `7.0` formats can be read when
using the `:message_pack` format. Additionally, cache entries written
using the `:message_pack` format can now be read when using the `6.1` or
`7.0` format. These behaviors makes it easy to migrate between formats
without invalidating the entire cache.
This test was added in 8f5095a to ensure that there was coverage
for `AbstractMysqlAdapter#execute`, but 63c0d6b refactored `#execute`
to be defined at the Abstract adapter level, and rely on concrete MySQL
adapters to implement `#raw_execute`.
This test won't pass without having the `ExampleMysqlAdapter` implement
`#raw_execute`, but this test is obsolete now that AbstractMysql2Adapter
doesn't implement `#execute` directly.
This deals with a problem introduced in #7743ab95b8e15581f432206245c691434a3993d1a751b9d451170956d59457a9R8
that was preventing query `Class` serialized attributes. Duplicating the original
`Class` argument generates an anonymous class that can't be serialized as YAML.
This change makes query attributes hasheable based on their frozen casted values
to prevent the problem.
This solution is based on an idea by @matthewd from https://github.com/rails/rails/issues/47338#issuecomment-1424402777.
Ruby 3.1 added `intersects?` which is equivalent to `(a & b).any?`. Rubocop added a corresponding cop, `Style/ArrayIntersect`, which transforms the old style to use `intersect?`. Unfortunately as `intersects?` is not delegated on `CollectionProxy`, this leads to false positives that need to be disabled for no good reason other than the fact the method isn't delegated.
This PR add delegation of `intersects?` to `Relation` which fixes this.