2 पॉइंट द्वारा GN⁺ 2025-03-01 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • पहले मेरे सिस्टम में CPU उपयोग 3,200% तक पहुंच गया था, यानी सभी 32 कोर पूरी तरह भर गए थे
  • मैं Java 17 runtime का उपयोग कर रहा था, और thread dump में CPU time देखकर उसे CPU time के अनुसार sort किया, तो कई मिलते-जुलते threads मिले
  • समस्या वाले कोड का विश्लेषण
    • stack trace के जरिए BusinessLogic class की 29वीं line की जांच की
    • वह कोड unrelatedObjects list पर iterate करते हुए relatedObject की value को treeMap में insert कर रहा था
    • यह loop के अंदर unrelatedObject का उपयोग न करने वाला एक अक्षम कोड था

कोड संशोधन और टेस्ट

  • अनावश्यक loop हटाकर इसे एक line treeMap.put(relatedObject.a(), relatedObject.b()); में बदल दिया गया
  • बदलाव से पहले और बाद में unit test चलाए गए, लेकिन समस्या को reproduce नहीं किया जा सका
  • treeMap और unrelatedObjects दोनों का size 1,000,000 से अधिक होने पर भी समस्या नहीं हुई

समस्या का कारण मिला

  • treeMap को कई threads एक साथ access कर रहे थे, और उसमें synchronization नहीं था
  • यह समस्या कई threads द्वारा TreeMap को एक साथ modify करने से हुई थी

प्रयोग के जरिए समस्या को reproduce करना

  • कई threads द्वारा shared TreeMap को random तरीके से update करने का प्रयोग किया गया
  • try-catch block का उपयोग करके NullPointerException को ignore करने के लिए सेट किया गया
  • प्रयोग के नतीजे में CPU उपयोग 500% तक बढ़ने की घटना देखी गई

निष्कर्ष

  • synchronization के बिना TreeMap को एक साथ modify करना गंभीर performance समस्याएं पैदा कर सकता है
  • ऐसी समस्याओं से बचने के लिए TreeMap को synchronize करना या ConcurrentMap जैसी thread-safe collection का उपयोग करने की सिफारिश की जाती है

1 टिप्पणियां

 
GN⁺ 2025-03-01
Hacker News राय
  • मैं सोचता था कि race condition से data corruption या deadlock होता है, लेकिन यह performance समस्याएँ भी पैदा कर सकता है, यह नहीं सोचा था। data इस तरह corrupt हो सकता है कि infinite loop बन जाए

    • मेरा मानना है कि किसी project में errors, अजीब behavior, या warnings को सिद्धांततः ठीक करना चाहिए। क्योंकि ये असंबंधित समस्याएँ भी पैदा कर सकते हैं
    • यह अच्छी तरह जाना जाता है कि Java के core collections design के हिसाब से thread-safe नहीं हैं। OP को यह भी जाँचना चाहिए कि code के दूसरे हिस्सों में भी कई threads collections को manipulate कर रहे हैं या नहीं
    • TreeMap को Collections.synchronizedMap से wrap करना, या ConcurrentHashMap में बदलकर ज़रूरत पड़ने पर sort करना, सबसे आसान समाधान है
    • अलग-अलग map operations को thread-safe बनाया जा सकता है, लेकिन लगातार होने वाले operations thread-safe हैं या नहीं, इस पर भरोसा नहीं किया जा सकता। यह भी निश्चित नहीं कि TreeMap को own करने वाला object thread-safe है
    • एक विवादास्पद समाधान के रूप में visited nodes को track करना अच्छा तरीका नहीं है। collection फिर भी thread-safe नहीं रहेगा, और दूसरे सूक्ष्म तरीकों से fail हो सकता है
    • जो developer details पर ध्यान देता है, वह threads और TreeMap के संयोजन को पहचान सकता था, या अगर sorted elements की ज़रूरत नहीं थी तो TreeMap न इस्तेमाल करने का सुझाव दे सकता था। लेकिन इस मामले में ऐसा नहीं हुआ
    • समस्या collection के contract का उल्लंघन करने की थी, और TreeMap को HashMap से बदलने पर भी यह गलत ही रहेगा
  • ऐसे code में जहाँ कई threads काम कर रहे हों, हर object को immutable बनाना, और जिन्हें immutable नहीं बनाया जा सकता उन्हें छोटे, self-contained, और सख्ती से नियंत्रित sections तक सीमित रखना ही एकमात्र भरोसेमंद रणनीति है

    • इन सिद्धांतों का पालन करते हुए core modules को फिर से लिखा गया, और वे लगातार समस्या पैदा करने वाले स्रोत से बदलकर codebase के सबसे resilient sections में से एक बन गए
    • इन guidelines के बाद code review करना बहुत आसान हो गया
  • "ssh में लगभग लॉग इन नहीं हो पा रहा था" वाली बात ने graduate school के दिनों की याद दिला दी, जब Sun UltraSparc 170 इस्तेमाल करते थे

    • कोई नया user या student parallel में काम चलाने की कोशिश कर रहा था, और एक बड़ी text file को line numbers के आधार पर कई sections में बाँटकर हर section को parallel में process कर रहा था
    • बहुत सारा RAM इस्तेमाल हो रहा था, और swap की कोशिशें उसी file के अलग-अलग sections पढ़ने के लिए बेतहाशा seek कर रही थीं
    • console पर login prompt तक नहीं मिल पा रहा था, लेकिन एक session पहले से logged in था, और root session लेकर समस्या ठीक की जा सकी
    • समस्या यह थी कि system की limits को समझा नहीं गया था
  • code को बस इस तरह घटाया जा सकता है

    • मूल code में <i>unrelatedObjects</i> खाली न होने पर ही <i>treeMap.put</i> किया जाता है। यह एक bug भी हो सकता है
    • यह जाँचना चाहिए कि <i>a</i> और <i>b</i> हर बार एक जैसे values लौटाते हैं या नहीं, और <i>treeMap</i> सच में map की तरह behave करता है या नहीं
  • infinite loop पाने का एक और तरीका है ऐसा <i>Comparator</i> या <i>Comparable</i> implementation इस्तेमाल करना जो consistent total ordering लागू नहीं करता

    • इसका concurrency से संबंध नहीं है, और यह खास data तथा processing order के आधार पर हो सकता है
  • बढ़ते हुए counter का इस्तेमाल करके cycle detect करने, और tree depth या collection size से अधिक होने पर exception throw करने का तरीका विचार किया जा सकता है

    • इसमें लगभग न के बराबर memory या CPU overhead लगता है, और इसे स्वीकार किए जाने की संभावना अधिक है
  • Java में thread-safe न होने वाले objects पर concurrent operations चलाना सबसे दिलचस्प bugs पैदा करता है

  • यह सवाल है कि क्या unprotected TreeMap 3,200% utilization पैदा कर सकता है

    • 2009 के आसपास ऐसा ही एक मामला देखा था, और यह अब भी हो सकता है
    • उन लोगों के लिए यह निराशाजनक है जो सोचते हैं कि data race बस थोड़ी-बहुत बुरी चीज़ है
  • लेखक ने Poison Pill का एक प्रकार खोज लिया था। यह event sourcing systems में अधिक आम होता है, और ऐसा message होता है जो जिस किसी से भी टकराए उसे मार देता है

    • जब data structure किसी illegal state में पहुँच जाता है, तो उसके बाद आने वाले सभी threads उसी logical bomb में फँस जाते हैं
  • threads में exceptions बिल्कुल गंभीर समस्या होती हैं

    • C++, select(), और threads द्वारा exceptions उछालने वाली भयावह bug-hunt कहानी भी है