- Notifications
You must be signed in to change notification settings - Fork0
License
agilie/activerecord-delay_touching
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Note: this version requires ActiveRecord 4.2 or higher. To use ActiveRecord 3.2 through 4.1, use the branchhttps://github.com/godaddy/activerecord-delay_touching/tree/pre-activerecord-4.2.
Batch up your ActiveRecord "touch" operations for better performance.
When you want to invalidate a cache in Rails, you usetouch: true
. But whenyou modify a bunch of records that allbelong_to
the same owning record, that recordwill be touched N times. It's incredibly slow.
With this gem, alltouch
operations are consolidated into as few databaseround-trips as possible. Instead of N touches you get 1 touch.
Add this line to your application's Gemfile:
gem 'activerecord-delay_touching'
And then execute:
$ bundle
Or install it yourself:
$ gem install activerecord-delay_touching
The setup:
class Person < ActiveRecord::Base has_many :pets accepts_nested_attributes_for :petsendclass Pet < ActiveRecord::Base belongs_to :person, touch: trueend
Withoutdelay_touching
, this simpleupdate
in the controller calls@person.touch
N times, where N is the number of pets that were updatedvia nested attributes. That's N-1 unnecessary round-trips to the database:
class PeopleController < ApplicationController def update ... # @person.update(person_params) ... endend# SQL (0.1ms) UPDATE "people" SET "updated_at" = '2014-07-09 19:48:07.137158' WHERE "people"."id" = 1# SQL (0.1ms) UPDATE "people" SET "updated_at" = '2014-07-09 19:48:07.138457' WHERE "people"."id" = 1# SQL (0.1ms) UPDATE "people" SET "updated_at" = '2014-07-09 19:48:07.140088' WHERE "people"."id" = 1
Withdelay_touching
, @person is touched only once:
ActiveRecord::Base.delay_touching do @person.update(person_params)end# SQL (0.1ms) UPDATE "people" SET "updated_at" = '2014-07-09 19:48:07.140088' WHERE "people"."id" = 1
In the following example, a person gives his pet to another person. ActiveRecordautomatically touches the old person and the new person. Withdelay_touching
,this will only make asingle round-trip to the database, settingupdated_at
for all Person records in a single SQL UPDATE statement. Not a big deal when there areonly two touches, but when you're updating records en masse and have a cascadeof hundreds touches, it really is a big deal.
class Pet < ActiveRecord::Base belongs_to :person, touch: true def give(to_person) ActiveRecord::Base.delay_touching do self.person = to_person save! # touches old person and new person in a single SQL UPDATE. end endend
Whendelay_touch
runs through and touches everything, it captures additionaltouch
calls that might be called as side-effects. (E.g., inafter_touch
handlers.) Then it makes a second pass, batching up those touches as well.
It keeps doing this until there are no more touches, or until the sun swallowsup the earth. Whichever comes first.
Things to note:
after_touch
callbacks are still fired for every instance, but not until the block is exited.And they won't happen in the same order as they would if you weren't batching up your touches.- If you call person1.touch and then person2.touch, and they are two separate instanceswith the same id, only person1's
after_touch
handler will be called.
- Fork it (https://github.com/godaddy/activerecord-delay_touching/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
About
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Languages
- Ruby100.0%