स्टेजिंगमध्ये सहज चालणारे API प्रत्यक्ष ट्रॅफिक येताच कोसळू शकते. ही विफलता क्वचितच नाट्यमय असते; ती 'हजारो जखमांनी होणारा मृत्यू' (death by a thousand cuts) सारखी असते. इथे एक ब्लॉक झालेला थ्रेड, तिथे एक अनावश्यक क्वेरी. कमी लोड असताना, या लहान अकार्यक्षमते मागे लपलेल्या असतात. प्रोडक्शनच्या दबावाखाली, त्यांचे रूपांतर थ्रेड पूल स्टार्व्हेशन (thread pool starvation), डेटाबेस चोकिंग (database choking) आणि प्रतिसाद वेळेत (response times) वाढ होण्यात होते, ज्यामुळे वापरकर्त्याचा विश्वास उडतो. परफॉर्मन्स ट्यूनिंग म्हणजे एखादा एक जादुई उपाय शोधणे नव्हे. तर ते असे सिस्टम तयार करण्याबद्दल आहे जिथे प्रत्येक लेयर थ्रेड्स, मेमरी आणि डेटाबेस कनेक्शन्सच्या मर्यादेचा आदर करतो. जेव्हा तुम्ही या संसाधनांना मर्यादित मानता, तेव्हा तुम्ही आउटेजवर (outages) फक्त प्रतिक्रिया देणे थांबवता आणि ते रोखण्यास सुरुवात करता.
Sync-over-Async तुम्हाला संपवण्यापूर्वी तुम्ही त्याला संपवा
हाय-ट्रॅफिक ASP.NET Core ॲप्लिकेशन्समधील सर्वात विनाशकारी पॅटर्न म्हणजे sync-over-async. जेव्हा कोणीतरी .Result किंवा .Wait() चा वापर एखाद्या asynchronous मेथडवर करते, कारण त्यांना व्हॅल्यू लगेच हवी असते आणि त्यांना कॉल स्टॅक रिफॅक्टर करायचा नसतो, तेव्हा हे दिसून येते. हा निर्णय कॉल करणाऱ्या थ्रेडला ब्लॉक करतो. तो थ्रेड रिकामी बसून राहतो, अशा कामाची वाट पाहतो जे आधीच इतरत्र सुरू आहे, परंतु रनटाइम त्याचा वापर दुसऱ्या रिक्वेस्टसाठी करू शकत नाही.
जेव्हा पुरेशा रिक्वेस्ट्स असे करतात, तेव्हा थ्रेड पूल स्टार्व्ह (starve) होतो. तुमचा CPU ग्राफ निरोगी दिसतो कारण प्रोसेसर व्यस्त नसतात, तरीही तुमची लॅटन्सी (latency) प्रचंड वाढते. रिक्वेस्ट्स रांगेत लागतात, अशा थ्रेड्सची वाट पाहतात जे कधीच मोकळे होणार नाहीत. याचे निराकरण तांत्रिक आहे परंतु त्यासाठी शिस्त लागते: कॉल स्टॅकच्या शेवटपर्यंत await वापरा. जर एखादी मेथड async API ला कॉल करत असेल, तर ती स्वतः देखील async असणे आवश्यक आहे. येथे कोणताही शॉर्टकट नाही. तुम्ही काढलेला प्रत्येक सिंक्रोनस ब्लॉकेज प्रत्यक्ष ट्रॅफिकसाठी अधिक हेडरुम (headroom) उपलब्ध करून देतो.
डिस्कनेक्ट झालेल्या क्लायंट्सवर काम वाया घालवणे थांबवा
क्लायंट्स डिस्कनेक्ट होतात. ब्राउझर टॅब बंद करतात. मोबाईल ॲप्सचा सिग्नल जातो. जर तुमच्या सर्व्हरला क्लायंट निघून गेला आहे हे समजले नाही, तर तो डेटाबेस क्वेरीज चालवत राहतो, JSON पार्स करतो आणि अशा प्रतिसादासाठी थ्रेड्स खर्च करतो जो कोणीही प्राप्त करणार नाही. जे प्रत्येक async ऑपरेशन CancellationToken ला सपोर्ट करते, त्यात ते पास करा. याचा अर्थ Entity Framework क्वेरीज, HttpClient सह HTTP कॉल्स आणि कोणतेही दीर्घकाळ चालणारे बॅकग्राउंड वर्क.
जेव्हा कनेक्शन तुटते, तेव्हा टोकन कॅन्सलेशन ट्रिगर करते आणि काम लगेच थांबते. यामुळे डेटाबेस CPU सायकल वाचतात आणि थ्रेड्स वेगाने पूलमध्ये परत मिळतात. मेथड सिग्नेचरमधील हा एक छोटा बदल लोड असताना मोठा फायदा देतो.
जे बदलत नाही त्याचे कॅशिंग करा
तुमचा प्रॉडक्ट कॅटलॉग कदाचित प्रत्येक रिक्वेस्टमध्ये बदलत नसेल. तुमचे कॉन्फिगरेशन फ्लॅग्स तर नक्कीच बदलत नाहीत. तरीही अनेक APIs एकाच स्टॅटिक डेटासाठी वारंवार डेटाबेसवर हिट करतात. ASP.NET Core मधील आउटपुट कॅशिंग तुम्हाला रेंडर केलेले रिस्पॉन्स साठवण्याची आणि तुमचे कंट्रोलर्स किंवा डेटाबेस पुन्हा न वापरता थेट मेमरीमधून ते सर्व्ह करण्याची परवानगी देते.
टॅग-आधारित इनव्हॅलिडेशन (tag-based invalidation) काळजीपूर्वक वापरा. जेव्हा तुम्ही एखादे उत्पादन अपडेट करता, तेव्हा फक्त त्या कॅटेगरी किंवा आयटमशी संबंधित टॅग इनव्हॅलिडेट करा. तुम्हाला संपूर्ण कॅशे फ्लश करण्याची गरज नाही. यामुळे तुमचा कॅशे हिट रेशो (cache hit ratio) उच्च राहतो आणि डेटाबेस क्वेरीची संख्या कमी राहते.
प्रथम तुमचा डेटा ॲक्सेस सुधारा
बहुतेक APIs मध्ये डेटाबेस कॉल्समुळे रिक्वेस्टचा बराचसा वेळ खर्च होतो. इतर काहीही ऑप्टिमाइझ करण्यापूर्वी, येथे पहा.
जर तुम्ही डेटा फक्त प्रदर्शित करण्यासाठी क्वेरी करत असाल आणि तो अपडेट करण्याचा तुमचा कोणताही विचार नसेल, तर तुमच्या Entity Framework क्वेरीजमध्ये AsNoTracking() जोडा. EF Core चेंज ट्रॅकिंग आणि स्नॅपशॉट क्रिएशन वगळते.
