प्रतिनिधि इंटरव्यू विषय

बैकएंड इंटरव्यू: आप डेटाबेस और मैसेज ब्रोकर के डुअल-राइट (Dual-Write) की समस्या को कैसे हल करते हैं?

बैकएंडकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

एक ऑर्डर सर्विस PostgreSQL में एक ऑर्डर लिखती है और मैसेज ब्रोकर पर OrderCreated पब्लिश करती है। बिना डिस्ट्रीब्यूटेड टू-फेज कमिट के, आप यह कैसे सुनिश्चित करेंगे कि रोलबैक हुआ ऑर्डर कोई इवेंट उत्सर्जित न करे, कमिट हुआ ऑर्डर अंततः कम से कम एक इवेंट उत्सर्जित करे, और रिले या कंज्यूमर के क्रैश होने पर भी बिज़नेस प्रभाव डुप्लिकेट न हो? ऑर्डरिंग, संचालन (operations) और सत्यापन (verification) को भी स्पष्ट करें।

प्रॉम्प्ट और लागू संदर्भ

एक ऑर्डर सर्विस को एक कमांड के लिए दो काम करने होंगे:

  1. PostgreSQL में एक ऑर्डर लिखना; और
  2. मैसेज ब्रोकर पर एक OrderCreated इवेंट पब्लिश करना।

बिज़नेस अनुबंध "दोनों कॉल्स का प्रयास करें" की तुलना में अधिक सख्त है। यदि डेटाबेस ट्रांज़ैक्शन रोलबैक हो जाता है, तो कोई भी इवेंट उस गैर-मौजूद ऑर्डर का विवरण नहीं दे सकता। यदि ट्रांज़ैक्शन कमिट होता है, तो पब्लिश करने का इरादा प्रोसेस क्रैश के बाद भी सुरक्षित रहना चाहिए और अंततः ब्रोकर तक पहुँचना चाहिए। एक ही ऑर्डर के इवेंट्स क्रम में रहने चाहिए, जबकि ग्लोबल ऑर्डरिंग अनावश्यक है। रिले और कंज्यूमर किसी भी समय क्रैश हो सकते हैं, और ब्रोकर पुनः डिलीवर कर सकता है। डिस्ट्रीब्यूटेड टू-फेज कमिट उपलब्ध नहीं है।

यह ट्रांज़ैक्शनल आउटबॉक्स की समस्या है। यह तब सामने आती है जब एक अनुरोध ट्रांज़ैक्शनल स्थिति को बदलता है और उसे किसी अन्य सिस्टम में विश्वसनीय रूप से कार्य ट्रिगर करना होता है: इन्वेंट्री रिज़र्वेशन, बिलिंग, सर्च इंडेक्सिंग, ईमेल, वेबहुक या एनालिटिक्स। लक्ष्य जादुई रूप से एक्ज़ैक्टली-वंस डिलीवरी प्राप्त करना नहीं है। लक्ष्य उस परमाणु (atomic) सीमा की पहचान करना है जो उपलब्ध है, सीमा पार के इरादे को ड्यूरेबल बनाना है, और पुनः प्रयासों (retries) को सुरक्षित बनाना है।

इंटरव्यूअर क्या मूल्यांकन करता है

पहला संकेत फेलियर-विंडो का तर्क है। "डेटाबेस में लिखें, फिर पब्लिश करें" में यदि प्रोसेस कमिट के बाद क्रैश हो जाता है तो इवेंट खो जाता है। "पब्लिश करें, फिर कमिट करें" इवेंट को तब भी उजागर कर देता है यदि डेटाबेस बाद में रोलबैक हो जाता है। इन-मेमोरी आफ्टर-कमिट कॉलबैक भी प्रोसेस के साथ गायब हो जाता है। एक मज़बूत उत्तर किसी पैटर्न का प्रस्ताव देने से पहले इन विंडोज़ का नाम लेता है।

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

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

अंतिम संकेत ऑर्डरिंग और संचालन क्षमता (operability) हैं: प्रति-एग्रीगेट सीक्वेंस नंबर, पार्टीशन कीज़, कन्करेंट रिले ओनरशिप, पॉइज़न इवेंट्स, रीट्राई पॉलिसी, क्लीनअप, रीप्ले रिटेंशन, लैग मेट्रिक्स, और फॉल्ट-इंजेक्शन टेस्ट्स। इन सीमाओं के बिना पैटर्न का नाम लेना अधूरा है।

उत्तर देने से पहले स्पष्ट करने योग्य प्रश्न

  • आवश्यक गारंटी क्या है? क्या एक्ज़ैक्टली-वंस बिज़नेस प्रभाव के साथ एट-लीस्ट-वंस पब्लिकेशन

पर्याप्त है, या कॉलर को जवाब देने से पहले सिंक्रोनस कन्फर्मेशन की आवश्यकता है?

  • कौन सा म्यूटेशन और इवेंट एक साथ संबंधित हैं? एक ऑर्डर म्यूटेशन एक इवेंट बना सकता है, या एक ही

ट्रांज़ैक्शन कई इवेंट्स बना सकता है जिन्हें प्रति-ऑर्डर क्रमिक सीक्वेंस नंबरों की आवश्यकता होती है।

  • किस प्रकार की ऑर्डरिंग आवश्यक है? यह डिज़ाइन प्रति ऑर्डर ऑर्डरिंग मानता है, सभी ऑर्डर में

एक ग्लोबल टोटल ऑर्डर नहीं।

  • ब्रोकर क्या गारंटी दे सकता है? एकनॉलेजमेंट्स, पुनः डिलीवरी, पार्टीशन ऑर्डरिंग,

रिटेंशन और प्रोड्यूसर आइडेम्पोटेंसी के बारे में पूछें। इनमें से कोई भी डेटाबेस-से-ब्रोकर हैंडऑफ़ गैप को अपने आप दूर नहीं करता है।

  • इवेंट कितनी जल्दी दिखाई देना चाहिए? लेटेंसी लक्ष्य पोलिंग अंतराल, डेटाबेस लोड,

और क्या चेंज डेटा कैप्चर उचित है, इसे प्रभावित करता है।

  • कंज्यूमर क्या करता है? एक लोकल डेटाबेस अपडेट इनबॉक्स रो के साथ ट्रांज़ैक्शन साझा कर सकता है;

एक बाहरी भुगतान या ईमेल को डाउनस्ट्रीम आइडेम्पोटेंसी की या किसी अन्य ड्यूरेबल हैंडऑफ़ की आवश्यकता होती है।

  • रीप्ले कब तक संभव रहना चाहिए? आउटबॉक्स और कंज्यूमर-डिडुप्लिकेशन रिकॉर्ड्स की सफाई को

आवश्यक रीट्राई और रीप्ले क्षितिज (horizon) को बनाए रखना चाहिए।

  • क्या दोनों संसाधन टू-फेज कमिट में भाग ले सकते हैं? प्रॉम्प्ट कहता है नहीं। यदि कोई वास्तविक सिस्टम वास्तव में

सिंक्रोनस क्रॉस-रिसोर्स एटॉमिसीटी की मांग करता है और दोनों संसाधन इसका समर्थन करते हैं, तो इसे सार्वभौमिक रूप से असंभव घोषित करने के बजाय इसकी उपलब्धता और कपलिंग लागतों का मूल्यांकन किया जाना चाहिए।

30-सेकंड उत्तर रूपरेखा

“मैं उसी PostgreSQL ट्रांज़ैक्शन में ऑर्डर और एक इम्यूटैबल आउटबॉक्स इवेंट लिखूँगा। एक अलग रिले कमिटेड आउटबॉक्स पंक्तियों को क्लेम करता है, उन्हें पब्लिश करता है, और ब्रोकर पावती मिलने के बाद ही उन्हें पब्लिश्ड के रूप में चिह्नित करता है। यदि रिले पब्लिश से पहले बंद हो जाता है, तो पंक्ति पेंडिंग रहती है; यदि यह ब्रोकर द्वारा स्वीकार किए जाने के बाद लेकिन मार्क करने से पहले बंद हो जाता है, तो यह फिर से पब्लिश करता है, इसलिए डिलीवरी कम से कम एक बार (at least once) होती है। प्रत्येक इवेंट की एक स्थिर आईडी होती है, और कंज्यूमर उस आईडी को अपने बिज़नेस अपडेट के समान ट्रांज़ैक्शन में एक डिडुप्लिकेशन टेबल में सम्मिलित करता है। मैं प्रति-ऑर्डर अनुक्रम आवंटित करूँगा, ब्रोकर पार्टीशन कुंजी के रूप में ऑर्डर आईडी का उपयोग करूँगा, बाद के इवेंट्स को पहले के पेंडिंग इवेंट से आगे निकलने से रोकूँगा, और सबसे पुरानी पेंडिंग आयु की निगरानी करूँगा। फिर मैं इनवेरिएंट्स को सत्यापित करने के लिए प्रत्येक कमिट, पब्लिश, पावती और कंज्यूमर सीमा पर क्रैश इंजेक्ट करूँगा।”

चरण-दर-चरण विस्तृत विश्लेषण

यह साबित करके शुरुआत करें कि स्पष्ट कॉल ऑर्डर क्यों विफल होते हैं। डेटाबेस-फर्स्ट प्रवाह में, डेटाबेस समय T1 पर कमिट कर सकता है और T2 पर ब्रोकर द्वारा स्वीकार किए जाने से पहले प्रोसेस रुक सकता है; ऑर्डर मौजूद है लेकिन कोई इवेंट नहीं है। HTTP अनुरोध को पुनः प्रयास करना पूर्ण समाधान नहीं है क्योंकि क्लाइंट पुनः प्रयास नहीं भी कर सकता है, और पुनः प्रयास ऑर्डर को डुप्लिकेट कर सकता है जब तक कि कमांड स्वयं आइडेम्पोटेंट न हो। ब्रोकर-फर्स्ट प्रवाह में, कंज्यूमर्स ऑर्डर ट्रांज़ैक्शन विफल होने से पहले ही इवेंट देख सकते हैं। कॉल्स को उलटने से केवल असंगति (inconsistency) उलट जाती है।

ड्यूरेबल इरादे को उस एकल एटॉमिक सीमा के अंदर ले जाएँ जिसका सर्विस मालिक है। एकल PostgreSQL ट्रांज़ैक्शन में, कमांड को मान्य करें, ऑर्डर को म्यूटेट करें, उस ऑर्डर के लिए अगला सीक्वेंस आवंटित करें, और एक इम्यूटैबल आउटबॉक्स रो डालें। या तो दोनों पंक्तियाँ कमिट होती हैं या कोई भी नहीं। एक प्रतिनिधि स्कीमा है:

sql
CREATE TABLE outbox_events (
  event_id uuid PRIMARY KEY,
  aggregate_type text NOT NULL,
  aggregate_id text NOT NULL,
  aggregate_sequence bigint NOT NULL,
  event_type text NOT NULL,
  schema_version integer NOT NULL,
  payload jsonb NOT NULL,
  occurred_at timestamptz NOT NULL DEFAULT now(),
  available_at timestamptz NOT NULL DEFAULT now(),
  claimed_by text,
  claim_until timestamptz,
  published_at timestamptz,
  attempt_count integer NOT NULL DEFAULT 0,
  last_error text,
  UNIQUE (aggregate_type, aggregate_id, aggregate_sequence)
);

CREATE INDEX outbox_dispatch_idx
ON outbox_events (available_at, occurred_at)
WHERE published_at IS NULL;

event_id प्रत्येक पुनः प्रयास के दौरान स्थिर रहता है। schema_version पेलोड इवोल्यूशन को स्पष्ट करता है। अद्वितीय एग्रीगेट सीक्वेंस दो इवेंट्स को एक ही लॉजिकल स्थिति पर आने से रोकता है। सीक्वेंस को एग्रीगेट के समान ट्रांज़ैक्शन और लॉकिंग नियम के तहत आवंटित किया जाना चाहिए; टाइमस्टैम्प या रिले प्रोसेसिंग ऑर्डर एक सुरक्षित विकल्प नहीं है। यदि एक ट्रांज़ैक्शन कई इवेंट्स उत्सर्जित करता है, तो उनके इच्छित क्रम में लगातार सीक्वेंस मान आवंटित करें।

एक पोलिंग रिले को एक छोटे ट्रांज़ैक्शन में एक छोटा बैच क्लेम करना चाहिए। यह FOR UPDATE SKIP LOCKED के साथ पंक्तियों का चयन कर सकता है, claimed_by और claim_until को अपडेट कर सकता है, और फिर कमिट कर सकता है; पर्सिस्टेड लीज़ रो लॉक जारी होने के बाद अन्य वर्कर्स को उसी पंक्ति को जानबूझकर प्रोसेस करने से रोकती है। इसे लंबे समय तक चलने वाले डेटाबेस लॉक के बाहर पब्लिश करना चाहिए और ब्रोकर द्वारा स्वीकार किए जाने के बाद ही पंक्ति को पब्लिश्ड चिह्नित करना चाहिए। लीज़ की समाप्ति वर्कर के बंद होने पर रिकवरी की अनुमति देती है। बैकऑफ़ और available_at एक विफल गंतव्य को एक टाइट रीट्राई लूप बनाने से रोकते हैं। नेटवर्क पब्लिकेशन के दौरान डेटाबेस ट्रांज़ैक्शन को खुला रखना कंटेन्शन को बढ़ाता है और फिर भी ब्रोकर के साथ एटॉमिसीटी नहीं बनाता है।

यहाँ एक अपरिहार्य पावती गैप है। ब्रोकर इवेंट E को ड्यूरेबली स्वीकार कर सकता है, जिसके बाद published_at सेट करने से पहले रिले क्रैश हो सकता है। रिकवरी पर, E को फिर से पब्लिश किया जाता है। पब्लिकेशन से पहले इसे चिह्नित करने से विपरीत, डेटा हानि (lossy) वाला गैप बन जाएगा। इसलिए रिले को सुरक्षित पक्ष—संभावित डुप्लिकेट्स—को चुनना चाहिए, और कंज्यूमर्स को डिडुप्लिकेट करना चाहिए।

ऐसे कंज्यूमर के लिए जिसका बिज़नेस प्रभाव डेटाबेस में है, संसाधित इवेंट आईडी को उसी ट्रांज़ैक्शन में संग्रहीत करें:

sql
CREATE TABLE processed_events (
  consumer_name text NOT NULL,
  event_id uuid NOT NULL,
  processed_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (consumer_name, event_id)
);

कंज्यूमर एक ट्रांज़ैक्शन शुरू करता है और (consumer_name, event_id) के लिए INSERT ... ON CONFLICT DO NOTHING RETURNING का उपयोग करता है। यह बिज़नेस परिवर्तन तभी लागू करता है जब इन्सर्ट एक रो लौटाता है, फिर कमिट करता है। कोई रो न लौटने का अर्थ है कि इवेंट पहले ही लागू हो चुका था, इसलिए डुप्लिकेट को परिवर्तन को दोहराए बिना स्वीकार किया जा सकता है। एक ट्रांज़ैक्शन में डिडुप्लिकेशन रो और दूसरे में बिज़नेस प्रभाव लिखना केवल एक नई डुअल-राइट समस्या पैदा करता है। डिडुप्लिकेशन रिकॉर्ड को कम से कम तब तक बनाए रखा जाना चाहिए जब तक कि पुराने इवेंट को रीप्ले किया जा सके।

यह लोकल इनबॉक्स गैर-ट्रांज़ैक्शनल बाहरी प्रभाव को एटॉमिक रूप से कवर नहीं करता है। पेमेंट एपीआई के लिए, प्रदाता की आइडेम्पोटेंसी की के रूप में event_id पास करें। यदि गंतव्य में कोई आइडेम्पोटेंसी समर्थन नहीं है, तो एक अन्य ड्यूरेबल कमांड/आउटबॉक्स प्लस समाधान (reconciliation) पेश करें, या एक प्रलेखित डुप्लिकेट जोखिम स्वीकार करें। यही चेतावनी ईमेल, वेबहुक और अन्य अपरिवर्तनीय कॉल्स पर लागू होती है।

ऑर्डरिंग बिज़नेस सीमा से मेल खानी चाहिए। प्रति ऑर्डर एक मोनोटोनिक सीक्वेंस असाइन करें, सीक्वेंस k + 1 को अप्रकाशित k से आगे न निकलने दें, और ब्रोकर पार्टीशन कुंजी के रूप में aggregate_id का उपयोग करें। क्लेम क्वेरी प्रत्येक एग्रीगेट के लिए केवल सबसे कम अप्रकाशित सीक्वेंस का चयन कर सकती है, या रिले ओनरशिप को aggregate_id के एक स्थिर हैश द्वारा विभाजित किया जा सकता है; दोनों में से किसी भी विकल्प को प्रति एग्रीगेट एक ऑर्डर्ड पब्लिशिंग लेन सुनिश्चित करनी चाहिए। कंज्यूमर डोमेन के अनुसार सीक्वेंस गैप को अस्वीकार, बफर या रीकॉन्साइल कर सकते हैं। एक ग्लोबल सीक्वेंस की आवश्यकता असंबद्ध ऑर्डर्स को सीरियलाइज़ कर देगी और प्रति-ऑर्डर इनवेरिएंट की मदद किए बिना उपलब्धता को कम कर देगी।

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

ऑपरेशन्स डिज़ाइन को पूरा करते हैं। पेंडिंग रो काउंट, सबसे पुरानी अप्रकाशित रो की आयु, डिस्पैच थ्रूपुट और विफलताएं, प्रयास संख्या, समाप्त हो चुके क्लेम, ब्रोकर पावती लेटेंसी, कंज्यूमर डिडुप्लिकेशन काउंट, क्वारंटाइन किए गए इवेंट्स और टेबल ग्रोथ की निगरानी करें। रीप्ले और ऑडिट क्षितिज के बाद ही बाउंडेड बैचों में प्रकाशित पंक्तियों को संग्रहीत या हटाएं। पॉइज़न इवेंट को एक सुविचारित नीति की आवश्यकता होती है: रीट्राई, क्वारंटाइन या मरम्मत। इसे छोड़ना प्रति-ऑर्डर क्रम का उल्लंघन कर सकता है, इसलिए उस एग्रीगेट के लिए बाद के इवेंट चुपचाप जारी नहीं रह सकते।

सत्यापन को सीमाओं को लक्षित करना चाहिए, न केवल सामान्य पथ (happy path) को। डेटाबेस कमिट से पहले, कमिट के बाद लेकिन प्रतिक्रिया से पहले, रिले क्लेम के दौरान, पब्लिश से पहले, ब्रोकर स्वीकृति के बाद लेकिन published_at से पहले, कंज्यूमर बिज़नेस कमिट के बाद लेकिन पावती से पहले, और क्लीनअप के दौरान विफलता इंजेक्ट करें। परीक्षणों को चार इनवेरिएंट स्थापित करने चाहिए:

  1. प्रत्येक कमिटेड बिज़नेस म्यूटेशन में ठीक एक ड्यूरेबल आउटबॉक्स इरादा होता है;
  2. प्रत्येक रोलबैक किए गए म्यूटेशन में कोई आउटबॉक्स इरादा नहीं होता है;
  3. प्रत्येक ड्यूरेबल इरादा रिकवरी के बाद अंततः कम से कम एक बार पब्लिश किया जाता है; और
  4. डुप्लिकेट डिलीवरी कंज्यूमर-दृश्यमान बिज़नेस प्रभाव को एक बार लागू करती है।

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

उच्च गुणवत्ता वाला नमूना उत्तर

“डेटाबेस और ब्रोकर एक एटॉमिक कमिट साझा नहीं करते हैं, इसलिए मैं सबसे पहले इवेंट के इरादे को डेटाबेस ट्रांज़ैक्शन का हिस्सा बनाऊँगा। ऑर्डर पंक्ति और एक इम्यूटैबल आउटबॉक्स पंक्ति एक साथ कमिट होते हैं। यदि ट्रांज़ैक्शन रोलबैक होता है, तो दोनों में से कोई भी मौजूद नहीं रहता है। यदि यह कमिट होता है और प्रोसेस तुरंत समाप्त हो जाता है, तो दूसरा प्रोसेस अभी भी आउटबॉक्स पंक्ति को देख सकता है।

एक रिले संक्षिप्त डेटाबेस ट्रांज़ैक्शन और एक समाप्त होने वाली लीज़ के साथ पेंडिंग पंक्तियों को क्लेम करता है, उन्हें पब्लिश करता है, और ब्रोकर पावती के बाद ही published_at सेट करता है। मैं नेटवर्क पर प्रतीक्षा करते समय डेटाबेस लॉक नहीं रखूँगा। ब्रोकर द्वारा स्वीकार किए जाने के बाद और स्थिति अपडेट से पहले अभी भी एक क्रैश विंडो है, इसलिए रिले डुप्लिकेट पब्लिश कर सकता है। यह सही फेलियर बायस (failure bias) है: एक डुप्लिकेट रिकवर करने योग्य है, जबकि एक खोया हुआ इवेंट नहीं है।

प्रत्येक इवेंट में एक स्थिर UUID होता है। एक डेटाबेस कंज्यूमर उस UUID को अपने बिज़नेस अपडेट के समान ट्रांज़ैक्शन में कंज्यूमर नाम द्वारा की गई टेबल में सम्मिलित करता है। एक डुप्लिकेट का टकराव होता है और वह नो-ऑप (no-op) बन जाता है। यदि कंज्यूमर किसी भुगतान या ईमेल प्रदाता को कॉल करता है, तो उसे इवेंट UUID को आइडेम्पोटेंसी की के रूप में पास करना होगा या किसी अन्य ड्यूरेबल हैंडऑफ़ का उपयोग करना होगा, क्योंकि लोकल डिडुप्लिकेशन ट्रांज़ैक्शन उस रिमोट प्रभाव को शामिल नहीं कर सकता है।

ऑर्डरिंग के लिए, मैं ऑर्डर ट्रांज़ैक्शन के तहत एक सीक्वेंस आवंटित करता हूँ, पार्टीशन कुंजी के रूप में ऑर्डर आईडी के साथ पब्लिश करता हूँ, और उस ऑर्डर के लिए बाद के सीक्वेंस को पहले के पेंडिंग इवेंट से आगे निकलने से रोकता हूँ। मैं ग्लोबल ऑर्डरिंग लागू नहीं करता। मैं पोलिंग से शुरुआत करूँगा जब तक कि लेटेंसी और लोड लक्ष्य CDC को उचित न ठहराएं, फिर सबसे पुरानी पेंडिंग आयु, पुनः प्रयास, समाप्त क्लेम, डुप्लिकेट दर, पॉइज़न इवेंट्स और टेबल ग्रोथ की निगरानी करूँगा।

अंत में, मैं हर सीमा पर प्रक्रियाओं को किल करूँगा। आवश्यक परिणाम हैं: रोलबैक कोई इरादा नहीं बनाता है, कमिट हमेशा एक इरादा छोड़ता है, रिकवरी हर इरादे को कम से कम एक बार पब्लिश करती है, और डुप्लिकेट डिलीवरी कंज्यूमर स्थिति को एक बार बदलती है। आउटबॉक्स विश्वसनीय हैंडऑफ़ को हल करता है; अनुरोध आइडेम्पोटेंसी, कंज्यूमर आइडेम्पोटेंसी, स्कीमा इवोल्यूशन और समाधान सिस्टम के स्पष्ट भाग बने रहते हैं।”

सामान्य गलतियाँ

  • डेटाबेस और ब्रोकर को क्रमिक रूप से कॉल करना → कोई भी कॉल अकेले सफल हो सकती है → एक लोकल डेटाबेस ट्रांज़ैक्शन में बिज़नेस म्यूटेशन और इवेंट इरादे को कमिट करें।
  • कमिट के बाद इन-मेमोरी पब्लिशर को कॉल करना → एक क्रैश कॉलबैक और उसकी स्थिति को खो देता है → वापस लौटने से पहले इरादे को पर्सिस्ट करें।
  • यह दावा करना कि आउटबॉक्स एक्ज़ैक्टली-वंस डिलीवरी देता है → ब्रोकर-स्वीकृत/स्थिति-दर्ज-नहीं गैप डुप्लिकेट बनाता है → एट-लीस्ट-वंस पब्लिकेशन बताएं और एक्ज़ैक्टली-वंस बिज़नेस प्रभाव डिज़ाइन करें।
  • ब्रोकर पावती से पहले पंक्ति को प्रकाशित चिह्नित करना → एक क्रैश स्थायी रूप से इवेंट को खो सकता है → पावती के बाद ही सफलता रिकॉर्ड करें और पुनः पब्लिकेशन को सहन करें।
  • कंज्यूमर प्रभाव से अलग डिडुप्लिकेशन स्थिति लिखना → कंज्यूमर वही डुअल-राइट गैप फिर से बना देता है → दोनों को एक लोकल ट्रांज़ैक्शन में रखें।
  • रिमोट चार्ज के संरक्षण के लिए लोकल इनबॉक्स का उपयोग करना → रिमोट प्रभाव ट्रांज़ैक्शन में शामिल नहीं हो सकता → डाउनस्ट्रीम आइडेम्पोटेंसी की, ड्यूरेबल हैंडऑफ़ और समाधान का उपयोग करें।
  • ऑर्डरिंग के रूप में टाइमस्टैम्प का उपयोग करना → क्लॉक और कन्करेंसी व्यवहार एक अद्वितीय कारणात्मक (causal) स्थिति आवंटित नहीं करते हैं → एक ट्रांज़ैक्शनल प्रति-एग्रीगेट सीक्वेंस आवंटित करें और एग्रीगेट को पार्टीशन की के रूप में उपयोग करें।
  • बिना क्लेम या लीज़ के कई पोलर्स चलाना → वर्कर्स जानबूझकर समान पंक्तियों पर रेस करते हैं → छोटे क्लेम, समाप्ति, छोटे बैच और एक पेंडिंग-रो इंडेक्स का उपयोग करें।
  • प्रकाशित और डिडुप्लिकेशन पंक्तियों को तुरंत हटाना → विलंबित पुनः प्रयास और रीप्ले पुराने प्रभावों को दोहरा सकते हैं → प्रलेखित रीप्ले और ऑडिट क्षितिज से क्लीनअप सेट करें।
  • केवल सफल पब्लिकेशन का परीक्षण करना → डिज़ाइन की गारंटियां क्रैश विंडोज़ में रहती हैं → प्रत्येक ड्यूरेबल सीमा से पहले और बाद में विफलता इंजेक्ट करें और इनवेरिएंट्स का दावा करें।

अनुवर्ती प्रश्न और उत्तर

अनुवर्ती 1: क्या होगा यदि गंतव्य ब्रोकर के बजाय एक बाहरी एपीआई है?

सोर्स ट्रांज़ैक्शन अभी भी एक आउटबॉक्स कमांड लिख सकता है। एक वर्कर आइडेम्पोटेंसी की के रूप में event_id के साथ एपीआई को कॉल करता है और प्रतिक्रिया रिकॉर्ड करता है। एक टाइमआउट अस्पष्ट है—रिमोट सर्विस कॉल पूरी कर चुकी हो सकती है—इसलिए केवल उसी की के साथ पुनः प्रयास करें। यदि एपीआई न तो आइडेम्पोटेंसी प्रदान करता है और न ही क्वेरी करने योग्य ऑपरेशन स्थिति, तो एक्ज़ैक्टली-वंस प्रभाव की गारंटी नहीं दी जा सकती; समाधान (reconciliation) जोड़ें या बिज़नेस अनुबंध में डुप्लिकेट जोखिम को उजागर करें।

अनुवर्ती 2: क्या होगा यदि कोई वर्कफ़्लो कई सेवाओं और डेटाबेस में फैला हो?

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

अनुवर्ती 3: क्या होगा यदि इंटरव्यूअर को सख्त ग्लोबल इवेंट ऑर्डर की आवश्यकता हो?

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

अनुवर्ती 4: डिज़ाइन सीडीसी (CDC) कनेक्टर आउटेज से कैसे उबरता है?

कमिटेड आउटबॉक्स पंक्तियाँ सत्य का स्रोत (source of truth) बनी रहती हैं। कनेक्टर लैग और डेटाबेस-लॉग रिटेंशन हेडरूम पर अलर्ट करें, कनेक्टर ऑफ़सेट को ड्यूरेबली स्टोर करें, और अंतिम स्वीकृत ऑफ़सेट से रीस्टार्ट का परीक्षण करें। डेटाबेस को आउटेज उद्देश्य के लिए पर्याप्त समय तक लॉग बनाए रखना चाहिए; अन्यथा एक स्नैपशॉट या नियंत्रित बैकफ़िल की आवश्यकता होती है। कंज्यूमर डिडुप्लिकेशन ओवरलैपिंग रेंज को फिर से चलाना सुरक्षित बनाता है।

अनुवर्ती 5: आपको ट्रांज़ैक्शनल आउटबॉक्स से कब बचना चाहिए?

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

सार्वजनिक स्रोत

संबंधित प्रश्न