ActsAsInsertOrUpdate
Problem
With high volume Rails applications, entities with unique constraints are expensive and error prone to create/update. ActsAsInsertOrUpdate helps solve that problem (if you’re using MySQL), by leveraging the “INSERT … ON DUPLICATE KEY UPDATE” functionality.
Scenario
Lets say you have a Person, and Entity, and a Rating. Each user can rate each entity only once, and if they re-rate the entity, it should update the value.
class Entity < ActiveRecord::Base has_many :ratings end class Person < ActiveRecord::Base has_many :ratings end class Rating < ActiveRecord::Base belongs_toerson belongs_to :Entity end
Here is the table that back’s Rating. Notice the Unique Key constraint on (entity_id, person_id).
CREATE TABLE `ratings` ( `id` int(11) NOT NULL auto_increment, `rating` tinyint(4) default '0', `person_id` int(11) default NULL, `entity_id` int(11) default NULL, `created_at` datetime default NULL, `updated_at` datetime default NULL, PRIMARY KEY (`id`), UNIQUE KEY `index_ratings_on_entity_id_and_person_id` (`entity_id`,`person_id`), )
Previously, the logic would be something like:
- 1) Check if a rating exists for the User + Entity
- 2) If so, update
- 3) If not, insert
- 4) rescue the insert in case there is a unqiue constraint error
- 5) retrieve the record (and/or update with the new rating)
If the table is MyISAM, Steps 1-5 aren’t transactionally safe. If you’re using InnoDB, and experience heavy volumes of traffic, you’re prone to Deadlock’s. This is even more of a concern is the unique entity is shared across multiple users, as seen with a recent client of ours.
Solution:
class Rating < ActiveRecord::Base belongs_toerson belongs_to :Entity acts_as_insert_or_update :field_to_update => "rating" end
Now Steps 1-5 above become, just one. Rating.create(..)
In the background, ActsAsInsertOrUpdate overwrites the implementation of ActionRecord:Base#create, to leverage an often unsed feature of MySQL called INSERT … ON DUPLICATE KEY UPDATE. As configured above, if a duplicate record is found for the unique constraint, the rating field will be updated with the new value.
Caution
This is a brute force hack on ActiveRecord::Base#create. Use at your own risk.
Code
Waiting for a rubyforge account. Will post more info soon.

FWIW, this is called “Upsert” by Oracle and “Merge” by Microsoft. I think both of those verbs are better than the slightly tedious “ActsAsInsertOrUpdate”.
Sure… ActsAsUpsert didnt sound as cool though… Its a play on all the crazy ActsAsChicken, ActsAsTaggable, ActsAsCool, etc names used in the Rails community… The name was supposed to be funny..
maybe use github.com for this patch?
Extremely useful, even with the somewhat dire warning!
FWIW: we were using MySQL’s "REPLACE INTO…" command to achieve the same net effect:
http://dev.mysql.com/doc/refman/5.0/en/replace.html
… but the "INSERT … ON DUPLICATE KEY UPDATE" functionality definitely seems a better way of doing things.
There is also REPLACE. There are differences:
http://www.mysqlperformanceblog.com/2007/01/18/insert-on-duplicate-key-update-and-replace-into/
Is this plugin available somewheres? I can’t seem to track down the actual files and I’d lvoe to take a look at how this works.
Thanks! J
This plugin would be really helpful. Any sense of when it will become publicly available?