Module: IndexedSearch::Collision

Included in:
Word
Defined in:
lib/indexed_search/collision.rb

Overview

This implements a kind of optimistic duplication-prevention behavior without using any slow locking or anything like that.

The way it works is: certain column(s) get a unique index on them at the database level, so that you cannot create duplicate entries for them. If you were to try, you’d cause an ActiveRecord::RecordNotUnique error.

The expected scenario is like such: you look up and find the records you need, and if any aren’t found, you do a (relatively expensive) generation process for the new ones and insert them. You wrap this entire process in a special function that catches any insertion errors, and retries (including the looking-up of what is in there again).

This may sound similar to find_or_create_by_ already built into ActiveRecord, but it has one key difference: if your generation process is quite expensive, you only have to do it for missing records. If most of the time records should be found and not need to be generated, then this can give you significant time savings.

Other potential alternatives (each with other different drawbacks) include: MySQL’s INSERT ON DUPLICATE UPDATE and INSERT IGNORE and ANSI SQL’s MERGE

Defined Under Namespace

Classes: TooManyCollisionsException

Instance Method Summary collapse

Instance Method Details

#retrying_on_collision(retry_count = 0) ⇒ Object

usage like this:

extend IndexedSearch::Collision def find_or_create_foos_ids(foos)

retrying_on_collision do
  ids_hash = quick_find_current_foos_ids(foos)
  foos.collect { |foo| ids_hash[foo] || expensive_create_foo_and_return_id(foo) }
end

end

or something else similar… :)



61
62
63
64
65
66
67
# File 'lib/indexed_search/collision.rb', line 61

def retrying_on_collision(retry_count=0)
  yield
rescue ActiveRecord::RecordNotUnique
  raise TooManyCollisionsException.new('Too many db uniqueness collisions') if retry_count >= max_collision_retries
  sleep(rand(wait_time_seconds[retry_count]))
  retrying_on_collision(retry_count + 1) { yield }
end