norman/friendly_id — Issue #180: Duplicate Key Value problems by using history and parallel processing
Author: gedankenstuecke
Created: 2011-11-08 18:56:44+00:00
State: open

Hi,
we're using friendly_id (4.0.0.beta14) with the history-feature enabled on our project, together with resque for managing queues and found that some jobs are failing with *PGError: ERROR: duplicate key value violates unique constraint "index_friendly_id_slugs_on_slug_and_sluggable_type" : INSERT INTO "friendly_id_slugs" ("slug", "sluggable_id", "sluggable_type", "created_at") VALUES ('rs696974', 1676331, 'Snp', '2011-11-08 17:04:02.299340') RETURNING "id"*

This happened as soon as an object was supposed to be saved in this resque-job. After a bit of bug hunting I found the problem:

Some actions in our application trigger multiple jobs on the same object that – if the queue is empty enough – are worked on at the same time as we have multiple resque-workers (with different queues) running in parallel. By chance it can happen that 2 jobs trigger the save-action at more or less the same time. This is fine with Rails/Postgres in general, as it blocks such stuff.

But it is not fine with friendly_id as it does not perform updates on the friendly_id_slugs-table if the combination of (object-ID & slug) are already in the table. Instead the old entry gets deleted and a new one is created:

```
def build_slug
return unless should_generate_new_friendly_id?
# Allow reversion back to a previously used slug
slugs.where(:slug => friendly_id).delete_all
slugs.build :slug => friendly_id
end
```
See https://github.com/norman/friendly_id/blob/master/lib/friendly_id/history.rb#L71

If those two save-actions are performed in fast succession it can happen that *worker A* has already performed deleting and creating the new slug-entry while *worker B* had nothing to delete but went on trying to create the new entry. Which then fails as *worker A* was some milliseconds faster.

### Real world example:
The timestamp of an entry in the friendly_id_slugs-table: 2011-11-08 17:04:02.151681
The timestamp of the same entry where the save-transaction failed: 2011-11-08 17:04:02.299340

## Possible solution?
None that are really obvious, but what about updating entries instead of deleting/creating anew?