मेरे पास वास्तव में एक सरल रेल एप्लिकेशन है जो उपयोगकर्ताओं को पाठ्यक्रमों के एक सेट पर अपनी उपस्थिति पंजीकृत करने की अनुमति देता है। ActiveRecord मॉडल इस प्रकार हैं:मैं अपने रेल ऐप में दौड़ की स्थिति से कैसे बचूं?
class Course < ActiveRecord::Base
has_many :scheduled_runs
...
end
class ScheduledRun < ActiveRecord::Base
belongs_to :course
has_many :attendances
has_many :attendees, :through => :attendances
...
end
class Attendance < ActiveRecord::Base
belongs_to :user
belongs_to :scheduled_run, :counter_cache => true
...
end
class User < ActiveRecord::Base
has_many :attendances
has_many :registered_courses, :through => :attendances, :source => :scheduled_run
end
एक ScheduledRun उदाहरण उपलब्ध स्थानों की एक सीमित संख्या है, और एक बार सीमा पार होने के कोई और अधिक उपस्थिति स्वीकार किया जा सकता।
def full?
attendances_count == capacity
end
attendances_count एक काउंटर कैश एक विशेष ScheduledRun रिकार्ड के लिए बनाई गई उपस्थिति संघों की संख्या पकड़े स्तंभ है।
मेरी समस्या यह है कि मैं यह सुनिश्चित करने के लिए सही तरीके से पूरी तरह से नहीं जानता कि दौड़ की स्थिति तब नहीं होती है जब 1 या अधिक लोग एक ही समय में पाठ्यक्रम पर अंतिम उपलब्ध स्थान के लिए पंजीकरण करने का प्रयास करते हैं।
मेरे उपस्थिति नियंत्रक इस तरह दिखता है:
class AttendancesController < ApplicationController
before_filter :load_scheduled_run
before_filter :load_user, :only => :create
def new
@user = User.new
end
def create
unless @user.valid?
render :action => 'new'
end
@attendance = @user.attendances.build(:scheduled_run_id => params[:scheduled_run_id])
if @attendance.save
flash[:notice] = "Successfully created attendance."
redirect_to root_url
else
render :action => 'new'
end
end
protected
def load_scheduled_run
@run = ScheduledRun.find(params[:scheduled_run_id])
end
def load_user
@user = User.create_new_or_load_existing(params[:user])
end
end
आप देख सकते हैं, यह खाता है, जहां ScheduledRun उदाहरण पहले से ही क्षमता तक पहुँच गया है पर ध्यान नहीं देता।
किसी भी मदद के लिए इस पर बहुत सराहना की जाएगी।
अद्यतन
मैं कुछ नहीं कर रहा हूँ अगर यह इस मामले में आशावादी ताला प्रदर्शन करने के लिए सही तरीका है, लेकिन यहाँ मैं क्या किया है:
मैं ScheduledRuns मेज पर दो कॉलम जोड़ा
: -t.integer :attendances_count, :default => 0
t.integer :lock_version, :default => 0
मैं भी ScheduledRun मॉडल के लिए एक विधि जोड़ा
def attend(user)
attendance = self.attendances.build(:user_id => user.id)
attendance.save
rescue ActiveRecord::StaleObjectError
self.reload!
retry unless full?
end
जब उपस्थिति मॉडल सहेजा जाता है, ActiveRecord आगे बढ़ता है और अनुसूचित रून मॉडल पर काउंटर कैश कॉलम अपडेट करता है। यहाँ लॉग उत्पादन दिखाने वाला ऐसा होता है -
ScheduledRun Load (0.2ms) SELECT * FROM `scheduled_runs` WHERE (`scheduled_runs`.`id` = 113338481) ORDER BY date DESC
Attendance Create (0.2ms) INSERT INTO `attendances` (`created_at`, `scheduled_run_id`, `updated_at`, `user_id`) VALUES('2010-06-15 10:16:43', 113338481, '2010-06-15 10:16:43', 350162832)
ScheduledRun Update (0.2ms) UPDATE `scheduled_runs` SET `lock_version` = COALESCE(`lock_version`, 0) + 1, `attendances_count` = COALESCE(`attendances_count`, 0) + 1 WHERE (`id` = 113338481)
बाद में एक अद्यतन ScheduledRun मॉडल से पहले नए उपस्थिति मॉडल सहेजा जाता है करने के लिए होता है, तो इस StaleObjectError अपवाद सक्रिय होना चाहिए। उस बिंदु पर, अगर पूरी तरह से पहले से ही नहीं पहुंच पाई है, तो पूरी चीज फिर से कोशिश की जाती है।
अद्यतन # 2
यहाँ है SheduledRun वस्तु पर अद्यतन में भाग लेने विधि @ kenn की प्रतिक्रिया से इसे जारी रखते हुए:
# creates a new attendee on a course
def attend(user)
ScheduledRun.transaction do
begin
attendance = self.attendances.build(:user_id => user.id)
self.touch # force parent object to update its lock version
attendance.save # as child object creation in hm association skips locking mechanism
rescue ActiveRecord::StaleObjectError
self.reload!
retry unless full?
end
end
end
नवीनतम रेल में फिक्स्ड। –
आपको आशावादी लॉकिंग का उपयोग करने की आवश्यकता है। यह स्क्रीनकास्ट आपको दिखाएगा कि यह कैसे करें: [लिंक टेक्स्ट] (http://railscasts.com/episodes/59-optimistic-locking) – rtacconi
आपका क्या मतलब है, डमित्री? – Edward