1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Avoid creating extra relation and build_arel in _create_record and _update_record (#29999)

Currently `_create_record` and `_update_record` in `Persistence` are
creating extra `unscoped` and calling `build_arel` in the relation. But
`compile_insert` and `compile_update` can be done without those
expensive operation for `SelectManager` creation. So I moved the
implementation to `Persistence` to avoid creating extra relation and
refactored to avoid calling `build_arel`.

https://gist.github.com/kamipo/8ed73d760112cfa5f6263c9413633419

Before:

```
Warming up --------------------------------------
      _update_record   150.000  i/100ms
Calculating -------------------------------------
      _update_record      1.548k (±12.3%) i/s -      7.650k in   5.042603s
```

After:

```
Warming up --------------------------------------
      _update_record   201.000  i/100ms
Calculating -------------------------------------
      _update_record      2.002k (±12.8%) i/s -      9.849k in   5.027681s
```

30% faster for STI classes.
This commit is contained in:
Ryuta Kamizono 2017-11-17 03:12:57 +09:00 committed by GitHub
parent 8bfa617e99
commit bbae710a40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 63 deletions

View file

@ -165,6 +165,38 @@ module ActiveRecord
where(primary_key => id_or_array).delete_all
end
def _insert_record(values) # :nodoc:
primary_key_value = nil
if primary_key && Hash === values
arel_primary_key = arel_attribute(primary_key)
primary_key_value = values[arel_primary_key]
if !primary_key_value && prefetch_primary_key?
primary_key_value = next_sequence_value
values[arel_primary_key] = primary_key_value
end
end
if values.empty?
im = arel_table.compile_insert(connection.empty_insert_statement_value)
im.into arel_table
else
im = arel_table.compile_insert(_substitute_values(values))
end
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
end
def _update_record(values, id, id_was) # :nodoc:
bind = predicate_builder.build_bind_attribute(primary_key, id_was || id)
um = arel_table.where(
arel_attribute(primary_key).eq(bind)
).compile_update(_substitute_values(values), primary_key)
connection.update(um, "#{self} Update")
end
private
# Called by +instantiate+ to decide which class to use for a new
# record instance.
@ -174,6 +206,13 @@ module ActiveRecord
def discriminate_class_for_record(record)
self
end
def _substitute_values(values)
values.map do |attr, value|
bind = predicate_builder.build_bind_attribute(attr.name, value)
[attr, bind]
end
end
end
# Returns true if this object hasn't been saved yet -- that is, a record
@ -671,7 +710,7 @@ module ActiveRecord
rows_affected = 0
@_trigger_update_callback = true
else
rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database
rows_affected = self.class._update_record(attributes_values, id, id_in_database)
@_trigger_update_callback = rows_affected > 0
end
@ -685,7 +724,7 @@ module ActiveRecord
def _create_record(attribute_names = self.attribute_names)
attributes_values = arel_attributes_with_values_for_create(attribute_names)
new_id = self.class.unscoped.insert attributes_values
new_id = self.class._insert_record(attributes_values)
self.id ||= new_id if self.class.primary_key
@new_record = false

View file

@ -36,67 +36,6 @@ module ActiveRecord
reset
end
def insert(values) # :nodoc:
primary_key_value = nil
if primary_key && Hash === values
primary_key_value = values[values.keys.find { |k|
k.name == primary_key
}]
if !primary_key_value && klass.prefetch_primary_key?
primary_key_value = klass.next_sequence_value
values[arel_attribute(klass.primary_key)] = primary_key_value
end
end
im = arel.create_insert
im.into @table
substitutes = substitute_values values
if values.empty? # empty insert
im.values = Arel.sql(connection.empty_insert_statement_value)
else
im.insert substitutes
end
@klass.connection.insert(
im,
"#{@klass} Create",
primary_key || false,
primary_key_value,
nil,
)
end
def _update_record(values, id, id_was) # :nodoc:
substitutes = substitute_values values
scope = @klass.unscoped
if @klass.finder_needs_type_condition?
scope.unscope!(where: @klass.inheritance_column)
end
relation = scope.where(@klass.primary_key => (id_was || id))
um = relation
.arel
.compile_update(substitutes, @klass.primary_key)
@klass.connection.update(
um,
"#{@klass} Update",
)
end
def substitute_values(values) # :nodoc:
values.map do |arel_attr, value|
bind = predicate_builder.build_bind_attribute(arel_attr.name, value)
[arel_attr, bind]
end
end
def arel_attribute(name) # :nodoc:
klass.arel_attribute(name, table)
end