Skip to content

Commit

Permalink
Avoid method call if @transaction_state is not finalized
Browse files Browse the repository at this point in the history
Method call in Ruby is a bit slow.

This makes attribute access 10% faster by avoiding method call
(`sync_with_transaction_state`).

Before (96cf7e0):

```
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
```
  • Loading branch information
kamipo committed Apr 21, 2019
1 parent 96cf7e0 commit f9326e5
Show file tree
Hide file tree
Showing 7 changed files with 14 additions and 14 deletions.
Expand Up @@ -46,7 +46,7 @@ module BeforeTypeCast
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
def read_attribute_before_type_cast(attr_name)
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@attributes[attr_name.to_s].value_before_type_cast
end

Expand All @@ -61,7 +61,7 @@ def read_attribute_before_type_cast(attr_name)
# task.attributes_before_type_cast
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
def attributes_before_type_cast
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@attributes.values_before_type_cast
end

Expand All @@ -73,7 +73,7 @@ def attribute_before_type_cast(attribute_name)
end

def attribute_came_from_user?(attribute_name)
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@attributes[attribute_name].came_from_user?
end
end
Expand Down
4 changes: 2 additions & 2 deletions activerecord/lib/active_record/attribute_methods/dirty.rb
Expand Up @@ -157,12 +157,12 @@ def attributes_in_database

private
def mutations_from_database
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
super
end

def mutations_before_last_save
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
super
end

Expand Down
2 changes: 1 addition & 1 deletion activerecord/lib/active_record/attribute_methods/read.rb
Expand Up @@ -39,7 +39,7 @@ def read_attribute(attr_name, &block)
# This method exists to avoid the expensive primary_key check internally, without
# breaking compatibility with the read_attribute API
def _read_attribute(attr_name, &block) # :nodoc
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@attributes.fetch_value(attr_name.to_s, &block)
end

Expand Down
4 changes: 2 additions & 2 deletions activerecord/lib/active_record/attribute_methods/write.rb
Expand Up @@ -43,14 +43,14 @@ def write_attribute(attr_name, value)
# This method exists to avoid the expensive primary_key check internally, without
# breaking compatibility with the write_attribute API
def _write_attribute(attr_name, value) # :nodoc:
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@attributes.write_from_user(attr_name.to_s, value)
value
end

private
def write_attribute_without_type_cast(attr_name, value)
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@attributes.write_cast_value(attr_name.to_s, value)
value
end
Expand Down
2 changes: 1 addition & 1 deletion activerecord/lib/active_record/core.rb
Expand Up @@ -466,7 +466,7 @@ def freeze

# Returns +true+ if the attributes hash has been frozen.
def frozen?
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@attributes.frozen?
end

Expand Down
6 changes: 3 additions & 3 deletions activerecord/lib/active_record/persistence.rb
Expand Up @@ -423,20 +423,20 @@ def _substitute_values(values)
# Returns true if this object hasn't been saved yet -- that is, a record
# for the object doesn't exist in the database yet; otherwise, returns false.
def new_record?
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@new_record
end

# Returns true if this object has been destroyed, otherwise returns false.
def destroyed?
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@destroyed
end

# Returns true if the record is persisted, i.e. it's not a new record and it was
# not destroyed, otherwise returns false.
def persisted?
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
!(@new_record || @destroyed)
end

Expand Down
4 changes: 2 additions & 2 deletions activerecord/lib/active_record/transactions.rb
Expand Up @@ -365,7 +365,7 @@ def with_transaction_returning_status
status = nil
self.class.transaction do
unless has_transactional_callbacks?
sync_with_transaction_state
sync_with_transaction_state if @transaction_state&.finalized?
@transaction_state = self.class.connection.transaction_state
end
remember_transaction_record_state
Expand Down Expand Up @@ -479,7 +479,7 @@ def has_transactional_callbacks?
# the TransactionState, and rolls back or commits the Active Record object
# as appropriate.
def sync_with_transaction_state
if (transaction_state = @transaction_state)&.finalized?
if transaction_state = @transaction_state
if transaction_state.fully_committed?
force_clear_transaction_record_state
elsif transaction_state.committed?
Expand Down

0 comments on commit f9326e5

Please sign in to comment.