| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
| |
In most cases it works now without explicit require because it's accidentally required through
active_support/core_ext/date_and_time/calculations.rb where we still call `try`,
but that would stop working if we changed the Calculations implementation and remove the require call there.
|
|
|
|
| |
We're already running Performance/RegexpMatch cop, but it seems like the cop is not always =~ justice
|
|
|
|
|
|
|
|
|
|
|
| |
We sometimes say "✂️ newline after `private`" in a code review (e.g.
https://github.com/rails/rails/pull/18546#discussion_r23188776,
https://github.com/rails/rails/pull/34832#discussion_r244847195).
Now `Layout/EmptyLinesAroundAccessModifier` cop have new enforced style
`EnforcedStyle: only_before` (https://github.com/rubocop-hq/rubocop/pull/7059).
That cop and enforced style will reduce the our code review cost.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This partly reverts the effect of d1107f4d.
d1107f4d makes `touch` tracks the mutation whether the `touch` is
occurred by explicit or not.
Existing apps expects that the previous changes tracks only the changes
which is explicit action by users.
I'd revert the implicit `touch` mutation tracking since I'd not like to
break existing apps.
Fixes #36219.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
If we want to get alias resolved attribute finally, we can use
`attribute_alias` directly.
For that purpose, avoiding redundant `attribute_alias?` makes alias
attribute access 40% faster.
https://gist.github.com/kamipo/e427f080a27b46f50bc508fae3612a0e
Before (2c0729d8):
```
Warming up --------------------------------------
user['id'] 102.668k i/100ms
user['new_id'] 80.660k i/100ms
user['name'] 99.368k i/100ms
user['new_name'] 81.626k i/100ms
Calculating -------------------------------------
user['id'] 1.431M (± 4.0%) i/s - 7.187M in 5.031985s
user['new_id'] 1.042M (± 4.2%) i/s - 5.243M in 5.039858s
user['name'] 1.406M (± 5.6%) i/s - 7.055M in 5.036743s
user['new_name'] 1.074M (± 3.6%) i/s - 5.387M in 5.024152s
```
After (this change):
```
Warming up --------------------------------------
user['id'] 109.775k i/100ms
user['new_id'] 103.303k i/100ms
user['name'] 105.988k i/100ms
user['new_name'] 99.618k i/100ms
Calculating -------------------------------------
user['id'] 1.520M (± 6.7%) i/s - 7.574M in 5.011496s
user['new_id'] 1.485M (± 6.2%) i/s - 7.438M in 5.036252s
user['name'] 1.538M (± 5.4%) i/s - 7.737M in 5.049765s
user['new_name'] 1.516M (± 4.6%) i/s - 7.571M in 5.007293s
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I've realized that `user.id` is 20% slower than `user.name` in the
benchmark (https://github.com/rails/rails/pull/35987#issuecomment-483882480).
The reason that performance difference is that `self.class.primary_key`
method call is a bit slow.
Avoiding that method call will make almost attribute access faster and
`user.id` will be completely the same performance with `user.name`.
Before (02b5b8cb):
```
Warming up --------------------------------------
user.id 140.535k i/100ms
user['id'] 96.549k i/100ms
user.name 158.110k i/100ms
user['name'] 94.507k i/100ms
user.changed? 19.003k i/100ms
user.saved_changes? 25.404k i/100ms
Calculating -------------------------------------
user.id 2.231M (± 0.9%) i/s - 11.243M in 5.040066s
user['id'] 1.310M (± 1.3%) i/s - 6.565M in 5.012607s
user.name 2.683M (± 1.2%) i/s - 13.439M in 5.009392s
user['name'] 1.322M (± 0.9%) i/s - 6.615M in 5.003239s
user.changed? 201.999k (±10.9%) i/s - 1.007M in 5.091195s
user.saved_changes? 258.214k (±17.1%) i/s - 1.245M in 5.007421s
```
After (this change):
```
Warming up --------------------------------------
user.id 158.364k i/100ms
user['id'] 106.412k i/100ms
user.name 158.644k i/100ms
user['name'] 107.518k i/100ms
user.changed? 19.082k i/100ms
user.saved_changes? 24.886k i/100ms
Calculating -------------------------------------
user.id 2.768M (± 1.1%) i/s - 13.936M in 5.034957s
user['id'] 1.507M (± 2.1%) i/s - 7.555M in 5.017211s
user.name 2.727M (± 1.5%) i/s - 13.643M in 5.004766s
user['name'] 1.521M (± 1.3%) i/s - 7.634M in 5.018321s
user.changed? 200.865k (±11.1%) i/s - 992.264k in 5.044868s
user.saved_changes? 269.652k (±10.5%) i/s - 1.344M in 5.077972s
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Method call in Ruby is a bit slow.
This makes attribute access 10% faster by avoiding method call
(`sync_with_transaction_state`).
Before (96cf7e0e):
```
Warming up --------------------------------------
user.id 131.291k i/100ms
user['id'] 91.786k i/100ms
user.name 151.605k i/100ms
user['name'] 92.664k i/100ms
user.changed? 17.772k i/100ms
user.saved_changes? 23.909k i/100ms
Calculating -------------------------------------
user.id 1.988M (± 7.0%) i/s - 9.978M in 5.051474s
user['id'] 1.155M (± 5.8%) i/s - 5.783M in 5.022672s
user.name 2.450M (± 4.3%) i/s - 12.280M in 5.021234s
user['name'] 1.263M (± 2.1%) i/s - 6.394M in 5.066638s
user.changed? 175.070k (±13.3%) i/s - 853.056k in 5.011555s
user.saved_changes? 259.114k (±11.8%) i/s - 1.267M in 5.001260s
```
After (this change):
```
Warming up --------------------------------------
user.id 137.625k i/100ms
user['id'] 96.054k i/100ms
user.name 156.379k i/100ms
user['name'] 94.795k i/100ms
user.changed? 18.172k i/100ms
user.saved_changes? 24.337k i/100ms
Calculating -------------------------------------
user.id 2.201M (± 0.5%) i/s - 11.010M in 5.002955s
user['id'] 1.320M (± 1.0%) i/s - 6.628M in 5.021293s
user.name 2.677M (± 1.6%) i/s - 13.449M in 5.024399s
user['name'] 1.314M (± 1.8%) i/s - 6.636M in 5.051444s
user.changed? 190.588k (±11.1%) i/s - 944.944k in 5.065848s
user.saved_changes? 262.782k (±12.1%) i/s - 1.290M in 5.028080s
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Currently the rollback only restores primary key value, `new_record?`,
`destroyed?`, and `frozen?`. Since the `save` clears current dirty
attribute states, retrying save after rollback will causes no change
saved if partial writes is enabled (by default).
This makes `remember_transaction_record_state` remembers original values
then restores dirty attribute states after rollback.
Fixes #15018.
Fixes #30167.
Fixes #33868.
Fixes #33443.
Closes #33444.
Closes #34504.
|
|
|
|
|
|
|
|
|
|
| |
Before this fix, `touch` only clears dirty tracking for touched
attributes, doesn't track saved (touched) changes.
This fixes that tracks saved changes and carry over remaining changes.
Fixes #33429.
Closes #34306.
|
|
|
|
|
|
| |
Most of the time, these methods are called from actual methods defined
from columns in the schema, not from method_missing, so the current
wording is misleading.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Currently, although using both dirty tracking (ivar backed and
attributes backed) on one model is not supported (doesn't fully work at
least), both dirty tracking are being performed, that is very slow.
As long as attributes backed dirty tracking is used, ivar backed dirty
tracking should not need to be performed.
I've refactored to extract new `ForcedMutationTracker` which only tracks
`force_change` to be performed for ivar backed dirty tracking, that
makes dirty tracking on Active Record 2x ~ 30x faster.
https://gist.github.com/kamipo/971dfe0891f0fe1ec7db8ab31f016435
Before:
```
Warming up --------------------------------------
changed? 4.467k i/100ms
changed 5.134k i/100ms
changes 3.023k i/100ms
changed_attributes 4.358k i/100ms
title_change 3.185k i/100ms
title_was 3.381k i/100ms
Calculating -------------------------------------
changed? 42.197k (±28.5%) i/s - 187.614k in 5.050446s
changed 50.481k (±16.0%) i/s - 246.432k in 5.045759s
changes 30.799k (± 7.2%) i/s - 154.173k in 5.030765s
changed_attributes 51.530k (±14.2%) i/s - 252.764k in 5.041106s
title_change 44.667k (± 9.0%) i/s - 222.950k in 5.040646s
title_was 44.635k (±16.6%) i/s - 216.384k in 5.051098s
```
After:
```
Warming up --------------------------------------
changed? 24.130k i/100ms
changed 13.503k i/100ms
changes 6.511k i/100ms
changed_attributes 9.226k i/100ms
title_change 48.221k i/100ms
title_was 96.060k i/100ms
Calculating -------------------------------------
changed? 245.478k (±16.1%) i/s - 1.182M in 5.015837s
changed 157.641k (± 4.9%) i/s - 796.677k in 5.066734s
changes 70.633k (± 5.7%) i/s - 358.105k in 5.086553s
changed_attributes 95.155k (±13.6%) i/s - 470.526k in 5.082841s
title_change 566.481k (± 3.5%) i/s - 2.845M in 5.028852s
title_was 1.487M (± 3.9%) i/s - 7.493M in 5.046774s
```
|
|
|
|
|
|
|
|
|
|
| |
typecasted value
change the line to check an attribute has user-defined type
ref: https://github.com/rails/rails/pull/35320#discussion_r257924552
check query attribute method is working when given value does not respond to to_i method
|
|\
| |
| | |
Unify _read_attribute definition to use &block
|
| |
| |
| |
| |
| |
| |
| |
| | |
Thanks to ko1, passing block parameter to another method is
significantly optimized in Ruby 2.5.
https://bugs.ruby-lang.org/issues/14045
Thus we no longer need to keep this ugly hack.
|
| |
| |
| |
| | |
[ci skip]
|
| |
| |
| |
| | |
Allow aliased attributes to be used in `#update_columns` and `#update`.
|
| |
| |
| |
| |
| |
| |
| |
| |
| | |
When `attr_name` is passed as a symbol, it's currently converted to a
string by `attribute_alias?`, and potentially also `attribute_alias`,
as well as by the `read_attribute`/`write_attribute` method itself.
By converting `attr_name` to a string up front, the extra allocations
related to attribute aliases can be avoided.
|
| |
| |
| |
| |
| |
| |
| |
| | |
Ruby uses the original method name, so will show the __temp__ method
name in the backtrace. However, in the common case the method name
is compatible with the `def` keyword, so we can avoid the __temp__
method name in that case to improve the name shown in backtraces
or TracePoint#method_id.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Since Rails 6.0 will support Ruby 2.4.1 or higher
`# frozen_string_literal: true` magic comment is enough to make string object frozen.
This magic comment is enabled by `Style/FrozenStringLiteralComment` cop.
* Exclude these files not to auto correct false positive `Regexp#freeze`
- 'actionpack/lib/action_dispatch/journey/router/utils.rb'
- 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb'
It has been fixed by https://github.com/rubocop-hq/rubocop/pull/6333
Once the newer version of RuboCop released and available at Code Climate these exclude entries should be removed.
* Replace `String#freeze` with `String#-@` manually if explicit frozen string objects are required
- 'actionpack/test/controller/test_case_test.rb'
- 'activemodel/test/cases/type/string_test.rb'
- 'activesupport/lib/active_support/core_ext/string/strip.rb'
- 'activesupport/test/core_ext/string_ext_test.rb'
- 'railties/test/generators/actions_test.rb'
|
| |
| |
| |
| |
| |
| |
| | |
It should be done only once in `Persistence` module.
https://github.com/rails/rails/blob/c83e30da27eafde79164ecb376e8a28ccc8d841f/activerecord/lib/active_record/persistence.rb#L721
https://github.com/rails/rails/blob/c83e30da27eafde79164ecb376e8a28ccc8d841f/activerecord/lib/active_record/persistence.rb#L740
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
`changes_applied` should be called before continuing around callback
chain. Otherwise the mutation tracker returns old value for methods like
`changed`? or `id_in_database` in around callbacks. Also methods depend
on `id_in_database`, like `update_column`, are not working in
`around_create` callbacks.
```
class Foo < ActiveRecord::Base
around_create :around_create_callback
def around_create_callback
...
yield
p id_in_database # => nil
update_column(:generated_column, generate_value) # silently fails
end
...
end
```
|
|/
|
|
|
|
| |
The first thing this method does is run on the argument. This change passes
in a string so we don't allocate a bunch of unnecessary extra strings by
calling to_s on a symbol over and over.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`after_initialize`
`becomes` creates new object and copies attributes from the receiver. If
new object has mutation tracker which is created in `after_initialize`,
it should be cleared since it is for discarded attributes.
But if the receiver doesn't have mutation tracker yet, it will not be
cleared properly.
It should be cleared regardless of whether the receiver has mutation
tracker or not.
Fixes #32867.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Previously the documentation for the newly introduced (in 5.1) AR::Dirty
methods was misleading, as it stated the the new methods were aliases
for the old methods. This was false, and caused confusion when the
differences in their implementation became apparent.
This change attempts to describe the behaviour of these methods more
accurately, also noting when they are likely to be useful (i.e. before
or after saving a record).
This change also makes minor updates to consistently format the
documentation of this API, in accordance with the API Documentation
Guidelines.
|
|
|
|
|
| |
This commit fixes all references in the codebase missing a trailing :,
which causes the nodoc not to actually work :) [skip ci]
|
|
|
|
|
|
|
|
|
|
| |
`changed_attribute_names_to_save` is called in `keys_for_partial_write`,
which is called on every save when partial writes are enabled.
We can avoid generating the full changes hash by asking the mutation
tracker for just the names of the changed attributes. At minimum this
saves one array allocation per attribute, but will also avoid calling
`Attribute#original_value` which is expensive for serialized attributes.
|
| |
|
|
|
|
| |
This reverts commit a19e91f0fab13cca61acdb1f33e27be2323b9786.
|
|
|
|
|
| |
https://bugs.ruby-lang.org/issues/12752
https://ruby-doc.org/core-2.4.0/String.html#method-i-unpack1
|
|
|
|
|
| |
Some places we can't remove because Ruby still don't have a method
equivalent to strip_heredoc to be called in an already existent string.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
#30985 caused `object.save` performance regression since calling
`changes` in `changes_applied` is very slow.
We don't need to call the expensive method in `changes_applied` as long
as `@attributes` is tracked by mutation tracker.
https://gist.github.com/kamipo/1a9f4f3891803b914fc72ede98268aa2
Before:
```
Warming up --------------------------------------
create_string_columns
73.000 i/100ms
Calculating -------------------------------------
create_string_columns
722.256 (± 5.8%) i/s - 3.650k in 5.073031s
```
After:
```
Warming up --------------------------------------
create_string_columns
96.000 i/100ms
Calculating -------------------------------------
create_string_columns
950.224 (± 7.7%) i/s - 4.800k in 5.084837s
```
|
|
|
|
|
| |
Use these to back the attributes API. Stop automatically including
ActiveModel::Dirty in ActiveModel::Attributes, and make it optional.
|
|
|
|
| |
This basically reverts 9d4f79d3d394edb74fa2192e5d9ad7b09ce50c6d
|
|\
| |
| | |
activerecord: Remove a redundant mutation tracker
|
| |
| |
| |
| |
| |
| |
| | |
The extra mutation tracker was needed in Rails 5.1 to preserve the
old behaviour of `changes`, but now there is no difference
between `changes` and `changes_to_save`, so `@mutation_tracker`
can be removed.
|
|/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Benchmark Script:
```
require 'active_record'
require 'benchmark/ips'
ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL'))
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define do
create_table :users, force: true do |t|
t.string :name, :email
t.integer :topic_id
t.timestamps null: false
end
create_table :topics, force: true do |t|
t.string :title
t.timestamps null: false
end
end
attributes = {
name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
email: 'foobar@email.com'
}
class Topic < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :topic
end
100.times do
User.create!(attributes)
end
users = User.first(50)
Topic.create!(title: 'This is a topic', users: users)
Benchmark.ips do |x|
x.config(time: 10, warmup: 5)
x.report("preload") do
User.includes(:topic).all.to_a
end
end
```
Before:
```
Calculating -------------------------------------
preload 40.000 i/100ms
-------------------------------------------------
preload 407.962 (± 1.5%) i/s - 4.080k
```
After:
```
alculating -------------------------------------
preload 43.000 i/100ms
-------------------------------------------------
preload 427.567 (± 1.6%) i/s - 4.300k
```
|
| |
|
| |
|
|
|
|
|
|
| |
`write_attribute_without_type_cast` is defined as a private method in
`AttributeMethods::Write`, but `AttributeMethods::Dirty` overrode it as
a public method. It should be kept the original visibility.
|
|
|
|
|
|
|
| |
We already have a test case for `serialize` with a custom coder in
`PostgresqlHstoreTest`.
https://github.com/rails/rails/blob/v5.1.3/activerecord/test/cases/adapters/postgresql/hstore_test.rb#L316-L335
|
|
|
|
|
| |
This module has behavior that is not present in `ActiveModel::Dirty`,
which is intended to be public API.
|
|\
| |
| | |
Do not let use `serialize` on native JSON/array column
|
| | |
|
|\ \
| |/
|/| |
Sync transaction state when accessing primary key
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
If a record is modified inside a transaction, it must check the outcome
of that transaction before accessing any state which would no longer be
valid if it was rolled back.
For example, consider a new record that was saved inside a transaction
which was later rolled back: it should be restored to its previous state
so that saving it again inserts a new row into the database instead of
trying to update a row that no longer exists.
The `id` and `id=` methods defined on the PrimaryKey module implement
this correctly, but when a model uses a custom primary key, the reader
and writer methods for that attribute must check the transaction state
too. The `read_attribute` and `write_attribute` methods also need to
check the transaction state when accessing the primary key.
|
| | |
|
| | |
|
| | |
|
|/ |
|
|\
| |
| | |
Improve the performance of writing attributes
|