2012-06-26 12 views
9

मेरा एपीआई उपयोगकर्ताओं को कुछ अद्वितीय आइटम खरीदने की अनुमति देता है, जहां प्रत्येक आइटम केवल एक उपयोगकर्ता को बेचा जा सकता है। इसलिए जब एकाधिक उपयोगकर्ता एक ही आइटम खरीदने का प्रयास करते हैं, तो एक उपयोगकर्ता को प्रतिक्रिया मिलनी चाहिए: ठीक और अन्य उपयोगकर्ता को प्रतिक्रिया too_late प्राप्त करना चाहिए।मल्टी-थ्रेडेड समवर्ती कैपिबरा अनुरोध?

अब, मेरे कोड में बग प्रतीत होता है। एक दौड़ की स्थिति। यदि दो उपयोगकर्ता एक ही आइटम को एक ही समय में खरीदने का प्रयास करते हैं, तो उन्हें दोनों को ठीक उत्तर मिलता है। मुद्दा उत्पादन में स्पष्ट रूप से पुन: उत्पन्न करने योग्य है।

context "when I try to provoke a race condition" do 
    # ... 

    before do 
    @concurrent_requests = 2.times.map do 
     Thread.new do 
     Thread.current[:answer] = post "/api/v1/item/buy.json", :id => item.id 
     end 
    end 

    @answers = @concurrent_requests.map do |th| 
     th.join 
     th[:answer].body 
    end 
    end 

    it "should only sell the item to one user" do 
    @answers.sort.should == ["ok", "too_late"].sort 
    end 
end 

यह की तरह लगता है एक ही समय में प्रश्नों निष्पादित नहीं करता है: अब मैं एक साधारण परीक्षण rspec के माध्यम से इसे पुन: पेश करने की कोशिश करता है कि लिखा है। इस परीक्षण के लिए, मैं अपने नियंत्रक कार्रवाई में निम्न कोड डाल:

puts "Is it concurrent?" 
sleep 0.2 
puts "Oh Noez." 

अपेक्षित उत्पादन होगा, अगर अनुरोध समवर्ती हैं:

:

Is it concurrent? 
Is it concurrent? 
Oh Noez. 
Oh Noez. 

हालांकि, मैं निम्नलिखित आउटपुट प्राप्त

Is it concurrent? 
Oh Noez. 
Is it concurrent? 
Oh Noez. 

जो मुझे बताता है, कि कैपिबरा अनुरोध एक ही समय में नहीं चल रहे हैं, लेकिन एक समय में। मैं अपने कैपबरा अनुरोधों को समवर्ती कैसे कर सकता हूं?

+0

ऊपर आपका कोड उदाहरण वर्तमान कैपिबरा डीएसएल की तरह दिखता नहीं है। यह रैक :: टेस्ट का उपयोग कर एक सादा नियंत्रक परीक्षण की तरह दिखता है। क्या यही वह चीज है? –

उत्तर

13

मल्टीथ्रेडिंग और कैपिबरा काम नहीं करता है, क्योंकि कैपबरा एक अलग सर्वर थ्रेड का उपयोग करता है जो अनुक्रमिक रूप से कनेक्शन को संभालता है। लेकिन अगर आप कांटा, यह काम करता है।

मैं एक इंटर-प्रोसेस संचार तंत्र के रूप में निकास कोड का उपयोग कर रहा हूं। यदि आप अधिक जटिल चीजें करते हैं, तो आप सॉकेट का उपयोग करना चाह सकते हैं।

यह मेरा त्वरित और गंदी हैक है:

before do 
    @concurrent_requests = 2.times.map do 
    fork do 
     # ActiveRecord explodes when you do not re-establish the sockets 
     ActiveRecord::Base.connection.reconnect! 

     answer = post "/api/v1/item/buy.json", :id => item.id 

     # Calling exit! instead of exit so we do not invoke any rspec's `at_exit` 
     # handlers, which cleans up, measures code coverage and make things explode. 
     case JSON.parse(answer.body)["status"] 
     when "accepted" 
      exit! 128 
     when "too_late" 
      exit! 129 
     end 
    end 
    end 

    # Wait for the two requests to finish and get the exit codes. 
    @exitcodes = @concurrent_requests.map do |pid| 
    Process.waitpid(pid) 
    $?.exitstatus 
    end 

    # Also reconnect in the main process, just in case things go wrong... 
    ActiveRecord::Base.connection.reconnect! 

    # And reload the item that has been modified by the seperate processs, 
    # for use in later `it` blocks. 
    item.reload 
end 

it "should only accept one of two concurrent requests" do 
    @exitcodes.sort.should == [128, 129] 
end 

मैं और की तरह नहीं बल्कि विदेशी बाहर निकलने के कोड का उपयोग, क्योंकि यदि यह मामला ब्लॉक पर पहुंच गया कोड 0 के साथ प्रक्रियाओं से बाहर निकलें और नहीं कर रहा है 1 अगर एक अपवाद होता है। दोनों नहीं होना चाहिए। तो उच्च कोड का उपयोग करके, मुझे लगता है कि चीजें गलत होने पर मुझे पता चलता है।

+0

अच्छा कामकाज! एक संदर्भ के रूप में, क्या आप प्रासंगिक नियंत्रक/मॉडल कोड पोस्ट कर सकते हैं जो दौड़ की स्थिति को प्रकट करता है? –

+0

इस प्रश्न पर विश्वास नहीं कर सकता और उत्तर अभी तक मतदान नहीं किया गया है। मेरा दिन बचाया! –

+0

ग्रेट वर्कअराउंड! साझा करने के लिए Mni thx! – ctp

5

आप समवर्ती कैपिबरा अनुरोध नहीं कर सकते हैं। हालांकि, आप एकाधिक कैपिबरा सत्र बना सकते हैं और समवर्ती उपयोगकर्ताओं को अनुकरण करने के लिए उसी परीक्षण के भीतर उनका उपयोग कर सकते हैं।

user_1 = Capybara::Session.new(:webkit) # or whatever driver 
user_2 = Capybara::Session.new(:webkit) 

user_1.visit 'some/page' 
user_2.visit 'some/page' 

# ... more tests ... 

user_1.click_on 'Buy' 
user_2.click_on 'Buy' 
+1

मुझे अनुक्रमिक अनुरोधों के बारे में पता था। मैंने अंततः समस्या को हल किया। मेरा जवाब देखें मैं ** समवर्ती अनुरोध कर सकता हूं। – iblue

संबंधित मुद्दे