Is this a good way of handling item reservations? I have a site were we sell unique items. When a user clicks to buy an item we have to reserve it. This prevents other buyers from purchasing the item while the first buyer is checking out.

When a buyer clicks the "Buy" button, he is redirected to the orders controller. The orders controller executes the following steps:


Verify item is available (not reserved, or sold).

If available, the item is marked as reserved, and its id is stored in the session.
If no, check if it is reserved, and if the current session has the item id (meaning the current user has made the reservation). If both conditions are true the buyer can complete his purchase. Otherwise, the buyer notified and presented with alternatives.

If buyer navigates away from the orders controller, the item is released back to the catalogue.


Are there any obvious pitfalls in my approach?

I have reservations about using the session to indicate the current user has reserved an item. Users don't have to register to check out. 

I also feel like I'm repeating myself with @item.reserve!, the reserve! in item_reserver.rb.

Thoughts? Feedback?

application_controller.rb

include ItemReservation
before_action :release_item


items_controller.rb

# ensures item is available
before_action :verify_item_is_available, only: :show


orders_controller.rb

# ensures item is available
before_action :verify_item_is_available, only: [:new, :create]

def new
  reserve! @item
  @order = Order.new
end


controllers/concerns/item_reserver.rb

# before filter
def verify_item_is_available
  unless @item.available?
    redirect_to similar_item_path(@item) unless @item.reserved? &amp;&amp; session[:reserved_item_id] == @item.id
  end
end

# before filter
def release_item
  if session[:reserved_item_id].present? &amp;&amp; controller_name != 'orders'
    release! session[:reserved_item_id]
  end
end

def reserve!(item)
  if item.available?
    session[:reserved_item_id] = item.id if item.reserve!
  end
end

def release!(item_id)
  item = Item.find(item_id)
  if item.reserved?
    session[:reserved_item_id] = nil if item.release!
  end
end


item.rb

  def reserve!
    update!(availability: 'reserved')
  end

  def release!
    update!(availability: 'available')
  end

  def purchase!
    update!(availability: 'sold')
  end
Placing locks is inherently fragile, especially on a website.  HTTP is stateless, and a user could walk away or close the browser at any time, leaving a lock in place forever.

I would create a table of Lock objects, where each Lock refers to an item, states the quantity, and most importantly has a creation timestamp.  Locks that are too old can easily be purged or ignored.