Skip to content

Commit

Permalink
Only update changed attributes. Don't store values that are nil or ma…
Browse files Browse the repository at this point in the history
…tch the default.
  • Loading branch information
ileitch committed Dec 13, 2014
1 parent 6948e63 commit f89db70
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 8 deletions.
31 changes: 28 additions & 3 deletions benchmark/create.rb → benchmark/persistence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class User
include Modis::Model

attribute :name, :string
attribute :name, :string, default: 'Test'
attribute :age, :integer
attribute :percentage, :float
attribute :created_at, :timestamp
Expand All @@ -16,13 +16,17 @@ class User
index :name
end

def create_user
User.create!(name: 'Test', age: 30, percentage: 50.0, created_at: Time.now,
flag: true, array: [1, 2, 3], hash: { k: :v }, string_or_hash: "an string")
end

n = 10_000

Bench.run do |b|
b.report(:create) do
n.times do
User.create!(name: 'Test', age: 30, percentage: 50.0, created_at: Time.now,
flag: true, array: [1, 2, 3], hash: { k: :v }, string_or_hash: "an string")
create_user
end
end

Expand All @@ -47,4 +51,25 @@ class User
flag: true, array: [1, 2, 3], hash: { k: :v }, string_or_hash: "an string")
end
end

b.report(:update_without_changes) do
user = create_user
n.times do
user.update_attributes!(name: user.name, age: user.age)
end
end

b.report(:update_with_changes) do
user = create_user
n.times do |i|
user.update_attribute(:name, i.to_s)
end
end

b.report(:reload) do
user = create_user
n.times do
user.reload
end
end
end
25 changes: 21 additions & 4 deletions lib/modis/persistence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def create_or_update(args = {})
validate(args)
future = persist

if future && future.value == 'OK'
if future && (future == :unchanged || future.value == 'OK')
reset_changes
@new_record = false
new_record? ? add_to_index : update_index
Expand All @@ -170,9 +170,8 @@ def persist
run_callbacks :save do
run_callbacks callback do
run_callbacks "_internal_#{callback}" do
attrs = []
attributes.each { |k, v| attrs << k << coerce_for_persistence(v) }
future = redis.hmset(self.class.key_for(id), attrs)
attrs = coerced_attributes
future = attrs.any? ? redis.hmset(self.class.key_for(id), attrs) : :unchanged
end
end
end
Expand All @@ -181,6 +180,24 @@ def persist
future
end

def coerced_attributes # rubocop:disable Metrics/AbcSize
attrs = []

if new_record?
attributes.each do |k, v|
if (self.class.attributes[k][:default] || nil) != v
attrs << k << coerce_for_persistence(v)
end
end
else
changed_attributes.each do |k, _|
attrs << k << coerce_for_persistence(attributes[k])
end
end

attrs
end

def set_id
Modis.with_connection do |redis|
self.id = redis.incr("#{self.class.absolute_namespace}_id_seq")
Expand Down
3 changes: 2 additions & 1 deletion spec/persistence_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,10 @@ def test_before_save
model.reload
expect(model.name).to eq('Ian')

model.name = 'Kyle'
model.save!
record = redis.hgetall(key)
expect(record["name"]).to eq("\xA3Ian")
expect(record["name"]).to eq("\xA4Kyle")
end
end
end
Expand Down

0 comments on commit f89db70

Please sign in to comment.