समस्या और संदर्भ
PostgreSQL 18 RETURNING को INSERT, UPDATE, DELETE, और MERGE के लिए पुरानी और नई पंक्ति के मानों को स्पष्ट रूप से प्रदर्शित करने की अनुमति देता है। परिणाम उसी डेटा-परिवर्तनकारी स्टेटमेंट द्वारा निर्मित होता है, जो किसी अन्य राइटर के साथ रेस करने वाले फॉलो-अप क्वेरी से बचा सकता है। जब किसी ऑपरेशन के लिए वह पक्ष मौजूद नहीं होता है, तो पुराना या नया पक्ष NULL हो सकता है।
मान लें कि ऑडिट इवेंट्स को एक स्टेटमेंट द्वारा बदले गए सटीक मानों की आवश्यकता होती है, पुनः प्रयास (retries) इडेम्पोटेंट होने चाहिए, और एप्लिकेशन म्यूटेशन और ऑडिट उत्सर्जन के बीच दूसरा रीड वहन नहीं कर सकता है।
इंटरव्यूअर्स क्या मूल्यांकन करते हैं
इंटरव्यूअर्स स्टेटमेंट-स्तरीय एटॉमिकिटी, सही ऑपरेशन सिमेंटिक्स, और ट्रांजैक्शन सीमाओं व पुनः प्रयासों के लिए एक योजना की तलाश करते हैं। मजबूत उत्तर INSERT, UPDATE, DELETE, ON CONFLICT, और MERGE के बीच अंतर करते हैं, और समझाते हैं कि कमिट के सापेक्ष ऑडिट डिलीवरी कहाँ होनी चाहिए।
एक साधारण उत्तर लिखने के बाद पंक्ति को फिर से सेलेक्ट करता है। एक मजबूत उत्तर म्यूटेशन आउटपुट के रूप में RETURNING का उपयोग करता है, ऑपरेशन की पहचान बनाए रखता है, और ऐसे इवेंट को प्रकाशित करने से बचाता है जिसे ट्रांजैक्शन बाद में रोलबैक कर देता है।
पहले स्पष्ट करने योग्य प्रश्न
- क्या ऑडिट इवेंट केवल कमिट के बाद ही उत्सर्जित होना चाहिए, या एक आउटबॉक्स पंक्ति पर्याप्त है?
- क्या एक स्टेटमेंट कई पंक्तियों को बदल सकता है, और इवेंट आईडी कैसे असाइन किए जाते हैं?
- एक upsert संघर्ष (conflict) और प्रत्येक
MERGEक्रिया के लिए "old" का क्या अर्थ है? - पुनः प्रयास डुप्लिकेट ऑडिट इवेंट्स से कैसे बचेंगे?
- क्या ऐसे कॉलम हैं जिन्हें डेटाबेस से बाहर जाने से पहले रिडैक्ट (redact) किया जाना चाहिए?
यदि डाउनस्ट्रीम डिलीवरी कमिट के बाद होनी चाहिए, तो उसी ट्रांजैक्शन में एक आउटबॉक्स पंक्ति डालें और एसिंक्रोनस रूप से प्रकाशित करें। यदि ऑडिट केवल एक आंतरिक SQL रिपोर्ट के लिए है, तो RETURNING को कॉलर द्वारा सीधे उपभोग किया जा सकता है।
30-सेकंड का उत्तर
“मैं म्यूटेशन स्टेटमेंट से एक स्पष्ट ऑपरेशन, पुराने मान, नए मान और एक स्थिर इवेंट कुंजी लौटाने की व्यवस्था करूँगा। मल्टी-रो राइट्स के लिए मैं उन परिणामों को उसी ट्रांजैक्शन में एक आउटबॉक्स में डालूँगा, फिर कमिट के बाद प्रकाशित करूँगा। मैं इन्सर्ट और डिलीट के लिए NULL पक्ष का परीक्षण करूँगा, upsert और MERGE सिमेंटिक्स को परिभाषित करूँगा, संवेदनशील कॉलम को रिडैक्ट करूँगा, और इवेंट कुंजी का उपयोग करके पुनः प्रयासों को इडेम्पोटेंट बनाऊँगा।”
चरण-दर-चरण डिज़ाइन
- इवेंट अनुबंध को परिभाषित करें। टेबल पहचान, प्राइमरी की, ऑपरेशन, पुराना प्रोजेक्शन, नया प्रोजेक्शन, ट्रांजैक्शन या रिक्वेस्ट आईडी, और एक इडेम्पोटेन्सी कुंजी शामिल करें।
- स्पष्ट उपनामों (aliases) का उपयोग करें।
RETURNING *पर निर्भर रहने के बजायRETURNING WITH (OLD AS old_row, NEW AS new_row)या समकक्ष दस्तावेजीकृत सिंटैक्स लिखें। - ऑपरेशन सिमेंटिक्स को संभालें। इन्सर्ट में सामान्यतः कोई पुरानी पंक्ति नहीं होती है; डिलीट में सामान्यतः कोई नई पंक्ति नहीं होती है। Upserts और
MERGEको एक शाखा-विशिष्ट ऑपरेशन मान की आवश्यकता होती है। - एटॉमिक रूप से स्टोर करें। लौटाए गए रिकॉर्ड्स को उसी ट्रांजैक्शन के भीतर एक आउटबॉक्स में डालें। कमिट से पहले एक अलग रीड या बाहरी पब्लिश उस स्थिति को देख सकता है जो बाद में रोलबैक हो जाती है।
- डेटा और पुनः प्रयासों को सुरक्षित रखें। फ़ील्ड्स को रिडैक्ट करें, जहाँ उपयुक्त हो संवेदनशील मानों को हैश करें, और एक अद्वितीय इवेंट कुंजी लागू करें ताकि पुनः प्रयास किया गया स्टेटमेंट डिलीवरी को डुप्लिकेट न करे।
- समवर्तीता (concurrency) सत्यापित करें। समवर्ती राइटर्स, संघर्षों, रोलबैक, मल्टी-रो स्टेटमेंट्स, और आंशिक
MERGEशाखाओं को चलाएँ। ऑडिट पंक्तियों की तुलना कमिट की गई टेबल स्थिति से करें।
विकल्पों में केंद्रीय प्रवर्तन के लिए ट्रिगर्स, डेटाबेस-व्यापी कैप्चर के लिए लॉजिकल डिकोडिंग, या डोमेन सिमेंटिक्स के लिए एप्लिकेशन इवेंट्स शामिल हैं। RETURNING तब सबसे मजबूत होता है जब म्यूटेशन के पास पहले से ही सटीक पंक्ति-स्तरीय परिवर्तन होता है।
उदाहरण उत्तर
“एप्लिकेशन अपडेट एक स्पष्ट ऑपरेशन, प्राइमरी की, पुराना प्रोजेक्शन, नया प्रोजेक्शन और इवेंट कुंजी लौटाता है। एक upsert संघर्ष के लिए मैं परिणाम को update के रूप में लेबल करता हूँ; MERGE के लिए, प्रत्येक शाखा अपना ऑपरेशन प्रदान करती है। ट्रांजैक्शन प्रत्येक लौटाई गई पंक्ति को एक आउटबॉक्स में डालता है और एक बार कमिट करता है। एक वर्कर एक अद्वितीय इवेंट कुंजी के साथ कमिट के बाद प्रकाशित करता है और सुरक्षित रूप से पुनः प्रयास करता है। डिलीट में एक शून्य (null) नया प्रोजेक्शन होता है, इन्सर्ट में एक शून्य पुराना प्रोजेक्शन होता है, और इवेंट के डेटाबेस से बाहर जाने से पहले संवेदनशील कॉलम हटा दिए जाते हैं।”
सामान्य गलतियाँ
- त्रुटि: म्यूटेशन के बाद फिर से क्वेरी करना → यह क्यों विफल होता है: कोई अन्य राइटर पंक्ति को बदल सकता है → समाधान: उसी स्टेटमेंट से
RETURNINGका उपभोग करें। - त्रुटि: कमिट से पहले प्रकाशित करना → यह क्यों विफल होता है: एक ऑडिट इवेंट रोलबैक किए गए डेटा का वर्णन कर सकता है → समाधान: एक ट्रांजैक्शनल आउटबॉक्स का उपयोग करें।
- त्रुटि: प्रत्येक upsert को insert मानना → यह क्यों विफल होता है: संघर्ष अपडेट को अलग-अलग सिमेंटिक्स की आवश्यकता होती है → समाधान: एक स्पष्ट ऑपरेशन उत्सर्जित करें।
- त्रुटि: सभी कॉलम को बिना सोचे-समझे लौटाना → यह क्यों विफल होता है: सीक्रेट्स या व्यक्तिगत डेटा लीक हो जाते हैं → समाधान: एक अनुमति-सूची (allow-list) प्रोजेक्शन और रिडेक्शन का उपयोग करें।
फॉलो-अप प्रश्न और उत्तर
एक INSERT के लिए OLD में क्या शामिल होता है?
सामान्यतः कोई पूर्व पंक्ति मौजूद नहीं होती है, इसलिए पुराना पक्ष NULL होता है। इवेंट अनुबंध को डिफ़ॉल्ट पंक्ति का आविष्कार करने के बजाय उस अनुपस्थिति को मॉडल करना चाहिए।
आप एक मल्टी-रो MERGE को कैसे कैप्चर करते हैं?
प्रत्येक प्रभावित पंक्ति के लिए एक RETURNING परिणाम का उपभोग करें, शाखा ऑपरेशन को शामिल करें, और प्रत्येक इवेंट को उसी आउटबॉक्स ट्रांजैक्शन में डालें।
यदि आउटबॉक्स इन्सर्ट विफल हो जाता है तो क्या होगा?
ट्रांजैक्शन को विफल होना चाहिए और म्यूटेशन को रोलबैक करना चाहिए। इसके ऑडिट रिकॉर्ड को चुपचाप हटाते हुए व्यावसायिक राइट को स्वीकार न करें।
आप लॉजिकल डिकोडिंग को कब प्राथमिकता देंगे?
व्यापक डेटाबेस-व्यापी परिवर्तन कैप्चर या ऐसे सिस्टम के लिए लॉजिकल डिकोडिंग का उपयोग करें जो स्टेटमेंट्स को संशोधित नहीं कर सकते हैं। जब एप्लिकेशन को डोमेन-जागरूक प्रोजेक्शन और सटीक प्रति-स्टेटमेंट सिमेंटिक्स की आवश्यकता होती है, तो RETURNING को प्राथमिकता दें।