2011-12-04 20 views
62

में मोंडोडीबी डेटाबेस कनेक्शन को बंद करने के लिए नोड MongoDB मूल चालक के माध्यम से नोडजेस और मोंगोडीबी के साथ काम करना। कुछ दस्तावेजों को पुनः प्राप्त करने और संशोधन करने की आवश्यकता है, फिर उन्हें वापस सुरक्षित करें। यह एक उदाहरण है:नोडजेस

db.open(function (err, db) { 
    db.collection('foo', function (err, collection) { 
    var cursor = collection.find({}); 
    cursor.each(function (err, doc) { 
     if (doc != null) { 
     doc.newkey = 'foo'; // Make some changes 
     db.save(doc); // Update the document 
     } else { 
     db.close(); // Closing the connection 
     } 
    }); 
    }); 
}); 
अतुल्यकालिक प्रकृति के साथ

, अगर दस्तावेज़ अपडेट करने की प्रक्रिया में अधिक समय लगता है, तो जब कर्सर दस्तावेजों के अंत तक पहुँच जाता है, डेटाबेस कनेक्शन बंद कर दिया है। डेटाबेस में सभी अपडेट सहेजे नहीं गए हैं।

यदि db.close() छोड़ा गया है, तो सभी दस्तावेज़ सही तरीके से अपडेट किए गए हैं, लेकिन एप्लिकेशन लटकता है, कभी बाहर नहीं निकलता है।

मैंने एक पोस्ट को अद्यतनों की संख्या ट्रैक करने के लिए काउंटर का उपयोग करने का सुझाव दिया, जब शून्य पर वापस आना, फिर डीबी बंद करें। लेकिन क्या मैं यहाँ कुछ गलत कर रहा हूँ? इस तरह की स्थिति को संभालने का सबसे अच्छा तरीका क्या है? संसाधन को मुक्त करने के लिए db.close() का उपयोग किया जाना चाहिए? या एक नया डीबी कनेक्शन खोलने की जरूरत है?

उत्तर

23

गिनती दृष्टिकोण के आधार पर यहां एक संभावित समाधान है (मैंने इसका परीक्षण नहीं किया है और इसमें कोई त्रुटि नहीं है, लेकिन इसे विचार व्यक्त करना चाहिए)।

मूल रणनीति है: कितनी रिकॉर्ड्स को अपडेट करने की आवश्यकता है, प्रत्येक रिकॉर्ड को असीमित रूप से और सफलता पर कॉलबैक को बचाने के लिए, जो गिनती को कम करेगा और डीबी को बंद कर देगा यदि गणना 0 तक पहुंच जाती है (जब अंतिम अपडेट खत्म)। {safe:true} का उपयोग करके हम यह सुनिश्चित कर सकते हैं कि प्रत्येक अपडेट सफल हो।

मोंगो सर्वर एक थ्रेड प्रति कनेक्शन का उपयोग करेगा, इसलिए यह किसी के लिए अच्छा है) बंद अप्रयुक्त कनेक्शन, या बी) पूल/पुन: उपयोग करें।

db.open(function (err, db) { 
    db.collection('foo', function (err, collection) { 
    var cursor = collection.find({}); 
    cursor.count(function(err,count)){ 
     var savesPending = count; 

     if(count == 0){ 
     db.close(); 
     return; 
     } 

     var saveFinished = function(){ 
     savesPending--; 
     if(savesPending == 0){ 
      db.close(); 
     } 
     } 

     cursor.each(function (err, doc) { 
     if (doc != null) { 
      doc.newkey = 'foo'; // Make some changes 
      db.save(doc, {safe:true}, saveFinished); 
     } 
     }); 
    }) 
    }); 
}); 
+5

@realguess, भी वहाँ संगामिति utils मदद कर सकते हैं तो आप इस चीजें करते हैं ताकि आप विवरणों के प्रबंधन की जरूरत नहीं है के लिए libs हैं। async.js देखें, उदाहरण के लिए https://github.com/caolan/async – mpobrien

+0

@mpobrien, क्या आप इस समस्या को हल करने के लिए एसिंक का उपयोग करने के बारे में विस्तार से बता सकते हैं? –

+0

क्या आपको लगता है कि यह समाधान अभी भी 2017 में हैं या आप बेहतर कुछ भी जानते हैं? मैं इस तरह कुछ के बारे में सोच रहा था, लेकिन क्या होगा यदि 'कर्सर.एच (फ़ंक्शन (गलती, दस्तावेज़) {' में एक एसिंक फ़ंक्शन कॉल करता है, जो इस प्रकार कॉलबैक में तर्क निष्पादित करेगा और संभावित रूप से प्रत्येक के बाद डेटाबेस की आवश्यकता होगी) 'खत्म हो जाता है? और क्या होगा यदि सॉफ़्टवेयर में बाद में परिवर्तन के बाद, कॉलबैक एक और एसिंक फ़ंक्शन को कॉल करता है (मुझे आशा है कि आपको विचार मिल जाएगा)? – watery

4

मैंने पाया कि काउंटर का उपयोग कर सरल परिदृश्य के लिए आवेदन कर सकते हैं, लेकिन जटिल स्थितियों में मुश्किल हो सकता है।

var dbQueryCounter = 0; 
var maxDbIdleTime = 5000; //maximum db idle time 

var closeIdleDb = function(connection){ 
    var previousCounter = 0; 
    var checker = setInterval(function(){ 
    if (previousCounter == dbQueryCounter && dbQueryCounter != 0) { 
     connection.close(); 
     clearInterval(closeIdleDb); 
    } else { 
     previousCounter = dbQueryCounter; 
    } 
    }, maxDbIdleTime); 
}; 

MongoClient.connect("mongodb://127.0.0.1:27017/testdb", function(err, connection)(
    if (err) throw err; 
    connection.collection("mycollection").find({'a':{'$gt':1}}).toArray(function(err, docs) { 
    dbQueryCounter ++; 
    }); 
    //do any db query, and increase the dbQueryCounter 
    closeIdleDb(connection); 
)); 

यह किसी भी डेटाबेस कनेक्शन के लिए एक सामान्य समाधान हो सकता है: यहाँ एक समाधान है कि मैं डेटाबेस कनेक्शन बंद करके आते हैं जब डेटाबेस कनेक्शन निष्क्रिय है। maxDbIdleTime को डीबी क्वेरी टाइमआउट या अधिक के समान मान के रूप में सेट किया जा सकता है।

यह बहुत ही सुरुचिपूर्ण नहीं है, लेकिन मैं ऐसा करने का बेहतर तरीका नहीं सोच सकता। मैं मोंडो डीबी और माइस्क्ल से पूछताछ करने वाली स्क्रिप्ट चलाने के लिए नोडजेज़ का उपयोग करता हूं, और यदि डेटाबेस कनेक्शन ठीक से बंद नहीं होते हैं तो स्क्रिप्ट हमेशा के लिए लटकती है।

+1

अरे, मैं जवाब की सराहना करता हूं हालांकि आपको क्लियरइल्ड डीबी से क्लियर में स्पष्ट इंटरवल को बदलने की आवश्यकता है :)। यह वास्तव में मुझे – RNikoopour

+0

काफी रोचक मदद की! – watery

0

मैं ऐसे समाधान के साथ आया जिसमें इस तरह का काउंटर शामिल है। यह एक गिनती() कॉल पर निर्भर नहीं है और न ही यह एक समय के लिए इंतजार कर रहा है। प्रत्येक() में सभी दस्तावेजों को समाप्त होने के बाद यह डीबी बंद कर देगा।

var mydb = {}; // initialize the helper object. 

mydb.cnt = {}; // init counter to permit multiple db objects. 

mydb.open = function(db) // call open to inc the counter. 
{ 
    if(!mydb.cnt[db.tag]) mydb.cnt[db.tag] = 1; 
    else mydb.cnt[db.tag]++; 
}; 

mydb.close = function(db) // close the db when the cnt reaches 0. 
{ 
    mydb.cnt[db.tag]--; 
    if (mydb.cnt[db.tag] <= 0) { 
    delete mydb.cnt[db.tag]; 
    return db.close(); 
    } 
    return null; 
}; 

आप db.each() या db.save() आप इन तरीकों का उपयोग सुनिश्चित करने के लिए काम करते हुए और जब किया बंद कर दिया डाटाबेस तैयार है की तरह एक फोन बनाने जा रहे हैं तो हर बार। ओपी से

उदाहरण:

foo = db.collection('foo'); 

mydb.open(db); // *** Add here to init the counter.** 
foo.find({},function(err,cursor) 
{ 
    if(err) throw err; 
    cursor.each(function (err, doc) 
    { 
    if(err) throw err; 
    if (doc != null) { 
     doc.newkey = 'foo'; 
     mydb.open(db); // *** Add here to prevent from closing prematurely ** 
     foo.save(doc, function(err,count) { 
     if(err) throw err; 
     mydb.close(db); // *** Add here to close when done. ** 
     }); 
    } else { 
     mydb.close(db); // *** Close like this instead. ** 
    } 
    }); 
}); 

अब, यह मान लिया गया है mydb.open() पिछले कॉलबैक से पहले के माध्यम से यह करता है कि प्रत्येक से पिछले कॉलबैक करने के लिए दूसरा प्रत्येक mydb.close को जाता है से()। ... तो, ज़ाहिर है, अगर यह एक मुद्दा है तो मुझे बताएं।

तो: एक डीबी कॉल से पहले एक mydb.open (डीबी) डालें और एक mydb डालें।कॉलबैक के वापसी बिंदु पर या डीबी कॉल के बाद (कॉल प्रकार के आधार पर) (डीबी) बंद करें।

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

13

यह pooled कनेक्शन का उपयोग और उसके बाद सफाई समारोह में) db.close फोन (अपने आवेदन के जीवन के अंत में करने के लिए सबसे अच्छा है:

process.on('SIGINT', cleanup); 
process.on('SIGTERM', cleanup); 

http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html

थोड़ा पुराने धागा देखें, लेकिन वैसे भी ।

+0

यह वास्तव में मेरे लिए समस्याएं पैदा करता है। कभी-कभी जब मैं अपनी सेवा को पुनरारंभ करता हूं तो मुझे मोंगो "टोपोलॉजी नष्ट कर दिया गया" त्रुटियां मिलती हैं क्योंकि कनेक्शन काट दिया जाता है। क्या मुझसे कुछ गलत हो रही है? – ifightcrime

+1

@ifightcrime: कनेक्शन बंद करने के दौरान, एक चल रही क्वेरी की तरह लगता है। निर्भर करता है, भले ही आपको समाप्त करने के लिए प्रश्नों की आवश्यकता हो। यदि आपने लिखा है कि आपको प्रतीक्षा करने की ज़रूरत है, तो मुझे लगता है कि आपको ट्रैक करना होगा कि वे मैन्युअल रूप से किए गए हैं। आप यह जानने का प्रयास कर सकते हैं कि यह वास्तव में यहां कैसे काम करता है: https://github.com/mongodb/node-mongodb-native/blob/2.1/lib/db.js#L366 – pkopac

1

उपरोक्त @mpobrien से सुझाव के आधार पर, मुझे इस संबंध में async मॉड्यूल अविश्वसनीय रूप से सहायक पाया गया है। यहां एक उदाहरण पैटर्न है जिसे मैं अपनाने आया हूं:

const assert = require('assert'); 
const async = require('async'); 
const MongoClient = require('mongodb').MongoClient; 

var mongodb; 

async.series(
    [ 
     // Establish Covalent Analytics MongoDB connection 
     (callback) => { 
      MongoClient.connect('mongodb://localhost:27017/test', (err, db) => { 
       assert.equal(err, null); 
       mongodb = db; 
       callback(null); 
      }); 
     }, 
     // Insert some documents 
     (callback) => { 
      mongodb.collection('sandbox').insertMany(
       [{a : 1}, {a : 2}, {a : 3}], 
       (err) => { 
        assert.equal(err, null); 
        callback(null); 
       } 
      ) 
     }, 
     // Find some documents 
     (callback) => { 
      mongodb.collection('sandbox').find({}).toArray(function(err, docs) { 
       assert.equal(err, null); 
       console.dir(docs); 
       callback(null); 
      }); 
     } 
    ], 
    () => { 
     mongodb.close(); 
    } 
); 
+0

क्या आप इस बारे में कुछ स्पष्टीकरण जोड़ सकते हैं कि यह कैसे काम करता है और हल करता है समस्या? मेरे जैसे उन लोगों के लिए जो एसिंक नहीं जानते हैं। – watery

+0

@ वाटरसी, async.series एक श्रृंखला में एसिंक्रोनस फ़ंक्शंस का आह्वान करने का एक तरीका प्रदान करता है, जहां बाद के फ़ंक्शन को सफलतापूर्वक पूरा नहीं होने तक लागू नहीं किया जाता है। यह अंत में एक वैकल्पिक कॉलबैक प्रदान करता है, सरणी/ऑब्जेक्ट में सभी कार्यों को सफलतापूर्वक पूरा करने के बाद, जो मैं इस मामले में डेटाबेस कनेक्शन को बंद करने के लिए उपयोग कर रहा हूं। –

0

यहां एक समाधान है जिसके साथ मैं आया हूं। यह toArray का उपयोग कर टाल और यह बहुत छोटा और प्रभावी है:

var MongoClient = require('mongodb').MongoClient; 

MongoClient.connect("mongodb://localhost:27017/mydb", function(err, db) { 
    let myCollection = db.collection('myCollection'); 
    let query = {}; // fill in your query here 
    let i = 0; 
    myCollection.count(query, (err, count) => { 
    myCollection.find(query).forEach((doc) => { 
     // do stuff here 
     if (++i == count) db.close(); 
    }); 
    }); 
}); 
+0

क्या होगा यदि अंततः डेटाबेस में लिखने वाले अन्य एसिंक कॉल '// do stuff' भाग में हैं? क्या वे इसे बंद नहीं पाएंगे? – watery

संबंधित मुद्दे