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

डेटाबेस और मैसेज निरंतरता के लिए आप एक ट्रांजेक्शनल आउटबॉक्स कैसे डिज़ाइन करते हैं?

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

प्रश्न

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

1. प्रश्न

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

2. बाधाएं और स्पष्टीकरण

  • ऑर्डर डेटा और आउटबॉक्स तालिका एक ही स्थानीय ट्रांजेक्शनल डेटाबेस साझा करते हैं।
  • ब्रोकर ग्लोबल ऑर्डरिंग या ट्रांजेक्शनल सेंड के बिना, एट-लीस्ट-वन्स डिलीवरी प्रदान करता है।
  • इवेंचुअल कंसिस्टेंसी स्वीकार्य है; इन्वेंट्री उपभोक्ता को आइडेम्पोटेंट होना चाहिए।
  • प्रति-एग्रीगेट ऑर्डरिंग, क्या क्रॉस-एग्रीगेट ऑर्डरिंग की आवश्यकता है, और रिटेंशन/डिलीशन विंडो की व्याख्या करें।

3. मुख्य दृष्टिकोण

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

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

4. संदर्भ कार्यान्वयन

text
createOrder(command):
  begin transaction
  order = insert orders(...)
  event = insert outbox(
    event_id=uuid(), aggregate_id=order.id,
    aggregate_version=order.version, type="OrderCreated",
    payload=serialize(order), status="pending"
  )
  commit
  return order.id

relayBatch():
  rows = select pending outbox rows
         order by aggregate_id, aggregate_version, created_at
         for update skip locked limit BATCH_SIZE
  for row in rows:
    try:
      broker.publish(key=row.aggregate_id, id=row.event_id, body=row.payload)
      mark_sent(row.event_id)  // conditional update
    except transient_error:
      increment_attempts_and_schedule_retry(row.event_id)

consume(message):
  begin transaction
  inserted = insert processed_messages(message.id) on conflict do nothing
  if inserted:
    apply_business_change(message)
  commit

5. विश्वसनीयता और सटीकता

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

प्रति-एग्रीगेट ऑर्डरिंग एक मोनोटोनिक संस्करण और एग्रीगेट कुंजी द्वारा विभाजन (partitioning) का उपयोग कर सकती है; एग्रीगेट्स में ग्लोबल ऑर्डरिंग का वादा न करें। SELECT ... FOR UPDATE SKIP LOCKED या लीज फ़ील्ड कई रिले को एक ही पंक्ति पर दावा करने से रोकते हैं, लेकिन वे उपभोक्ता आइडेम्पोटेंसी को प्रतिस्थापित नहीं करते हैं। स्थिति, पुनः प्रयास समय और निर्माण समय को इंडेक्स करें, फिर तालिका वृद्धि को सीमित करने के लिए पुरानी पंक्तियों को आर्काइव करें या सुरक्षित रूप से हटाएं।

6. फॉलो-अप और जाल

  • "सफल" प्रकाशन के तुरंत बाद एक पंक्ति को हटाने से एक ऐसा गैप बन सकता है जिसे पुनर्प्राप्त नहीं किया जा सकता यदि एक्नॉलेजमेंट खो गया था; पहले सेंड स्थिति को बनाए रखें या एक ऑडिट रिकॉर्ड बनाए रखें।
  • ब्रोकर एक्नॉलेजमेंट टाइमआउट यह साबित नहीं करता है कि ब्रोकर से संदेश छूट गया है, इसलिए पुनः प्रयासों में डुप्लिकेट को सहन करना होगा।
  • पहले डेटाबेस में लिखना और फिर एप्लिकेशन एरर हैंडलिंग के अंदर ब्रोकर को कॉल करने में अभी भी डुअल-राइट रेस होती है; try/catch इसे एटॉमिक नहीं बना सकता।
  • यदि आउटबॉक्स और व्यावसायिक तालिकाएं एक ट्रांजेक्शन सीमा साझा नहीं कर सकती हैं, तो CDC, ट्रांजेक्शनल मैसेजिंग का उपयोग करें, या निरंतरता गारंटी को फिर से परिभाषित करें।

7. आगे पढ़ना

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

8. साक्षात्कार स्कोरिंग बिंदु

डुअल-राइट विंडो का पता लगा सकते हैं

उम्मीदवार को यह समझाना चाहिए कि सामान्य स्थानीय ट्रांजेक्शन डेटाबेस अपडेट और ब्रोकर सेंड को एक साथ कमिट क्यों नहीं कर सकते, फिर ऑर्डर परिवर्तन और आउटबॉक्स पंक्ति को एक ट्रांजेक्शन में रखें।

एट-लीस्ट-वन्स और आइडेम्पोटेंसी की व्याख्या कर सकते हैं

उन्हें रिले क्रैश विंडो का वर्णन करना चाहिए जो डुप्लिकेट बनाती है और उपभोक्ता को उसके व्यावसायिक अपडेट के समान ट्रांजेक्शन में इवेंट आईडी द्वारा डीडुप्लिकेट करना चाहिए।

ऑर्डरिंग और कॉनक्रेन्सी को संभाल सकते हैं

उन्हें प्रति-एग्रीगेट ऑर्डर को ग्लोबल ऑर्डर से अलग करना चाहिए और समझाना चाहिए कि विभाजन कुंजी, संस्करण, लॉक या लीज समवर्ती दावों को कैसे सीमित करते हैं।

परिचालन सीमाओं को कवर कर सकते हैं

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

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

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