2012-05-22 18 views
7

हमारे पास एक बहुत महंगी गणना है जिसे हम कैश करना चाहते हैं। calculate($key) दौरानकैश और कैश स्टैम्पेड से परहेज करना - एकाधिक एक साथ गणना

my $result = $cache->get($key); 

unless ($result) { 
    $result = calculate($key); 
    $cache->set($key, $result, '10 minutes'); 
} 

return $result; 
अब

,, इससे पहले कि हम कैश में परिणाम की दुकान, कई अन्य अनुरोधों में आते हैं, वह भी calculate($key) आरंभ हो जाएंगे, और प्रणाली के प्रदर्शन ग्रस्त है क्योंकि कई प्रक्रियाओं सभी गणना कर रहे हैं: तो हम के लिए इसी तरह कुछ करना वही चीज़।

आइडिया: आइए कैश में एक ध्वज डालें, जिस पर मूल्य की गणना की जा रही है, इसलिए अन्य अनुरोध सिर्फ उस गणना की समाप्ति के लिए प्रतीक्षा करते हैं, इसलिए वे सभी इसका उपयोग करते हैं। कुछ ऐसा:

my $result = $cache->get($key); 

if ($result) { 
    while ($result =~ /Wait, \d+ is running calculate../) { 
     sleep 0.5; 
     $result = $cache->get($key); 
    } 
} else { 
    $cache->set($key, "Wait, $$ is running calculate()", '10 minutes'); 
    $result = calculate($key); 
    $cache->set($key, $result, '10 minutes'); 
} 


return $result; 

अब यह कीड़े का एक नया नया खुलता है। यदि कैश सेट करने से पहले $$ मर जाता है तो क्या होगा। क्या होगा अगर, क्या हुआ अगर ... वे सब के सब व्याख्या करने योग्य है, लेकिन बाद से वहाँ CPAN में कुछ भी नहीं है कि करता है यह, मैं सोच शुरू (वहाँ CPAN में कुछ सब कुछ के लिए है) है:

वहाँ एक बेहतर दृष्टिकोण है? क्या कोई विशेष कारण है उदा। पर्ल का Cache और Cache::Cache कक्षाएं इस तरह कुछ तंत्र प्रदान नहीं करती हैं? क्या कोई कोशिश की गई और सही पैटर्न है जिसका मैं उपयोग कर सकता हूं?

आदर्श पहले से ही निचोड़ में एक Debian पैकेज या एक यूरेका पल, जहां मैं अपने तरीके से त्रुटि देख के साथ एक CPAN मॉड्यूल होगा ... :-)

संपादित करें: मैं के बाद से सीखा है कि यह कहा जाता है एक Cache stampede और प्रश्न के शीर्षक को अद्यतन किया है।

+0

[आईपीसी :: ShareLite] (http://search.cpan.org/~andya/IPC-ShareLite-0.17/lib/IPC/ShareLite.pm) SysV के लिए OO इंटरफेस प्रदान करता है शेयर्ड मेमोरी। इसकी तरह ** कैश ** के समान है जो एक विशेष लॉक प्रदान करता है। – tuxuday

+0

एक [कैश स्टैम्पेड - विकिपीडिया लेख] (https://en.wikipedia.org/wiki/Cache_stampede) है और एक [पर्ल-कैश चर्चा> मुद्रित विषय से परहेज] [https://groups.google.com/d/विषय/perl-cache-चर्चा/jDdBQliwlP4/चर्चा) इसके लिए रणनीतियों के बारे में। –

+0

और वहां एक [djangosnippets: MintCache] (https://www.djangosnippets.org/snippets/155/) रणनीति है। –

उत्तर

2

flock() यह।

चूंकि आपकी कार्यकर्ता प्रक्रियाएं एक ही प्रणाली पर हैं, इसलिए आप शायद calculate() आयनों को क्रमबद्ध करने के लिए अच्छी, पुरानी शैली वाली लॉकिंग का उपयोग कर सकते हैं। बोनस के रूप में, यह तकनीक कोर दस्तावेज़ों में से कई में दिखाई देती है।

use Fcntl qw(:DEFAULT :flock); # warning: this code not tested 

use constant LOCKFILE => 'you/customize/this/please'; 

my $result = $cache->get($key); 

unless ($result) { 
    # Get an exclusive lock 
    my $lock; 
    sysopen($lock, LOCKFILE, O_WRONLY|O_CREAT) or die; 
    flock($lock, LOCK_EX) or die; 

    # Did someone update the cache while we were waiting? 
    $result = $cache->get($key); 

    unless ($result) { 
     $result = calculate($key); 
     $cache->set($key, $result, '10 minutes'); 
    } 

    # Exclusive lock released here as $lock goes out of scope 
} 

return $result; 

लाभ: कार्यकर्ता मृत्यु तुरंत $lock जारी करेगी।

जोखिम: LOCK_EX हमेशा के लिए ब्लॉक कर सकता है, और यह एक लंबा समय है। SIGSTOP से बचें, शायद alarm() के साथ सहज महसूस करें।

एक्सटेंशन: आप सभी calculate() कॉल को क्रमानुसार नहीं करना चाहते, लेकिन अगर केवल एक ही $key के लिए सभी कॉल्स या कुंजियों के कुछ सेट, अपने कार्यकर्ताओं कर सकते हैं flock()/some/lockfile.$key_or_a_hash_of_the_key

+0

मैं चिंतित था कि क्या होगा उदा। कार्यकर्ता मृत्यु के दौरान। $ # लॉक के रूप में यहां जारी किया गया एक्सक्लूसिव लॉक गुंजाइश से बाहर चला गया है निफ्टी है! धन्यवाद! –

+0

सहमत हैं, जहां 'झुंड() 'उचित है, यह काफी निफ्टी है। सभी जादू अंतर्निहित फाइल डिस्क्रिप्टर में हैं, और दोनों प्रक्रियाओं की मौत और पर्ल के आखिरी संदर्भ-अंत-अंत-दायरे में फ़ाइल हैंडल का विनाश वास्तव में वही करता है जो कोई चाहता है। – pilcrow

1

लॉक का उपयोग करें? या शायद यह एक overkill होगा? या यदि यह संभव है, तो ऑफ़लाइन परिणाम को सटीक करें, फिर इसे ऑनलाइन उपयोग करें?

1

हालांकि यह आपके उपयोग के मामले में अधिक हो सकता है (या नहीं), क्या आपने प्रसंस्करण के लिए संदेश कतार का उपयोग करने पर विचार किया है? इस समय पर्ल समुदाय में RabbitMQ एक लोकप्रिय पसंद प्रतीत होता है और इसे AnyEvent::RabbitMQ मॉड्यूल के माध्यम से समर्थित किया जाता है।

जब भी आपको calculate की एक नई कुंजी की आवश्यकता होती है तो इस मामले में मूल रणनीति संदेश कतार का अनुरोध सबमिट करना होगा। कतार को calculate पर एक समय में केवल एक ही कुंजी (अनुरोध किए गए क्रम में) पर सेट किया जा सकता है यदि वह सब कुछ विश्वसनीय रूप से संभाल सकता है। वैकल्पिक रूप से, यदि आप एक साथ कई कुंजियों की सुरक्षित रूप से गणना कर सकते हैं, तो कतार का उपयोग उसी कुंजी के लिए एकाधिक अनुरोधों को समेकित करने, इसे एक बार कंप्यूटिंग करने और उस कुंजी का अनुरोध करने वाले सभी ग्राहकों को परिणाम लौटने के लिए भी किया जा सकता है।

बेशक, यह कुछ जटिलता जोड़ देगा और किसी भी प्रकार की प्रोग्रामिंग शैली के लिए किसी भी प्रकार की कॉल को आपके द्वारा उपयोग किया जा सकता है (मैं एक उदाहरण पेश करता हूं, लेकिन मैंने कभी इसे स्वयं लटका नहीं पाया है) लेकिन यह आपके खर्च के लायक होने के लिए दक्षता और विश्वसनीयता में पर्याप्त लाभ प्रदान कर सकता है।

+0

निश्चित रूप से योग्यता के साथ एक एवेन्यू भी। लेकिन मुझे लगता है कि एक बड़ी तस्वीर में फिट होना चाहिए, और हम अभी तक इसके लिए तैयार नहीं हैं। याद दिलाने के लिए शुक्रिया। –

-1

मैं आम तौर पर ऊपर तीर्थयात्रा के दृष्टिकोण से सहमत हूं। मैं इसमें एक चीज़ जोड़ूंगा: memoize() फ़ंक्शन के उपयोग की जांच करें ताकि संभावित रूप से calculate() आपके कोड में ऑपरेशन तेज हो सके।

जानकारी के लिए देखें http://perldoc.perl.org/Memoize.html

+0

ज्ञापन एक प्रक्रिया के भीतर ठीक काम करता है। जैसा कि ओपी में बताया गया है, यह मुद्दा एक ही चीज की गणना करने के इच्छुक सभी प्रक्रियाओं के बारे में है। जब तक मैंने कुछ गलत समझा नहीं है, ज्ञापन अलग-अलग प्रक्रियाओं में कैश किए गए मान उपलब्ध नहीं कराता है, और इस संदर्भ में इसका कोई उपयोग नहीं है। हां, यह बंधे हैंश का उपयोग कर सकता है, लेकिन फिर मुझे लगता है कि यह ओपी से बिल्कुल समस्या का अनुभव करेगा। –

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