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

अधिकतर असली फीचर केवल एक ही डेटाबेस अपडेट नहीं होते। वे छोटे-छोटे चरणों की एक श्रृंखला होते हैं: एक रो डालना, बैलेंस अपडेट करना, स्टेटस चिह्नित करना, एक ऑडिट रिकॉर्ड लिखना, या कोई जॉब enqueue करना। आंशिक लेखन तब होता है जब उन चरणों में से केवल कुछ ही डेटाबेस तक पहुँच पाते हैं।
यह तब दिखाई देता है जब चेन बीच में रुक जाती है: सर्वर एरर, आपके ऐप और Postgres के बीच टाइमआउट, स्टेप 2 के बाद क्रैश, या ऐसा रिट्राय जो स्टेप 1 को फिर से चला दे। हर स्टेटमेंट अपने आप में सही हो सकता है। वर्कफ़्लो तब टूटता है जब यह बीच में रुक जाता है।
आप इसे अक्सर जल्दी पहचान सकते हैं:
एक ठोस उदाहरण: प्लान अपग्रेड ग्राहक का प्लान अपडेट करता है, एक पेमेंट रिकॉर्ड जोड़ता है, और उपलब्ध क्रेडिट बढ़ाता है। अगर ऐप ने पेमेंट सेव कर ली पर क्रेडिट न बढ़ा पाए और क्रैश हो जाए, तो सपोर्ट को एक तालिका में "paid" दिखेगा और दूसरी में "no credits"। क्लाइंट के रिट्राय पर आप पेमेंट को दो बार भी रिकॉर्ड कर सकते हैं।
लक्ष्य सरल है: वर्कफ़्लो को एक स्विच की तरह मानें। या तो हर चरण सफल हो, या कोई भी न हो, ताकि आप कभी भी आधा-छुआ काम स्टोर न करें।
ट्रांज़ैक्शन डेटाबेस का तरीका है यह कहने का: इन स्टेप्स को एक कार्य इकाई की तरह मानो। या तो सभी बदलाव होते हैं, या कोई भी नहीं। जब भी आपका वर्कफ़्लो एक से ज़्यादा अपडेट की ज़रूरत रखता है—जैसे एक रो बनाना, बैलेंस अपडेट करना, और ऑडिट रिकॉर्ड लिखना—यह मायने रखता है।
पैसे को दो खातों के बीच भेजने की बात सोचें। आपको खाते A से घटाना और खाते B में जोड़ना होगा। अगर ऐप पहले स्टेप के बाद क्रैश हो जाए, तो आप नहीं चाहेंगे कि सिस्टम सिर्फ घटाई हुई राशि को ही "याद" रखे।
जब आप commit करते हैं, तो आप Postgres से कहते हैं: इस ट्रांज़ैक्शन में मैंने जो किया है उसे रखें। सभी बदलाव स्थायी हो जाते हैं और अन्य सेशंस को दिखने लगते हैं।
जब आप rollback करते हैं, तो आप Postgres से कहते हैं: मेरे द्वारा इस ट्रांज़ैक्शन में किया गया सब भूल जाओ। Postgres उन बदलावों को वैसे ही undo कर देता है जैसे कि वह ट्रांज़ैक्शन कभी हुआ ही नहीं।
एक ट्रांज़ैक्शन के अंदर, Postgres गारंटी देता है कि आप commit होने से पहले आधा-तैयार परिणाम अन्य सेशनों को नहीं दिखाएंगे। अगर कुछ फेल होता है और आप rollback करते हैं, तो डेटाबेस उस ट्रांज़ैक्शन के लिखे हुए को साफ़ कर देता है।
एक ट्रांज़ैक्शन खराब वर्कफ़्लो डिज़ाइन को ठीक नहीं करता। अगर आप गलत राशि घटाते हैं, गलत यूज़र ID उपयोग करते हैं, या कोई ज़रूरी चेक छोड़ देते हैं, तो Postgres गलत परिणाम को वफ़ादारी से commit कर देगा। ट्रांज़ैक्शन अपने आप हर बिज़नेस-लेवल कॉनफ़्लिक्ट (जैसे ओवरसेलिंग इन्वेंटरी) को भी रोके नहींगा जब तक आप सही कंस्ट्रेंट्स, लॉक या आइसोलेशन लेवल ना लगाएँ।
जब भी आप एक वास्तविक क्रिया पूरी करने के लिए एक से अधिक टेबल (या एक से अधिक रो) अपडेट करते हैं, तो यह ट्रांज़ैक्शन का उम्मीदवार है। मकसद वही रहता है: या तो सब कुछ किया गया, या कुछ भी नहीं।
ऑर्डर फ्लो क्लासिक केस है। आप एक order रो बना सकते हैं, इन्वेंटरी रिज़र्व कर सकते हैं, भुगतान ले सकते हैं, फिर ऑर्डर को paid के रूप में मार्क कर सकते हैं। अगर पेमेंट सफल हो पर स्टेटस अपडेट फेल हो जाए, तो आपके पास पैसा कैप्चर हुआ पर ऑर्डर unpaid दिखेगा। अगर ऑर्डर तो बना पर स्टॉक रिज़र्व नहीं हुआ, तो आप ऐसे आइटम बेच सकते हैं जो आपके पास वास्तव में नहीं हैं।
यूज़र ऑनबोर्डिंग भी इसी तरह चुपके से टूटती है। यूज़र बनाना, प्रोफ़ाइल रो डालना, रोल असाइन करना, और वेलकम ईमेल भेजने के लिए रिकॉर्ड करना—ये सब एक लॉजिकल एक्शन हैं। बिना समूहित किए आप ऐसे यूज़र के साथ फंस सकते हैं जो साइन इन कर सकता है पर पासपरमिशन नहीं है, या प्रोफ़ाइल है पर कोई यूज़र नहीं।
बैक-ऑफिस एक्शंस अक्सर "पेपर ट्रेल + स्टेट चेंज" व्यवहार चाहते हैं। किसी अनुरोध को स्वीकृत करना, एक ऑडिट एंट्री लिखना, और बैलेंस अपडेट करना साथ में सफल होना चाहिए। अगर बैलेंस बदलता है पर ऑडिट लॉग गायब है, तो आप खो देते हैं कि किसने क्या और क्यों बदला।
बैकग्राउंड जॉब्स भी लाभान्वित होते हैं, ख़ासकर जब आप एक वर्क आइटम कई चरणों में प्रोसेस करते हैं: आइटम को क्लेम करें ताकि दो वर्कर इसे न करें, बिज़नेस अपडेट लागू करें, रिपोर्टिंग और रिट्राय के लिए परिणाम रिकॉर्ड करें, फिर आइटम को पूरा (या किसी कारण से फेल) मार्क करें। अगर ये स्टेप अलग हो जाते हैं, तो रिट्राय और कॉन्करेंसी गड़बड़ कर देते हैं।
मल्टी-स्टेप फीचर टूटते हैं जब आप उन्हें स्वतंत्र अपडेट्स के ढेर की तरह ट्रीट करते हैं। डेटाबेस क्लाइंट खोलने से पहले, वर्कफ़्लो को एक छोटी कहानी के रूप में लिखें जिसमें एक स्पष्ट फिनिश लाइन हो: उपयोगकर्ता के लिए ठीक-ठीक क्या "किया हुआ" गिना जाएगा?
शुरू में स्टेप्स को साधारण भाषा में सूचीबद्ध करें, फिर एक सिंगल सक्सेस कंडीशन परिभाषित करें। उदाहरण: "ऑर्डर बनाया गया है, इन्वेंटरी रिज़र्व हुई है, और उपयोगकर्ता को एक ऑर्डर कन्फर्मेशन नंबर दिख रहा है।" इससे कम कुछ भी सफलता नहीं है, भले ही कुछ टेबल अपडेट हो गए हों।
अगला, डेटाबेस कार्य और बाहरी कार्य के बीच एक कठोर रेखा खींचें। डेटाबेस स्टेप्स वो हैं जिन्हें आप ट्रांज़ैक्शंस से सुरक्षित कर सकते हैं। कार्ड पेमेंट्स, ईमेल भेजना, या थर्ड-पार्टी APIs जैसी बाहरी कॉल्स धीमी और अस्थिर हो सकती हैं, और आप सामान्यतः उन्हें रोलबैक नहीं कर सकते।
एक सरल योजना: स्टेप्स को (1) ज़रूरी रूप से ऑल-ऑर-नथिंग होना चाहिए, (2) commit के बाद किया जा सकता है—में बाँट दें।
ट्रांज़ैक्शन के अंदर केवल वे स्टेप रखें जो एक साथ सुसंगत रहना ज़रूरी हैं:
साइड-इफेक्ट्स बाहर ले जाएँ। उदाहरण के लिए, पहले ऑर्डर commit करें, फिर आउटबॉक्स रिकॉर्ड के आधार पर कन्फर्मेशन ईमेल भेजें।
हर स्टेप के लिए लिखें कि अगर अगला स्टेप फेल हो जाए तो क्या होना चाहिए। "Rollback" का मतलब डेटाबेस rollback भी हो सकता है, या एक क्षतिपूर्तिक कार्रवाई (compensating action)।
उदाहरण: अगर पेमेंट सफल हो पर इन्वेंटरी रिज़र्वेशन फेल हो, तो पहले से तय करें कि आप तुरंत रिफंड करेंगे, या ऑर्डर को "payment captured, awaiting stock" के रूप में मार्क करेंगे और इसे असिंक्रोनस रूप से संभालेंगे।
एक ट्रांज़ैक्शन Postgres को बताता है: इन स्टेप्स को एक यूनिट की तरह ट्रीट करो। या तो ये सब होते हैं, या कुछ भी नहीं। यह सबसे सरल तरीका है आंशिक लेखन रोकने का।
शुरू से अंत तक एक ही डेटाबेस कनेक्शन (एक सेशन) का उपयोग करें। अगर आप स्टेप्स अलग कनेक्शनों में फैलाते हैं, तो Postgres ऑल-ऑर-नथिंग परिणाम की गारंटी नहीं दे सकता।
क्रम सरल है: begin करें, ज़रूरी reads और writes चलाएँ, अगर सब सफल हुआ तो commit करें, अन्यथा roll back करें और स्पष्ट त्रुटि लौटाएँ।
यहाँ SQL में एक न्यूनतम उदाहरण है:
BEGIN;
-- reads that inform your decision
SELECT balance FROM accounts WHERE id = 42 FOR UPDATE;
-- writes that must stay together
UPDATE accounts SET balance = balance - 50 WHERE id = 42;
INSERT INTO ledger(account_id, amount, note) VALUES (42, -50, 'Purchase');
COMMIT;
-- on error (in code), run:
-- ROLLBACK;
नोट: ऊपर के कोड ब्लॉक को जैसा है वैसा रखें—इनके अंदर का कंटेंट ट्रांसलेट नहीं किया गया है।
ट्रांज़ैक्शंस चलने के दौरान लॉक रखते हैं। जितने लंबे समय तक आप उन्हें खुला रखते हैं, उतना ज़्यादा आप अन्य कामों को ब्लॉक करते हैं औरTimeouts या deadlocks की संभावना बढ़ती है। ट्रांज़ैक्शन के अंदर आवश्यक काम ही रखें, और धीमी चीज़ें (ईमेल भेजना, पेमेंट प्रोवाइडर कॉल करना, PDF जनरेट करना) बाहर ले जाएँ।
जब कुछ फेल हो, तो इतना संदर्भ लॉग करें कि समस्या पुनरुत्पादित की जा सके बिना संवेदनशील डेटा लीक किए: वर्कफ़्लो नाम, order_id या user_id, प्रमुख पैरामीटर (amount, currency), और Postgres एरर कोड। पूरे पेलोड, कार्ड डेटा या पर्सनल डिटेल्स लॉग करने से बचें।
कॉन्करेंसी बस एक ही समय पर दो चीज़ें होने का नाम है। कल्पना करें दो ग्राहक आख़िरी टिकट खरीदने की कोशिश कर रहे हैं। दोनों स्क्रीन पर "1 left" दिखता है, दोनों ने Pay पर क्लिक किया, और अब आपका ऐप तय करेगा किसे टिकट मिलता है।
बिना सुरक्षा के दोनों रिक्वेस्ट एक ही पुरानी वैल्यू पढ़ सकते हैं और दोनों अपडेट लिख सकते हैं। इससे नेगेटिव इन्वेंटरी, डुप्लिकेट रिज़र्वेशन, या बिना ऑर्डर के भुगतान जैसे मामले होते हैं।
रो लॉक सबसे सरल गार्डरेल है। आप उस विशेष रो को लॉक करते हैं जिसे आप बदलने जा रहे हैं, चेक करते हैं, फिर अपडेट करते हैं। जो अन्य ट्रांज़ैक्शन उसी रो को छूना चाहेंगे उन्हें तब तक इंतजार करना होगा जब तक आप commit या rollback नहीं करते, जिससे डबल अपडेट से बचाव होता है।
एक सामान्य पैटर्न: ट्रांज़ैक्शन शुरू करें, FOR UPDATE के साथ इन्वेंटरी रो चुनें, चेक करें कि स्टॉक है, उसे घटाएँ, फिर ऑर्डर डालें। यह "दरवाज़ा थामे रखने" जैसा काम करता है जब तक आप महत्वपूर्ण स्टेप्स पूरा कर लें।
आइसोलेशन लेवल यह नियंत्रित करते हैं कि समवर्ती ट्रांज़ैक्शंस से कितनी अजीब चीज़ें दिखाई देंगी। ट्रेड-ऑफ अक्सर सुरक्षा बनाम गति का होता है:
लॉक्स को छोटा रखें। अगर ट्रांज़ैक्शन नेटवर्क कॉल का इंतजार करते हुए खुला बैठा है, तो आप लंबे इंतज़ार और टाइमआउट पैदा करेंगे। एक स्पष्ट फेल्योर पाथ पसंद करें: एक lock timeout सेट करें, एरर पकड़ें, और "कृपया फिर प्रयास करें" लौटाएँ बजाय कि रिक्वेस्ट हैंग होने दें।
अगर आपको डेटाबेस के बाहर काम करना है (जैसे कार्ड चार्ज करना), तो वर्कफ़्लो को विभाजित करें: जल्दी रिज़र्व करें, commit करें, फिर धीमी चीज़ करें, और एक और छोटे ट्रांज़ैक्शन से अंतिम रूप दें।
रिट्राइज़ Postgres-आधारित ऐप्स में सामान्य हैं। एक रिक्वेस्ट तब भी फेल हो सकती है जब आपका कोड सही हो: deadlocks, स्टेटमेंट टाइमआउट्स, छोटे नेटवर्क ड्रॉप्स, या उच्च आइसोलेशन में serialization errors। अगर आप बस वही हैंडलर फिर से चलाते हैं, तो आप दूसरा ऑर्डर बना सकते हैं, दो बार चार्ज कर सकते हैं, या डुप्लिकेट "ईवेंट" रो डाल सकते हैं।
फिक्स है आइडेम्पोटेंसी: ऑपरेशन को उसी इनपुट के साथ दो बार चलाना सुरक्षित होना चाहिए। डेटाबेस को पहचानने में सक्षम होना चाहिए "यह वही अनुरोध है" और स्थिर उत्तर देना चाहिए।
एक व्यावहारिक पैटर्न है हर मल्टी-स्टेप वर्कफ़्लो के साथ एक idempotency key जोड़ना (अक्सर क्लाइंट-जनरेटेड request_id) और उसे मुख्य रिकॉर्ड पर स्टोर करना, फिर उस की पर unique constraint लगाना।
उदाहरण: चेकआउट में, जब उपयोगकर्ता Pay पर क्लिक करे तब request_id जेनरेट करें, फिर उसी request_id के साथ ऑर्डर दर्ज करें। अगर रिट्राय होता है, दूसरा प्रयास unique constraint से टकराएगा और आप मौजूदा ऑर्डर लौटाएँगे बजाय कि नया बनाने के।
अक्सर जो मायने रखता है:
रिट्राय लूप को ट्रांज़ैक्शन के बाहर रखें। हर प्रयास एक नया ट्रांज़ैक्शन शुरू करे और वर्क यूनिट को ऊपर से फिर से चलाए। एक फेल हुये ट्रांज़ैक्शन के अंदर रिट्राय करना मदद नहीं करता क्योंकि Postgres उसे aborted के रूप में चिह्नित कर देता है।
एक छोटा उदाहरण: आपका ऐप ऑर्डर बनाता और इन्वेंटरी रिज़र्व करता है, पर COMMIT के ठीक बाद टाइमआउट हो जाता है। क्लाइंट रिट्राय करता है। idempotency key के साथ, दूसरा रिक्वेस्ट पहले से बने ऑर्डर को लौटाता है और दूसरी बार रिज़र्वेशन करने से बचता है।
ट्रांज़ैक्शंस मल्टी-स्टेप वर्कफ़्लो को साथ बनाए रखते हैं, पर वे अपने आप डेटा को सही नहीं बनाते। आंशिक-लिखन के दुष्प्रभाव से बचने का एक मजबूत तरीका यह है कि डेटाबेस में "गलत" स्थितियाँ कठिन या असंभव बनाकर रखें, भले ही एप्लिकेशन कोड में बग हो जाए।
सुरक्षा रेल्स से शुरू करें। फ़ॉरेन कीज़ सुनिश्चित करती हैं कि रेफरेंसेज़ वास्तविक हों (एक order line मिसिंग order की ओर इशारा न कर सके)। NOT NULL आधा भरी पंक्तियों को रोकता है। CHECK कंस्ट्रेंट्स उन मानों को पकड़ते हैं जो मतलब नहीं रखते (उदा., quantity > 0, total_cents >= 0)। ये नियम हर लिखित पर चलते हैं, चाहे कोई भी सर्विस या स्क्रिप्ट डेटाबेस छू रही हो।
लंबे वर्कफ़्लो के लिए, स्थिति परिवर्तन को स्पष्ट रूप से मॉडल करें। कई boolean फ्लैग्स के बजाय एक status कॉलम (pending, paid, shipped, canceled) उपयोग करें और केवल वैध ट्रांज़िशन की अनुमति दें। आप इसे कंस्ट्रेंट्स या ट्रिगर्स से लागू कर सकते हैं ताकि डेटाबेस अवैध जंप्स जैसे shipped -> pending को मना कर दे।
यूनिकनेस भी एक correctness का रूप है। उन जगहों पर unique constraints जोड़ें जहाँ डुप्लिकेट्स आपका वर्कफ़्लो तोड़ देंगे: order_number, invoice_number, या retries के लिए इस्तेमाल की जाने वाली idempotency_key। फिर, अगर आपकी ऐप वही रिक्वेस्ट फिर से भेजे, Postgres दूसरे insert को ब्लॉक कर देगा और आप सुरक्षित रूप से "पहले से प्रोसेस्ड" लौटाएँगे बजाय कि दूसरा ऑर्डर बनाने के।
जब आपको ट्रेसबिलिटी चाहिए, तो उसे स्पष्ट रूप से स्टोर करें। एक ऑडिट टेबल (या हिस्ट्री टेबल) जो रिकॉर्ड करे किसने क्या बदला और कब, "रहस्य अपडेट्स" को उन तथ्यों में बदल देती है जिन्हें आप घटना के समय क्वेरी कर सकते हैं।
अधिकतर आंशिक लेखन "खराब SQL" की वजह से नहीं होते। वे वर्कफ़्लो निर्णयों से आते हैं जो आधी कहानी commit करने को आसान बनाते हैं।
accounts फिर orders अपडेट करती है, पर दूसरी orders फिर accounts, तो लोड के तहत deadlocks की संभावना बढ़ जाती है।एक ठोस उदाहरण: चेकआउट में, आप इन्वेंटरी रिज़र्व करते हैं, ऑर्डर बनाते हैं, और फिर कार्ड चार्ज करते हैं। अगर आप चार्ज को उसी ट्रांज़ैक्शन के भीतर करते हैं, तो आप नेटवर्क के इंतज़ार में इन्वेंटरी लॉक पकड़ सकते हैं। अगर चार्ज सफल हो पर बाद में आपका ट्रांज़ैक्शन rollback हो जाए, तो आपने ग्राहक को चार्ज कर लिया पर ऑर्डर नहीं बनाया।
एक सुरक्षित पैटर्न यह है: ट्रांज़ैक्शन को डेटाबेस स्टेट पर केंद्रित रखें (रिज़र्व इन्वेंटरी, ऑर्डर बनाएं, payment pending दर्ज करें), commit करें, फिर एक्सटर्नल API कॉल करें, और परिणाम को एक नए छोटे ट्रांज़ैक्शन में लिखें। कई टीमें इसे एक pending स्टेटस और बैकग्राउंड जॉब से इम्प्लीमेंट करती हैं।
जब किसी वर्कफ़्लो में कई स्टेप्स हों (insert, update, charge, send), लक्ष्य सरल है: या तो सब कुछ रिकॉर्ड हो, या कुछ भी नहीं।
हर आवश्यक डेटाबेस लिखाई को एक ट्रांज़ैक्शन के अंदर रखें। अगर कोई स्टेप फेल हो, rollback करें और डेटा ठीक वैसा छोड़ दें जैसा पहले था।
सफलता की शर्त स्पष्ट करें। उदाहरण: "ऑर्डर बनाया गया है, स्टॉक रिज़र्व हुआ है, और भुगतान की स्थिति रिकॉर्ड की गई है।" इससे कम कुछ भी फेल है और ट्रांज़ैक्शन रद्द होना चाहिए।
BEGIN ... COMMIT ब्लॉक में हों।ROLLBACK की ओर जाता है, और कॉलर को स्पष्ट फेल्योर मिलता है।माना कि वही रिक्वेस्ट फिर से किए जाने की संभावना है। डेटाबेस को केवल-एक बार वाले नियम लागू करने में मदद करनी चाहिए।
ट्रांज़ैक्शन के अंदर न्यूनतम काम करें, और लॉक रखते हुए नेटवर्क कॉल से बचें।
अगर आप नहीं देख पाते कि कहा टूट रहा है, तो आप अनुमान लगाते रहेंगे।
एक चेकआउट में कई स्टेप होते हैं जिन्हें एक साथ चलना चाहिए: ऑर्डर बनाना, इन्वेंटरी रिज़र्व करना, पेमेंट प्रयास रिकॉर्ड करना, फिर ऑर्डर स्टेटस मार्क करना।
कल्पना करें उपयोगकर्ता 1 आइटम के लिए Buy पर क्लिक करता है।
एक ट्रांज़ैक्शन के अंदर, केवल डेटाबेस परिवर्तन करें:
orders तालिका में एक रो डालें जिसमें status pending_payment हो।inventory.available घटाएँ या reservations रो बनाएं)।idempotency_key (unique) के साथ payment_intents में एक रो डालें।outbox रो डालें जैसे "order_created"।अगर कोई स्टेटमेंट फेल होता है (out of stock, constraint error, क्रैश), Postgres पूरे ट्रांज़ैक्शन को रोलबैक कर देता है। आपको ऑर्डर बिना रिज़र्वेशन के नहीं मिलेगा, और न ही रिज़र्वेशन बिना ऑर्डर के।
पेमेंट प्रोवाइडर आपकी डेटाबेस के बाहर है, इसलिए इसे एक अलग चरण के रूप में ट्रीट करें।
अगर प्रोवाइडर कॉल commit से पहले फेल हो, तो ट्रांज़ैक्शन रद्द करें और कुछ भी लिखित न हो। अगर प्रोवाइडर कॉल commit के बाद फेल हो, तो एक नया ट्रांज़ैक्शन चलाकर payment attempt को failed मार्क करें, रिज़र्वेशन रिलीज़ करें, और ऑर्डर स्टेटस को canceled सेट करें।
क्लाइंट को प्रति चेकआउट प्रयास एक idempotency_key भेजें। इसे payment_intents(idempotency_key) पर unique index के साथ लागू करें (या आप चाहें तो orders पर)। रिट्राय पर, आपका कोड मौजूदा रो देखेगा और नया ऑर्डर बनाने के बजाय जारी रखेगा।
ट्रांज़ैक्शंस के भीतर ईमेल न भेजें। उसी ट्रांज़ैक्शन में एक आउटबॉक्स रिकॉर्ड लिखें, फिर commit के बाद एक बैकग्राउंड वर्कर ईमेल भेजे। इस तरह आप कभी ऐसे ऑर्डर के लिए ईमेल नहीं भेजेंगे जो रोलबैक हो गया।
एक ऐसा वर्कफ़्लो चुनें जो एक से अधिक टेबल छुए: signup + welcome email enqueue, checkout + inventory, invoice + ledger entry, या create project + default settings।
पहले स्टेप्स लिखें, फिर वे नियम तय करें जो हमेशा सच होने चाहिए (आपके invariants)। उदाहरण: "एक ऑर्डर या तो पूरी तरह paid और reserved है, या नहीं-paid और न ही reserved। कभी आधा-reserved नहीं।" उन नियमों को एक ऑल-ऑर-नथिंग यूनिट में बदल दें।
एक सरल योजना:
फिर जानबूझकर खराब मामलों का परीक्षण करें। स्टेप 2 के बाद क्रैश का सिमुलेशन करें, commit के ठीक पहले टाइमआउट, और UI से डबल-सब्मिट। लक्ष्य नीरस परिणाम हों: कोई orphan rows नहीं, कोई दोहरी चार्जिंग नहीं, कोई हमेशा के लिए pending नहीं।
अगर आप जल्दी प्रोटोटाइप कर रहे हैं, तो वर्कफ़्लो को प्लानिंग-फर्स्ट टूल में स्केच करना मददगार होता है इससे पहले कि आप हैंडलर्स और स्कीमा बनाएं। उदाहरण के लिए, Koder.ai (koder.ai) में एक Planning Mode है और यह स्नैपशॉट्स और रोलबैक का समर्थन करता है, जो ट्रांज़ैक्शन बाउंड्रीज़ और कंस्ट्रेंट्स पर इटरेशन करते समय उपयोगी हो सकता है।
इस सप्ताह एक वर्कफ़्लो के लिए यह करें। दूसरा वर्कफ़्लो बहुत तेज़ी से आ जायेगा।