2012-04-15 12 views
6

कृपया मेरी गलतफहमी खोजने में मेरी सहायता करें।ऐप इंजन, लेनदेन, और idempotency

मैं ऐप इंजन पर एक आरपीजी लिख रहा हूं। कुछ निश्चित कार्यकर्ता एक निश्चित स्थिति का उपभोग करते हैं। यदि स्थिति शून्य तक पहुंच जाती है तो खिलाड़ी कोई और कार्रवाई नहीं कर सकता है। मैंने खिलाड़ियों को धोखा देने के बारे में चिंता करना शुरू किया, हालांकि - क्या होगा यदि एक खिलाड़ी ने दो कार्यों को बहुत जल्दी भेजा, तो एक दूसरे के बगल में? यदि कोड को कम करने वाला कोड लेनदेन में नहीं है, तो खिलाड़ी को दो बार कार्रवाई करने का मौका मिलता है। तो, मुझे कोड को लपेटना चाहिए जो लेनदेन में स्टेट को कम करता है, है ना? अब तक सब ठीक है.

GAE अजगर में, हालांकि, हम इस documentation में है:

नोट: जब एक सौदा प्रस्तुत करने अपने अनुप्रयोग एक अपवाद प्राप्त करता है, यह नहीं हमेशा मतलब है कि लेन-देन में विफल रहा है। आप टाइमआउट, ट्रांज़ेक्शनफेल एरर, या लेनदेन किए गए मामलों में आंतरिक त्रुटि अपवाद प्राप्त कर सकते हैं और अंत में सफलतापूर्वक लागू किया जाएगा। जब भी संभव हो, अपने डेटास्टोर लेन-देन को बनाएं कि यदि आप एक लेनदेन दोहराते हैं, तो अंतिम परिणाम वही होगा।

हूप्स। इसका मतलब है कि समारोह मुझे लगता है कि चल रहा था इस तरह दिखता है कि:


def decrement(player_key, value=5): 
    player = Player.get(player_key) 
    player.stat -= value 
    player.put() 

ठीक है, कि नहीं करने वाला काम है, सही है क्योंकि बात idempotent नहीं है? अगर मैं इसके चारों ओर एक लेट्री लूप डालता हूं (क्या मुझे पायथन में आवश्यकता है? मैंने पढ़ा है कि मुझे SO पर आवश्यकता नहीं है ... लेकिन मुझे इसे दस्तावेज़ों में नहीं मिल रहा है) यह मूल्य को दो बार बढ़ा सकता है, सही? चूंकि मेरा कोड अपवाद पकड़ सकता है लेकिन डेटास्टोर ने अभी भी डेटा किया है ... हुह? मैं यह कैसे तय करुं? क्या यह एक मामला है जहां मुझे distributed transactions की आवश्यकता है? क्या मैं वास्तव में

+1

ठीक है, हाँ, और यह एक अच्छा मुद्दा है ... लेकिन इससे पहले कि मैं अपने कोड को कठोर-से-निदान के समूह के साथ कूड़ा, हार्ड-टू- बग पुन: उत्पन्न करें मैं सीखना चाहता हूं कि मुझे किस पैटर्न के लिए जाना चाहिए। –

+0

आपका पैटर्न सही रास्ते पर है, लेकिन जीएई में कुछ निराशाजनक बारीकियां हैं जो इस तरह के शल्य चिकित्सा के सटीक कार्यान्वयन को कठिन बनाती हैं। जीएई के साथ अपने अनुभव में, कभी-कभी यह प्रयास के लायक है, और कभी-कभी इसकी नहीं। –

+1

@TravisWebb असहमत। लेनदेन संबंधी सुरक्षा 'समयपूर्व अनुकूलन' नहीं है, न ही लेनदेन टकराव विशेष रूप से असंभव है। –

उत्तर

13

सबसे पहले, निक का जवाब सही नहीं है। DHayes का लेनदेन बेवकूफ नहीं है, इसलिए यदि यह कई बार चलाया जाता है (यानी एक प्रयास करें जब पहला प्रयास विफल हुआ था, जब यह नहीं हुआ), तो मान कई बार घट जाएगा। निक कहते हैं कि "डेटास्टोर जांचता है कि अगर संस्थाओं को उनके द्वारा संशोधित किया गया था, तो संशोधित किया गया है", लेकिन इससे समस्या को रोका नहीं जाता क्योंकि दोनों लेन-देन अलग-अलग होते थे, और दूसरा कार्य पूरा लेनदेन पूरा होने के बाद होता था।

समस्या को हल करने के लिए, आप लेनदेन के हिस्से के रूप में एक "लेनदेन कुंजी" बनाकर और नई इकाई में उस कुंजी को रिकॉर्ड करके लेनदेन idempotent बना सकते हैं। दूसरा लेनदेन उस लेनदेन कुंजी की जांच कर सकता है, और यदि पाया जाता है, तो कुछ भी नहीं करेगा। लेनदेन कुंजी हटा दी जा सकती है जब आप संतुष्ट हो जाते हैं कि लेनदेन पूरा हो गया है, या आप पुनः प्रयास छोड़ देते हैं।

मैं जानना चाहता हूं कि ऐपइंजिन (1-इन-ए-मिलियन, या 1-ए-बिलियन?) के लिए "बेहद दुर्लभ" का अर्थ क्या है, लेकिन मेरी सलाह यह है कि वित्तीय मामलों के लिए बेवकूफ लेनदेन की आवश्यकता है , लेकिन गेम स्कोर के लिए नहीं, या यहां तक ​​कि "जीवन" ;-)

1

क्या आप इस तरह की जानकारी को मेमकैच में स्टोर करने की कोशिश नहीं करना चाहिए, जो डेटास्टोर से बहुत तेज है (यदि आपको इस स्थिति का उपयोग आपके आवेदन में किया जाता है तो आपको कुछ चाहिए)। Memcache आपको एक अच्छा फ़ंक्शन प्रदान करता है: decr जो:

परमाणु रूप से एक कुंजी का मूल्य कम करता है। आंतरिक रूप से, मान एक हस्ताक्षरित 64-बिट पूर्णांक है। Memcache 64-बिट ओवरफ़्लो की जांच नहीं करता है। मूल्य, यदि बहुत बड़ा है, तो चारों ओर लपेट जाएगा।

decrhere के लिए खोजें। इसके बाद आपको किसी भी कार्य को डेटास्टोर में प्रत्येक x सेकंड या जब कोई निश्चित स्थिति पूरी हो जाती है, तो उसे सहेजने के लिए एक कार्य का उपयोग करना चाहिए।

+0

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

+0

ध्यान दें कि Memcache decr() फ़ंक्शन "शून्य से शून्य से नीचे की कमी" [\ [1 \]] (https://cloud.google.com/appengine/docs/python/refdocs/google.appengine.api भी है। मेम्कैश # google.appengine.api.memcache.Client.decr)। – Lee

1

यदि आप जो वर्णन कर रहे हैं उसके बारे में सावधानी से सोचते हैं, तो यह वास्तव में एक मुद्दा नहीं हो सकता है। इस बारे में इस बारे में सोचें:

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

यहां क्या हो सकता है:

ए 1 सफल होता है। ए 2 फिर रद्द कर देगा। सब अच्छा।

ए 1 वैध रूप से विफल रहता है (डेटा बदलने के बिना)। अनुसूचित पुनः प्रयास करें। ए 2 फिर कोशिश करता है, सफल होता है। जब ए 1 फिर से कोशिश करता है, तो यह निरस्त हो जाएगा।

ए 1 सफल होता है लेकिन एक त्रुटि रिपोर्ट करता है। अनुसूचित पुनः प्रयास करें। अगली बार या तो ए 1 या ए 2 कोशिश करता है, वे निरस्त हो जाएंगे।

इस काम के लिए, आपको ट्रैक रखने की आवश्यकता है कि ए 1 और ए 2 पूरा हो गया है - शायद उन्हें एक कार्य UUID दें और तैयार कार्यों की एक सूची स्टोर करें? या यहां तक ​​कि कार्य कतार का उपयोग करें।

+0

धन्यवाद, लेकिन मुझे यकीन नहीं है कि काम करता है, क्योंकि आईडी को संग्रहीत करने से ही इस समस्या में भाग हो सकता है ... लेकिन मुझे लगता है कि ऊपर निक का जवाब मेरे मामले को शामिल करता है। –

4

संपादित करें: यह गलत है - कृपया टिप्पणियां देखें।

आपका कोड ठीक है। दस्तावेज़ों का उल्लेख करने वाले मूर्खता दुष्प्रभावों के संबंध में है। जैसे-जैसे दस्तावेज़ों की व्याख्या होती है, आपका लेनदेन कार्य एक से अधिक बार चलाया जा सकता है; ऐसी स्थितियों में यदि फ़ंक्शन का कोई दुष्प्रभाव होता है, तो उन्हें कई बार लागू किया जाएगा। चूंकि आपका लेनदेन फ़ंक्शन ऐसा नहीं करता है, यह ठीक होगा।

idempotence के संबंध में एक समस्याग्रस्त समारोह का एक उदाहरण कुछ इस तरह होगा:

def do_something(self): 
    def _tx(): 
    # Do something transactional 
    self.counter += 1 
    db.run_in_transaction(_tx) 

इस मामले में, self.counter 1 से वृद्धि हो सकती है, या संभावित 1. अधिक से अधिक ऐसा करने से बचा जा सकता है लेनदेन के बाहर दुष्प्रभाव:

def do_something(self): 
    def _tx(): 
    # Do something transactional 
    return 1 
    self.counter += db.run_in_transaction(_tx) 
+0

धन्यवाद, निक।आप कह रहे हैं कि लेनदेन में किए गए किसी भी डेटास्टोर ऑपरेशंस केवल एक बार होगा, भले ही मेरे कोड को अपवाद हो और उसे पुनः प्रयास किया जाए? यदि मेरे 'कमी' लेनदेन को पुनः प्रयास के कारण दो बार बुलाया जाता है, तो यह केवल एक बार घट जाएगा? एनडीबी-भूमि में, ऐसा इसलिए है क्योंकि मेरे लेन-देन को कुछ आईडी सौंपी जाती है कि डेटास्टोर जानता है कि यह पहले से ही प्रतिबद्ध है? –

+0

(और "मेरे कोड ... retries" से मेरा मतलब है "ndb मेरे लिए retries") –

+1

@ डी। हैयस वे कई बार होंगे, लेकिन उनमें से केवल एक डेटास्टोर वापस प्रतिबद्ध हो जाएगा। डेटास्टोर आशावादी समेकन का उपयोग करता है, इसलिए जब यह लेनदेन करने की कोशिश करता है, तो डेटास्टोर जांच करता है कि अगर संस्थाएं उन्हें प्राप्त की गई थीं तो संशोधित किया गया है, और यदि वे नहीं हैं तो केवल लेनदेन स्वीकार करते हैं। –

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