I have the following models:
class Lyric < ActiveRecord::Base belongs_to :user belongs_to :song after_create :add_to_song end class Song < ActiveRecord::Base belongs_to :user has_many :lyrics end
The idea is that the user can add any number of songs for the song. If you enter lyrics for a new song that does not yet exist for this user, a new song is created for this user. This is achieved by calling the after_create 'add_to_song' method, which checks if the user has lyrics from this song:
def add_to_song sl = self.song_line # Check for adjacent songs prior_song = Song.where(:user_id => self.user.id, :title=> sl.title, :artist => sl.artist, :last_line => sl.linenum-1).first next_song = Song.where(:user_id => self.user.id, :title=> sl.title, :artist => sl.artist, :frst_line => sl.linenum+1).first # Case 1 - No existing song if !prior_song && !next_song song = Song.create!(:user_id => self.user.id, :length => 1, :title=> sl.title, :artist => sl.artist, :frst_line => sl.linenum, :last_line => sl.linenum ) self.update_attribute( :song_id, song.id ) # Case 2 - Lyric is between two songs -> merge songs elsif prior_song && next_song prior_song.absorb( next_song, self ) # Case 3 - Lyric is new first lyric of existing song elsif next_song next_song.expand( self ) # Case 4 - Lyric is new last lyric of existing song else prior_song.expand( self ) end end
The add_to_song method also combines two "songs" into one if the user adds a Lyric link. In other words, if the user has the first and third lines of a song, they are considered two different songs until she adds the second line of the same song.
Problem
When a user adds several texts from the same song at the same time (choosing several of them from the search results), the race condition is sometimes found in MySQL, and two song compositions are created for the same song, although the lyrics adjacent to each other should be combined in one "Song". (The unfortunate result of this is that the lyrics are presented in the correct order.)
I read endless posts about optimistic and pessimistic blocking, etc., and tried various options, but it seemed I could not get rid of this problem. It seems that locking the entire Song table too much every time the user creates lyrics.
Is this the only way to prevent this? (This seems to be a huge success for performance). Do I have something fundamentally wrong in my circuit? I would suggest that this is a common problem in many projects, but it seems to arise too often, as far as I can tell. It seems that whenever a parent association is created in the after_create method, there is a chance of a race condition if the creation of the parent model (in this case Song) depends on the existence of another child (in this case, Lyric).