संपादित करें - क्वेरी प्रदर्शन:
जैसा कि @NeilLunn ने अपनी टिप्पणियों में बताया है, आपको दस्तावेज़ों को मैन्युअल रूप से फ़िल्टर नहीं करना चाहिए, बल्कि .find(...)
का उपयोग करना चाहिए। इसके बजाय:
db.snapshots.find({
roundedDate: { $exists: true },
stream: { $exists: true },
sid: { $exists: false }
})
साथ ही, .bulkWrite()
. का उपयोग करके , MongoDB 3.2
. से उपलब्ध है , व्यक्तिगत अपडेट करने की तुलना में कहीं अधिक प्रदर्शनकारी होगा।
यह संभव है कि, इसके साथ, आप कर्सर के 10 मिनट के जीवनकाल के भीतर अपनी क्वेरी निष्पादित करने में सक्षम हों। यदि यह अभी भी इससे अधिक लेता है, तो आपका कर्सर समाप्त हो जाएगा और आपको वैसे भी वही समस्या होगी, जिसे नीचे समझाया गया है:
यहाँ क्या हो रहा है:
Error: getMore command failed
कर्सर टाइमआउट के कारण हो सकता है, जो दो कर्सर विशेषताओं से संबंधित है:
-
समयबाह्य सीमा, जो डिफ़ॉल्ट रूप से 10 मिनट है। डॉक्स से:
<ब्लॉकक्वॉट>डिफ़ॉल्ट रूप से, सर्वर 10 मिनट की निष्क्रियता के बाद कर्सर को स्वचालित रूप से बंद कर देगा, या यदि क्लाइंट ने कर्सर को समाप्त कर दिया है।
-
बैच का आकार, जो पहले बैच के लिए 101 दस्तावेज़ या 16 एमबी है, और बाद के बैचों के लिए दस्तावेजों की संख्या की परवाह किए बिना 16 एमबी (MongoDB
<ब्लॉकक्वॉट>3.4
के अनुसार) ) डॉक्स से:find()
औरaggregate()
संचालन में डिफ़ॉल्ट रूप से 101 दस्तावेज़ों का प्रारंभिक बैच आकार होता है। परिणामी कर्सर के विरुद्ध जारी किए गए बाद के getMore संचालन में कोई डिफ़ॉल्ट बैच आकार नहीं होता है, इसलिए वे केवल 16 मेगाबाइट संदेश आकार तक सीमित होते हैं।
संभवत:आप उन शुरुआती 101 दस्तावेजों का उपभोग कर रहे हैं और फिर 16 एमबी बैच प्राप्त कर रहे हैं, जो कि अधिक से अधिक दस्तावेजों के साथ अधिकतम है। चूंकि उन्हें संसाधित करने में 10 मिनट से अधिक समय लग रहा है, सर्वर पर कर्सर का समय समाप्त हो जाता है और, जब तक आप दूसरे बैच में दस्तावेज़ों को संसाधित करते हैं और एक नए का अनुरोध करते हैं, तब तक कर्सर पहले ही बंद हो चुका होता है:
<ब्लॉकक्वॉट>जैसे ही आप कर्सर के माध्यम से पुनरावृति करते हैं और लौटे बैच के अंत तक पहुँचते हैं, यदि अधिक परिणाम हैं, तो कर्सर.नेक्स्ट () अगले बैच को पुनः प्राप्त करने के लिए एक गेटमोर ऑपरेशन करेगा।
संभावित समाधान:
मैं इसे हल करने के 5 संभावित तरीके देखता हूं, 3 अच्छे, उनके फायदे और नुकसान के साथ, और 2 बुरे तरीके:
-
कर्सर को जीवित रखने के लिए बैच का आकार कम करना।
-
👍 कर्सर से टाइमआउट निकालें।
-
👍 कर्सर के समाप्त होने पर पुनः प्रयास करें।
-
परिणामों को मैन्युअल रूप से बैचों में क्वेरी करें।
-
👎 कर्सर की समय सीमा समाप्त होने से पहले सभी दस्तावेज़ प्राप्त करें।
ध्यान दें कि वे किसी विशिष्ट मानदंड के बाद क्रमांकित नहीं हैं। उनके माध्यम से पढ़ें और तय करें कि आपके विशेष मामले के लिए कौन सा सबसे अच्छा काम करता है।
<एच4>1. 👍 कर्सर को जीवित रखने के लिए बैच का आकार कम करना
इसे हल करने का एक तरीका है cursor.bacthSize
. का उपयोग करना आपके द्वारा लौटाए गए कर्सर पर बैच आकार सेट करने के लिए find
उन प्रश्नों से मेल खाने के लिए जिन्हें आप उन 10 मिनट में संसाधित कर सकते हैं:
const cursor = db.collection.find()
.batchSize(NUMBER_OF_DOCUMENTS_IN_BATCH);
हालांकि, ध्यान रखें कि एक बहुत ही रूढ़िवादी (छोटा) बैच आकार सेट करना शायद काम करेगा, लेकिन यह धीमा भी होगा, क्योंकि अब आपको सर्वर को अधिक बार एक्सेस करने की आवश्यकता है।
दूसरी ओर, इसे 10 मिनट में आपके द्वारा संसाधित किए जा सकने वाले दस्तावेज़ों की संख्या के बहुत करीब मान पर सेट करने का अर्थ है कि यह संभव है कि यदि किसी कारण से कुछ पुनरावृत्तियों को संसाधित होने में थोड़ा अधिक समय लगता है (अन्य प्रक्रियाएं अधिक संसाधनों का उपभोग कर सकती हैं) , कर्सर वैसे भी समाप्त हो जाएगा और आपको वही त्रुटि फिर से मिलेगी।
<एच4>2. 👍 कर्सर से टाइमआउट निकालेंएक अन्य विकल्प कर्सर को टाइम आउट होने से रोकने के लिए कर्सर.noCursorTimeout का उपयोग करना है:
const cursor = db.collection.find().noCursorTimeout();
इसे एक बुरा अभ्यास माना जाता है क्योंकि आपको कर्सर को मैन्युअल रूप से बंद करना होगा या इसके सभी परिणामों को समाप्त करना होगा ताकि यह स्वचालित रूप से बंद हो जाए:
<ब्लॉकक्वॉट>
noCursorTimeout
सेट करने के बाद विकल्प, आपको या तो cursor.close()
. के साथ मैन्युअल रूप से कर्सर को बंद करना होगा या कर्सर के परिणामों को समाप्त करके।
जैसा कि आप कर्सर में सभी दस्तावेज़ों को संसाधित करना चाहते हैं, आपको इसे मैन्युअल रूप से बंद करने की आवश्यकता नहीं होगी, लेकिन यह अभी भी संभव है कि आपके कोड में कुछ और गलत हो और आपके द्वारा किए जाने से पहले एक त्रुटि हो जाए, इस प्रकार कर्सर को खुला छोड़ दिया जाता है ।
यदि आप अभी भी इस दृष्टिकोण का उपयोग करना चाहते हैं, तो try-catch
. का उपयोग करें यह सुनिश्चित करने के लिए कि आप कर्सर के सभी दस्तावेज़ों का उपभोग करने से पहले कुछ भी गलत होने पर कर्सर को बंद कर दें।
नोट मैं इसे एक बुरा समाधान नहीं मानता (इसलिए 👍), जैसा कि सोचा भी कि इसे एक बुरा अभ्यास माना जाता है...:
-
यह ड्राइवर द्वारा समर्थित एक विशेषता है। यदि यह इतना बुरा था, क्योंकि टाइमआउट मुद्दों को हल करने के वैकल्पिक तरीके हैं, जैसा कि अन्य समाधानों में बताया गया है, यह समर्थित नहीं होगा।
-
इसे सुरक्षित रूप से उपयोग करने के तरीके हैं, बस इसके साथ अतिरिक्त सतर्क रहने की बात है।
-
मुझे लगता है कि आप इस तरह के प्रश्नों को नियमित रूप से नहीं चला रहे हैं, इसलिए संभावना है कि आप हर जगह खुले कर्सर छोड़ना शुरू कर दें। यदि ऐसा नहीं है, और आपको वास्तव में हर समय इन स्थितियों से निपटने की आवश्यकता है, तो इसका कोई मतलब नहीं है कि
noCursorTimeout
का उपयोग न करें। ।
मूल रूप से, आप अपना कोड try-catch
. में डालते हैं और जब आपको त्रुटि मिलती है, तो आपको एक नया कर्सर मिलता है जो उन दस्तावेज़ों को छोड़ देता है जिन्हें आपने पहले ही संसाधित कर लिया है:
let processed = 0;
let updated = 0;
while(true) {
const cursor = db.snapshots.find().sort({ _id: 1 }).skip(processed);
try {
while (cursor.hasNext()) {
const doc = cursor.next();
++processed;
if (doc.stream && doc.roundedDate && !doc.sid) {
db.snapshots.update({
_id: doc._id
}, { $set: {
sid: `${ doc.stream.valueOf() }-${ doc.roundedDate }`
}});
++updated;
}
}
break; // Done processing all, exit outer loop
} catch (err) {
if (err.code !== 43) {
// Something else than a timeout went wrong. Abort loop.
throw err;
}
}
}
ध्यान दें कि इस समाधान के काम करने के लिए आपको परिणामों को क्रमबद्ध करना होगा।
इस दृष्टिकोण के साथ, आप 16 एमबी के अधिकतम संभव बैच आकार का उपयोग करके सर्वर से अनुरोधों की संख्या को कम कर रहे हैं, बिना यह अनुमान लगाए कि आप पहले से 10 मिनट में कितने दस्तावेजों को संसाधित करने में सक्षम होंगे। इसलिए, यह पिछले दृष्टिकोण की तुलना में अधिक मजबूत भी है।
<एच4>4. परिणामों को मैन्युअल रूप से बैचों में क्वेरी करेंमूल रूप से, आप स्किप (), सीमा () और सॉर्ट () का उपयोग कई दस्तावेज़ों के साथ कई प्रश्नों को करने के लिए करते हैं जो आपको लगता है कि आप 10 मिनट में संसाधित कर सकते हैं।
मैं इसे एक खराब समाधान मानता हूं क्योंकि ड्राइवर के पास पहले से ही बैच आकार सेट करने का विकल्प है, इसलिए इसे मैन्युअल रूप से करने का कोई कारण नहीं है, बस समाधान 1 का उपयोग करें और पहिया को फिर से न लगाएं।
साथ ही, यह ध्यान देने योग्य है कि समाधान 1 की तुलना में इसमें समान कमियां हैं,
5. 👎 कर्सर की समय सीमा समाप्त होने से पहले सभी दस्तावेज़ प्राप्त करें
परिणाम प्रसंस्करण के कारण संभवतः आपके कोड को निष्पादित होने में कुछ समय लग रहा है, इसलिए आप पहले सभी दस्तावेज़ों को पुनः प्राप्त कर सकते हैं और फिर उन्हें संसाधित कर सकते हैं:
const results = new Array(db.snapshots.find());
यह सभी बैचों को एक के बाद एक पुनर्प्राप्त करेगा और कर्सर को बंद कर देगा। फिर, आप results
. के अंदर सभी दस्तावेज़ों को देख सकते हैं और वह करें जो आपको करने की आवश्यकता है।
हालांकि, अगर आपको टाइमआउट की समस्या हो रही है, तो संभावना है कि आपका परिणाम सेट काफी बड़ा है, इस प्रकार सब कुछ स्मृति में खींचना सबसे उचित काम नहीं हो सकता है।
स्नैपशॉट मोड और डुप्लीकेट दस्तावेज़ों के बारे में नोट करें
यह संभव है कि कुछ दस्तावेज़ कई बार लौटाए जाते हैं यदि दस्तावेज़ के आकार में वृद्धि के कारण हस्तक्षेप करने वाले लेखन कार्य उन्हें स्थानांतरित कर देते हैं। इसे हल करने के लिए, cursor.snapshot()
use का उपयोग करें . डॉक्स से:
"स्नैपशॉट" मोड को चालू करने के लिए स्नैपशॉट () विधि को कर्सर में जोड़ें। यह सुनिश्चित करता है कि क्वेरी एक दस्तावेज़ को कई बार वापस नहीं लौटाएगी, भले ही दस्तावेज़ के आकार में वृद्धि के कारण लिखने के संचालन में हस्तक्षेप के परिणामस्वरूप दस्तावेज़ में कोई बदलाव आया हो।
हालांकि, इसकी सीमाओं को ध्यान में रखें:
-
यह शार्प किए गए संग्रहों के साथ काम नहीं करता है।
-
यह
sort()
के साथ काम नहीं करता है याhint()
, इसलिए यह समाधान 3 और 4 के साथ काम नहीं करेगा। -
यह सम्मिलन या विलोपन से अलगाव की गारंटी नहीं देता है।
समाधान 5 के साथ नोट करें कि दस्तावेज़ों को स्थानांतरित करने के लिए समय खिड़की जो डुप्लिकेट दस्तावेज़ पुनर्प्राप्ति का कारण बन सकती है, अन्य समाधानों की तुलना में संकीर्ण है, इसलिए आपको snapshot()
की आवश्यकता नहीं हो सकती है ।
आपके विशेष मामले में, जैसा कि संग्रह को snapshot
. कहा जाता है , शायद इसके बदलने की संभावना नहीं है, इसलिए आपको शायद snapshot()
. की आवश्यकता नहीं है . इसके अलावा, आप उनके डेटा के आधार पर दस्तावेज़ों पर अपडेट कर रहे हैं और, एक बार अपडेट हो जाने के बाद, वही दस्तावेज़ फिर से अपडेट नहीं किया जाएगा, भले ही इसे कई बार पुनर्प्राप्त किया गया हो, जैसे कि if
शर्त इसे छोड़ देगी।
खुले कर्सर के बारे में ध्यान दें
खुले कर्सर की संख्या देखने के लिए db.serverStatus().metrics.cursor
का उपयोग करें ।