Felipe Cypriano
Felipe Cypriano

Reputation: 2737

Rails, how can I make a synchronous serial queue than works in multi-thread/process

In my Rails 4 with Ruby 2 app I've the following model:

Playlist and Tracks model

A Playlist can have many Tracks and each Track has its position in its Playlist. For each new Track I want to set its position to be the last, in thread-safe and/or multi-process-safe way.

In my research I found a bunch of projects that handle background jobs but I don't want this to be background, I want it to be synchronous. So the caller of my API will get the response with the correct position.

The question is: what's the best way to make a synchronous serial queue that will be used to set the position of a Track in its Playlist?

Upvotes: 0

Views: 963

Answers (2)

Jeremy Green
Jeremy Green

Reputation: 8574

If you want it to be synchronous, then you don't need a queue. Instead you'd just wrap a bit of code in a transaction where you find the last track position for that playlist, then set the position of the new track to be one more, then save the new track. Something like this (assuming you're in a controller action):

@playlist = Playlist.find params[:id]
@track = Track.new params[:track]
@playlist.transaction do
  last_track = @playlist.tracks.order("posistion desc").first
  @track.position = last_track.position + 1
  @track.save
end

Upvotes: 1

LoveRails
LoveRails

Reputation: 21

As others suggested, if you want it to be a synchronous call just add it to your controller action. Just have one question, is there a requirement that you explicitly require a position attribute in the Track model. Because by default the track that you add to the playlist is added to the end

playlist = Playlist.find params[:id]
playlist.transaction.do
  playlist.tracks.create(params[:track]) 
end

So when you want the last track that you added, then you can just fetch it by

playlist.tracks.last

If you want some kind of ordering for your tracks, then you can add some scope method to your Track model to render your tracks "by newest to oldest" or "by oldest to newest"(example without position attribute).

scope: sort_by_newest, order('created_at desc')
scope: sort_by_oldest, order('created_at asc')

If you still need an explicit tracking with the position attribute, then

playlist = Playlist.find params[:id]
track_params = params[:track] || {}
track_params.merge!({position: playlist.tracks.count('id') + 1})
playlist.transaction.do
  playlist.tracks.create(track_params) 
end

If you want some kind of ordering for your tracks, then you can add some scope method to your Track model to render your tracks "by newest to oldest" or "by oldest to newest"(example with position attribute).

scope: sort_by_newest, order('position desc')
scope: sort_by_oldest, order('position asc')

Hope it helps.

Upvotes: 1

Related Questions