MongoDB
 sql >> डेटाबेस >  >> NoSQL >> MongoDB

System.TimeoutException:30000ms के बाद CompositeServerSelector का उपयोग कर सर्वर का चयन करने के बाद एक टाइमआउट हुआ

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

यहां पुन:पेश करने का चरण है:

  1. MongoDB के CSharp ड्राइवर का सोर्स कोड डाउनलोड करें ।
  2. उस समाधान को खोलें और ड्राइवर प्रोजेक्ट को संदर्भित करते हुए एक कंसोल प्रोजेक्ट बनाएं।
  3. मुख्य कार्य में, एक System.Threading.Timer बनाएं जो समय पर TestTask को कॉल करेगा। टाइमर को तुरंत एक बार शुरू करने के लिए सेट करें। अंत में, एक कंसोल जोड़ें। पढ़ें()।
  4. TestTask में, Task.Factory.StartNew(DoOneThing) को कॉल करके 300 टास्क बनाने के लिए लूप के लिए उपयोग करें। उन सभी कार्यों को एक सूची में जोड़ें और कार्य का उपयोग करें। उन सभी के समाप्त होने की प्रतीक्षा करने के लिए प्रतीक्षा करें।
  5. DoOneThing फ़ंक्शन में, एक MongoClient बनाएं और कुछ आसान क्वेरी करें।
  6. अब इसे चलाएं।

यह उसी स्थान पर विफल हो जाएगा जिसका आपने उल्लेख किया था:MongoDB.Driver.Core.Clusters.Cluster.WaitForDescriptionChangedHelper.HandleCompletedTask(Task completedTask)

यदि आप कुछ विराम बिंदु डालते हैं, तो आपको पता चल जाएगा कि WaitForDescriptionChangedHelper ने एक टाइमआउट कार्य बनाया है। इसके बाद यह विवरण अद्यतन कार्य या टाइमआउट कार्य के पूरा होने में से किसी एक की प्रतीक्षा करता है। हालांकि, विवरण अपडेट कभी नहीं होता, लेकिन क्यों?

अब, मेरे उदाहरण पर वापस, एक दिलचस्प हिस्सा है:मैंने एक टाइमर शुरू किया। यदि आप सीधे TestTask को कॉल करते हैं, तो यह बिना किसी समस्या के चलेगा। विजुअल स्टूडियो की टास्क विंडो के साथ उनकी तुलना करके, आप देखेंगे कि टाइमर संस्करण गैर-टाइमर संस्करण की तुलना में बहुत अधिक कार्य बनाएगा। मैं इस भाग को थोड़ा बाद में समझाता हूँ। एक और महत्वपूर्ण अंतर है। आपको Cluster.cs . में डिबग लाइन जोड़नी होगी :

    protected void UpdateClusterDescription(ClusterDescription newClusterDescription)
    {
        ClusterDescription oldClusterDescription = null;
        TaskCompletionSource<bool> oldDescriptionChangedTaskCompletionSource = null;

        Console.WriteLine($"Before UpdateClusterDescription {_descriptionChangedTaskCompletionSource?.Task.Id}, {_descriptionChangedTaskCompletionSource?.Task?.GetHashCode().ToString("F8")}");
        lock (_descriptionLock)
        {
            oldClusterDescription = _description;
            _description = newClusterDescription;

            oldDescriptionChangedTaskCompletionSource = _descriptionChangedTaskCompletionSource;
            _descriptionChangedTaskCompletionSource = new TaskCompletionSource<bool>();
        }

        OnDescriptionChanged(oldClusterDescription, newClusterDescription);
        Console.WriteLine($"Setting UpdateClusterDescription {oldDescriptionChangedTaskCompletionSource?.Task.Id}, {oldDescriptionChangedTaskCompletionSource?.Task?.GetHashCode().ToString("F8")}");
        oldDescriptionChangedTaskCompletionSource.TrySetResult(true);
        Console.WriteLine($"Set UpdateClusterDescription {oldDescriptionChangedTaskCompletionSource?.Task.Id}, {oldDescriptionChangedTaskCompletionSource?.Task?.GetHashCode().ToString("F8")}");
    }

    private void WaitForDescriptionChanged(IServerSelector selector, ClusterDescription description, Task descriptionChangedTask, TimeSpan timeout, CancellationToken cancellationToken)
    {
        using (var helper = new WaitForDescriptionChangedHelper(this, selector, description, descriptionChangedTask, timeout, cancellationToken))
        {
            Console.WriteLine($"Waiting {descriptionChangedTask?.Id}, {descriptionChangedTask?.GetHashCode().ToString("F8")}");
            var index = Task.WaitAny(helper.Tasks);
            helper.HandleCompletedTask(helper.Tasks[index]);
        }
    }

इन पंक्तियों को जोड़कर, आपको यह भी पता चलेगा कि गैर-टाइमर संस्करण दो बार अपडेट होगा लेकिन टाइमर संस्करण केवल एक बार अपडेट होगा। और दूसरा ServerMonitor.cs में "MonitorServerAsync" से आता है। यह पता चला कि, टाइमर संस्करण में, MontiorServerAsync निष्पादित किया गया था, लेकिन इसके बाद ServerMonitor.HeartbeatAsync, BinaryConnection.OpenAsync, BinaryConnection.OpenHelperAsync और TcpStreamFactory.CreateStreamAsync के माध्यम से सभी तरह से आता है, यह अंततः TcpStreamFactory.ResolveEndPointsAsync पर पहुंच गया। यहां बुरी बात होती है:Dns.GetHostAddressesAsync . यह कभी निष्पादित नहीं होता है। यदि आप कोड को थोड़ा संशोधित करते हैं और उसे इसमें बदल देते हैं:

    var task = Dns.GetHostAddressesAsync(dnsInitial.Host).ConfigureAwait(false);

    return (await task)
        .Select(x => new IPEndPoint(x, dnsInitial.Port))
        .OrderBy(x => x, new PreferredAddressFamilyComparer(preferred))
        .ToArray();

आप टास्क आईडी ढूंढ पाएंगे। विजुअल स्टूडियो के टास्क विंडो में देखने से यह बिल्कुल स्पष्ट है कि इसके सामने लगभग 300 कार्य हैं। उनमें से केवल कई निष्पादित हो रहे हैं लेकिन अवरुद्ध हैं। यदि आप DoOneThing फ़ंक्शन में एक कंसोल.राइटलाइन जोड़ते हैं, तो आप देखेंगे कि कार्य शेड्यूलर उनमें से कई को लगभग एक ही समय में शुरू करता है, लेकिन फिर, यह लगभग एक प्रति सेकंड तक धीमा हो जाता है। तो, इसका मतलब है, डीएनएस को हल करने का कार्य शुरू होने से पहले आपको लगभग 300 सेकंड तक प्रतीक्षा करने की आवश्यकता है। यही कारण है कि यह 30 सेकंड के समयबाह्य से अधिक है।

अब, यदि आप पागल चीजें नहीं कर रहे हैं तो एक त्वरित समाधान यहां आता है:

Task.Factory.StartNew(DoOneThing, TaskCreationOptions.LongRunning);

यह ThreadPoolScheduler को एक नया बनाने से पहले एक सेकंड प्रतीक्षा करने के बजाय तुरंत एक थ्रेड प्रारंभ करने के लिए बाध्य करेगा।

हालाँकि, यह काम नहीं करेगा यदि आप मेरी तरह वास्तव में पागल काम कर रहे हैं। आइए लूप के लिए 300 से 30000 में बदलें, यहां तक ​​कि यह समाधान भी विफल हो सकता है। कारण यह है कि यह बहुत अधिक धागे बनाता है। यह संसाधन और समय लेने वाला है। और यह जीसी प्रक्रिया को शुरू कर सकता है। सब एक साथ, यह समय समाप्त होने से पहले उन सभी थ्रेड्स को बनाने में सक्षम नहीं हो सकता है।

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

हालांकि, यदि आप मूल संरचना को बहुत अधिक नहीं बदलना चाहते हैं, तो आप निम्न तरीके से प्रयास कर सकते हैं:

टास्क शेड्यूलर से व्युत्पन्न एक थ्रॉटलड टास्क शेड्यूलर बनाएं।

  1. यह ThrottledTaskScheduler एक कार्य शेड्यूलर को अंतर्निहित के रूप में स्वीकार करता है जो वास्तविक कार्य को चलाएगा।
  2. कार्यों को अंतर्निहित अनुसूचक में डंप करें, लेकिन यदि यह सीमा से अधिक है, तो इसके बजाय इसे एक कतार में रखें।
  3. यदि कोई कार्य समाप्त हो गया है, तो कतार की जांच करें और उन्हें सीमा के भीतर अंतर्निहित अनुसूचक में डंप करने का प्रयास करें।
  4. उन सभी नए कार्यों को शुरू करने के लिए निम्न कोड का उपयोग करें:

·

var taskScheduler = new ThrottledTaskScheduler(
    TaskScheduler.Default,
    128,
    TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler,
    logger
    );
var taskFactory = new TaskFactory(taskScheduler);
for (var i = 0; i < 30000; i++)
{
    tasks.Add(taskFactory.StartNew(DoOneThing))
}
Task.WaitAll(tasks.ToArray());

आप System.Threading.Tasks.ConcurrentExclusiveSchedulerPair.ConcurrentExclusiveTaskScheduler को संदर्भ के रूप में ले सकते हैं। यह हमारी जरूरत से थोड़ा अधिक जटिल है। यह किसी और उद्देश्य के लिए है। तो, उन हिस्सों के बारे में चिंता न करें जो ConcurrentExclusiveSchedulerPair वर्ग के अंदर फ़ंक्शन के साथ आगे और पीछे जाते हैं। हालांकि, आप इसे सीधे उपयोग नहीं कर सकते क्योंकि यह कार्य निर्माण विकल्प पास नहीं करता है। रैपिंग कार्य बनाते समय लॉन्ग रनिंग।

इससे मेरा काम बनता है। शुभकामनाएँ!

पीएस .:टाइमर संस्करण में बहुत सारे कार्य होने का कारण शायद कार्य शेड्यूलर के अंदर है। TryExecuteTaskInline। यदि यह मुख्य थ्रेड में है जहां थ्रेडपूल बनाया गया है, तो यह कुछ कार्यों को कतार में डाले बिना निष्पादित करने में सक्षम होगा।




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. उत्पादन में MongoDB और Mongoid

  2. जावा मोंगोडब संख्या लंबी क्वेरी

  3. मोंगो क्वेरी में घटाव काम नहीं करता है?

  4. संदर्भ फ़ील्ड मौजूदा दस्तावेज़ के संदर्भ में

  5. स्केलेबल फ़ाइल संग्रहण