AI-जनरेटेड APIs के लिए Go + Postgres प्रदर्शन ट्यूनिंग प्लेबुक: कनेक्शन पूल, EXPLAIN देखें, स्मार्ट इंडेक्स, सुरक्षित पेजिनेशन, और तेज़ JSON आकार दें।

AI-जेनरेट किए गए APIs शुरुआती परीक्षण में तेज़ लग सकते हैं। आप किसी एंडपॉइंट को कुछ बार हिट करते हैं, डाटासेट छोटा होता है, और रिक्वेस्ट एक-एक कर आते हैं। फिर असली ट्रैफिक आता है: मिश्रित एंडपॉइंट्स, बर्स्टी लोड, ठंडे कैश, और अपेक्षित से ज़्यादा पंक्तियाँ। वही कोड अचानक रैंडमली धीमा महसूस होने लगता है, हालांकि असल में कुछ टूटा नहीं होता।
धीमा आम तौर पर कुछ तरीकों से दिखता है: लेटेंसी स्पाइक्स (ज़्यादातर रिक्वेस्ट ठीक रहते हैं, कुछ 5x से 50x धीमे हो जाते हैं), टाइमआउट (एक छोटा प्रतिशत फेल होता है), या CPU गरम चलना (Postgres क्वेरी वर्क से CPU, या Go CPU JSON, goroutines, लॉगिंग, और retries से)।
एक सामान्य परिदृश्य एक लिस्ट एंडपॉइंट है जिसमें लचीला सर्च फ़िल्टर है और जो बड़ी JSON रिस्पॉन्स लौटाता है। टेस्ट DB में वह कुछ हजार पंक्तियों को स्कैन करके जल्दी खत्म कर देता है। प्रोडक्शन में वह कुछ मिलियन पंक्तियों को स्कैन करता है, उन्हें सॉर्ट करता है, और तभी LIMIT लागू होता है। API अभी भी "काम" करता है, पर p95 लेटेंसी विस्फोट कर जाती है और बर्स्ट के दौरान कुछ रिक्वेस्ट टाइमआउट हो जाते हैं।
डेटाबेस की धीमी गति को ऐप की धीमी गति से अलग करने के लिए मानसिक मॉडल सरल रखें।
अगर डेटाबेस धीमा है, तो आपका Go हैंडलर अपने समय का बड़ा हिस्सा क्वेरी के इंतज़ार में बिताता है। आप कई रिक्वेस्ट्स को "in flight" फंसा हुआ देख सकते हैं जबकि Go CPU सामान्य दिखता है।
अगर ऐप धीमा है, तो क्वेरी जल्दी खत्म होती है, लेकिन क्वेरी के बाद समय चला जाता है: बड़े रिस्पॉन्स ऑब्जेक्ट बनाना, JSON मैर्शल करना, प्रति-रो अतिरिक्त क्वेरियाँ चलाना, या हर रिक्वेस्ट पर ज्यादा काम करना। Go CPU बढ़ता है, मेमोरी बढ़ती है, और लेटेंसी रिस्पॉन्स साइज के साथ बढ़ती है।
लॉन्च से पहले "पर्याप्त अच्छा" प्रदर्शन पूर्णता नहीं है। कई CRUD एंडपॉइंट्स के लिए लक्ष्य स्थिर p95 लेटेंसी (केवल औसत नहीं), बर्स्ट के दौरान अनुमानित व्यवहार, और अपेक्षित पीक पर टाइमआउट न होना होना चाहिए। लक्ष्य साफ़ है: जब डेटा और ट्रैफिक बढ़े तो कोई अनपेक्षित धीमी रिक्वेस्ट न हो, और जब कुछ ड्रीफ्ट करे तो स्पष्ट संकेत हों।
कुछ भी ट्यून करने से पहले तय करें कि API के लिए "अच्छा" क्या है। बिना बेसलाइन के, सेटिंग्स बदलने में घंटे निकल सकते हैं और फिर भी आपको पता नहीं चलता कि आप ने सुधार किया या सिर्फ बॉटलनेक को आगे बढ़ाया।
तीन नंबर अक्सर कहानी का अधिकांश हिस्सा बताते हैं:
p95 वह "खराब दिन" मीट्रिक है। अगर p95 ऊँचा है पर औसत ठीक है, तो कुछ रिक्वेस्ट्स बहुत ज़्यादा काम कर रही हैं, लॉक पर ब्लॉक हो रही हैं, या स्लो प्लान ट्रिगर कर रही हैं।
धीमी क्वेरियों को जल्दी दिखाएँ। Postgres में प्रीलॉन्च टेस्टिंग के लिए स्लो क्वेरी लॉगिंग को कम थ्रेशोल्ड पर चालू करें (उदा. 100–200 ms) और पूरा स्टेटमेंट लॉग करें ताकि आप उसे SQL क्लाइंट में कॉपी कर सकें। यह अस्थायी रखें—प्रोडक्शन में हर स्लो क्वेरी लॉग करना जल्दी शोर पैदा कर देता है।
अगला, असली दिखने वाली रिक्वेस्ट्स के साथ टेस्ट करें, सिर्फ एक "hello world" रूट नहीं। एक छोटा सेट भी पर्याप्त है अगर वह वही करे जो यूज़र्स करेंगे: फ़िल्टर और सॉर्टिंग वाला लिस्ट कॉल, कुछ जोइन वाले डिटेल पेज, वेलिडेशन के साथ क्रिएट/अपडेट, और पार्शल मैच के साथ सर्च-स्टाइल क्वेरी।
अगर आप एक स्पेक से एंडपॉइंट्स जेनरेट कर रहे हैं (उदा. Koder.ai जैसी टूल से), तो वही छोटे सेट नियमित रूप से चलाएँ। इससे इंडेक्स, पेजिनेशन ट्वीक, और क्वेरी रीट्राइट्स को नापना आसान होगा।
अंत में, एक लक्ष्य चुने जिसे आप जोर से कह सकें। उदाहरण: "ज़्यादातर रिक्वेस्ट्स 50 concurrent users पर p95 में 200 ms से कम रहते हैं, और एरर 0.5% से कम रहते हैं।" सटीक संख्याएँ आपके प्रोडक्ट पर निर्भर होंगी, पर एक स्पष्ट लक्ष्य अनंत टिंकरिंग रोकता है।
एक कनेक्शन पूल सीमित संख्या में खुली DB कनेक्शन्स रखता है और उन्हें रीयूज़ करता है। बिना पूल के, हर रिक्वेस्ट नई कनेक्शन खोल सकती है, और Postgres सत्रों का प्रबंधन करने में समय और मेमोरी खर्च करता है बजाए क्वेरियों को रन करने के।
लक्ष्य यह है कि Postgres उपयोगी काम कर रहा हो, न कि बहुत सी कनेक्शन्स के बीच context-switching कर रहा हो। यह अक्सर पहली सार्थक जीत होती है, खासकर AI-जेनरेट API जिनमें धीरे-धीरे चैटी एंडपॉइंट्स बन जाते हैं।
Go में आप सामान्यतः max open connections, max idle connections, और connection lifetime ट्यून करते हैं। कई छोटे APIs के लिए सुरक्षित शुरुआती बिंदु अक्सर आपके CPU को एक छोटा गुणक होता है (अक्सर कुल 5 से 20 कनेक्शन्स), समान संख्या idle रखें, और कनेक्शन्स को समय-समय पर रीसायकल करें (उदा. हर 30–60 मिनट)।
अगर आप कई API इंस्टेंस चला रहे हैं, याद रखें कि पूल गुणा हो जाता है। 10 इंस्टेंस पर 20 कनेक्शन्स का पूल Postgres पर 200 कनेक्शन्स भेजता है — और इसी तरह टीमें अनपेक्षित रूप से कनेक्शन लिमिट में फंस जाती हैं।
पूल समस्याएँ धीमी SQL से अलग महसूस होती हैं।
अगर पूल बहुत छोटा है, तो रिक्वेस्ट्स Postgres तक पहुँचने से पहले ही इंतज़ार करती हैं। लेटेंसी स्पाइक होती है, पर DB CPU और क्वेरी टाइम सामान्य दिख सकते हैं।
अगर पूल बहुत बड़ा है, तो Postgres ओवरलोड दिखता है: बहुत सारे सक्रिय सत्र, मेमोरी प्रेशर, और एंडपॉइंट्स के बीच असमान लेटेंसी।
जल्दी से अलग करने का तरीका: अपने DB कॉल्स को दो हिस्सों में टाइम करें: कनेक्शन के लिए बिताया समय बनाम क्वेरी निष्पादन का समय। अगर ज्यादातर समय "waiting" में है तो पूल बॉटलनेक है। अगर ज्यादातर समय "in query" में है, तो SQL और इंडेक्स पर ध्यान दें।
उपयोगी त्वरित जाँचें:
max_connections के कितने करीब हैं।अगर आप pgxpool का उपयोग करते हैं, तो आपको Postgres-फर्स्ट पूल मिलता है जिसमें स्पष्ट स्टैट्स और Postgres व्यवहार के लिए अच्छे डिफ़ॉल्ट होते हैं। अगर आप database/sql इस्तेमाल करते हैं, तो आपको एक सामान्य इंटरफ़ेस मिलता है जो कई DB के साथ काम करता है, पर आपको पूल सेटिंग्स और ड्राइवर व्यवहार के बारे में स्पष्ट होना पड़ता है।
एक व्यावहारिक नियम: अगर आप पूरी तरह Postgres पर हैं और डायरेक्ट कंट्रोल चाहते हैं, तो pgxpool अक्सर सरल होता है। अगर आप लाइब्रेरीज़ पर निर्भर हैं जो database/sql की उम्मीद करती हैं, तो वहीं रहें, पूल स्पष्ट रूप से सेट करें, और इंतज़ारों को मापें।
उदाहरण: एक एंडपॉइंट जो 20 ms चल सकता है, पर 100 concurrent users पर वह 2 s पर छलांग लगा देता है। अगर लॉग्स में दिखता है कि 1.9 s कनेक्शन के लिए इंतज़ार में गया, तो तब तक क्वेरी ट्यूनिंग मदद नहीं करेगी जब तक पूल और कुल Postgres कनेक्शन्स सही आकार के न हों।
जब कोई एंडपॉइंट धीमा लगता है, तो देखें कि Postgres असल में क्या कर रहा है। EXPLAIN की एक त्वरित पढ़ाई अक्सर मिनटों में फिक्स का संकेत देती है।
API जो सही SQL भेजता है उसी पर यह चलाएँ:
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, status, created_at
FROM orders
WHERE user_id = $1 AND status = $2
ORDER BY created_at DESC
LIMIT 50;
कुछ लाइनों का सबसे अधिक महत्व होता है। ऊपर वाला नोड देखें (Postgres ने जो चुना) और नीचे के टोटल्स (कितना समय लगा)। फिर estimated बनाम actual rows की तुलना करें। बड़े गैप अक्सर प्लान गलत होने का संकेत हैं।
अगर आप Index Scan या Index Only Scan देखें, तो Postgres इंडेक्स का उपयोग कर रहा है, जो आमतौर पर ठीक है। Bitmap Heap Scan मध्यम आकार के मैचों के लिए ठीक हो सकता है। Seq Scan मतलब टेबल की पूरी पढ़ाई हुई—यह केवल तब ठीक है जब टेबल छोटी हो या लगभग हर रो मैच करे।
सामान्य रेड फ़्लैग्स:
ORDER BY के साथ अक्सर जुड़ा)धीमे प्लान अक्सर कुछ ही पैटर्न से आते हैं:
WHERE + ORDER BY पैटर्न के लिए मिसिंग इंडेक्स (उदा. (user_id, status, created_at))WHERE में फंक्शन का उपयोग (उदा. WHERE lower(email) = $1), जो स्कैन मजबूर कर सकता है जब तक आप मिलते हुए एक्सप्रेशन इंडेक्स ना जोड़ेंअगर प्लान अजीब दिखे और अनुमान बहुत गलत हों, तो अक्सर स्टैट्स पुरानी होती हैं। ANALYZE चलाएँ (या autovacuum को पकड़ने दें) ताकि Postgres वर्तमान रो काउंट और वैल्यू डिस्ट्रीब्यूशन सीख ले। यह बड़े इम्पोर्ट के बाद या जब नए एंडपॉइंट्स तेज़ी से बहुत डाटा लिखना शुरू करें तो मायने रखता है।
इंडेक्स तभी मदद करते हैं जब वे उसी तरह बनें जैसे आपका API डाटा क्वेरी करता है। अगर आप अनुमान से इंडेक्स बनाते हैं, तो लिखने में धीमा, स्टोरेज बड़ा और स्पीडअप कम मिलेगा।
एक उपयोगी तरीका यह सोचना है: इंडेक्स किसी खास सवाल के लिए शॉर्टकट है। अगर आपका API अलग सवाल पूछता है, तो Postgres शॉर्टकट को अनदेखा कर देता है।
अगर कोई एंडपॉइंट account_id से फ़िल्टर करता है और created_at DESC से सॉर्ट करता है, तो एक संयुक्त कॉम्पोजिट इंडेक्स अक्सर दो अलग-अलग इंडेक्स से बेहतर होता है। इससे Postgres सही रो ढूंढकर उन्हें सही क्रम में कम काम में लौटाने में मदद मिलती है।
अमूमन नियम जो सही रहते हैं:
उदाहरण: अगर आपके API में GET /orders?status=paid है और यह हमेशा नवीनतम पहले दिखाता है, तो (status, created_at DESC) जैसा इंडेक्स अच्छा फीट है। अगर अधिकांश क्वेरियाँ ग्राहक द्वारा भी फ़िल्टर करती हैं, तो (customer_id, status, created_at) बेहतर हो सकता है, पर केवल तभी जब वह वही पैटर्न प्रोडक्शन में चलता हो।
अगर अधिकांश ट्रैफ़िक एक संकुचित स्लाइस पर पड़ता है, तो partial index सस्ता और तेज़ हो सकता है। उदाहरण के लिए, अगर आपका ऐप ज़्यादातर सक्रिय रिकॉर्ड पढ़ता है, तो केवल WHERE active = true पर इंडेक्स करना इंडेक्स को छोटा रखता है और उसे मेमोरी में बने रहने की संभावना बढ़ाती है।
इंडेक्स मदद करता है या नहीं यह पुष्टि करने के लिए त्वरित जाँचें:
EXPLAIN (या सुरक्षित माहौल में EXPLAIN ANALYZE) चलाएँ और देखें कि क्या इंडेक्स स्कैन आपकी क्वेरी से मेल खाती है।अनउपयोगी इंडेक्स को सावधानी से हटाएँ। उपयोग आँकड़े (क्या किसी इंडेक्स को स्कैन किया गया) देखें। एक-एक करके लो-रीस्क विंडो में ड्रॉप करें और रोलबैक प्लान रखें। अनउपयोगी इंडेक्स हानिरहित नहीं हैं — वे हर राइट पर धीमा करते हैं।
पेजिनेशन अक्सर वह जगह होती है जहाँ एक तेज़ API धीमा महसूस होने लगता है, भले ही DB स्वस्थ हो। पेजिनेशन को UI डिटेल न मानकर क्वेरी डिज़ाइन समस्या समझें।
LIMIT/OFFSET सरल दिखता है, पर गहरे पेज अक्सर महंगे होते हैं। Postgres अभी भी उन रोज़ के पास से चलता है जिन्हें आप स्किप कर रहे हैं (और अक्सर उन्हें सॉर्ट भी करता है)। पेज 1 कुछ दर्जन रोज़ छू सकता है। पेज 500 में डेटाबेस कई हजारों रोज़ को स्कैन और डिसकार्ड कर सकता है सिर्फ 20 रिज़ल्ट्स लौटाने के लिए।
यह तब भी अस्थिर परिणाम दे सकता है जब रिक्वेस्ट्स के बीच पंक्तियाँ डाली या हटाई जाती हैं। उपयोगकर्ता डुप्लिकेट देख सकते हैं या आइटम मिस कर सकते हैं क्योंकि "रो 10,000" का मतलब बदल गया।
Keyset पेजिनेशन एक अलग सवाल पूछता है: "मुझे आखिरी देखी गई पंक्ति के बाद अगली 20 पंक्तियाँ दो।" इससे डेटाबेस एक छोटे, स्थिर स्लाइस पर काम करता है।
एक सरल संस्करण बढ़ते id का उपयोग करता है:
SELECT id, created_at, title
FROM posts
WHERE id > $1
ORDER BY id
LIMIT 20;
आपका API next_cursor लौटाता है जो पेज में आखिरी id के बराबर होता है। अगली रिक्वेस्ट उस वैल्यू को $1 के रूप में भेजती है।
टाइम-आधारित सॉर्टिंग के लिए एक स्थिर ऑर्डर और टाई-ब्रेकर का उपयोग करें। created_at अकेला पर्याप्त नहीं है अगर दो पंक्तियों का टाइमस्टैम्प समान हो। एक संयुक्त कर्सर का प्रयोग करें:
WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;
कई नियम डुप्लिकेट और गायब रोज़ रोकते हैं:
ORDER BY में हमेशा एक यूनिक टाई-ब्रेकर शामिल करें (आम तौर पर id)।created_at और id साथ में)।एक चौंकाने वाली सामान्य वजह कि API धीमा लगता है वह डेटाबेस नहीं होता—वह रिस्पॉन्स होता है। बड़ा JSON बनाना, भेजना और क्लाइंट का पार्स करना सब समय लेते हैं। सबसे तेज़ जीत अक्सर कम लौटाने में है।
अपने SELECT से शुरू करें। अगर एंडपॉइंट को सिर्फ id, name, और status चाहिए, तो सिर्फ वे कॉलम माँगें। SELECT * टेबल जैसे-जैसे लंबा टेक्स्ट, JSON ब्लॉब और ऑडिट कॉलम जोड़ती है, धीरे-धीरे भारी होता जाता है।
एक और सामान्य धीमापन N+1 रिस्पॉन्स निर्माण है: आप 50 आइटम की लिस्ट फेच करते हैं, फिर संबंधित डाटा जोड़ने के लिए 50 और क्वेरियाँ चलते हैं। यह टेस्ट पास कर सकता है, पर असली ट्रैफिक में धराशायी हो जाता है। एकल क्वेरी पसंद करें जो आपको चाहिए वह लौटाए (ध्यान से जॉइन), या IDs के आधार पर दूसरी क्वेरी जहां दूसरी बैच करता है।
पेलोड्स छोटे रखने के कुछ तरीके बिना क्लाइंट तोड़े:
include= फ्लैग (या fields=) रखें ताकि लिस्ट रिस्पॉन्स हल्का रहे और डिटेल ऑप्शनेल हो।दोनों तेज़ हो सकते हैं। चुनें कि आप किसके लिए ऑप्टिमाइज़ कर रहे हैं।
Postgres JSON फ़ंक्शन्स (jsonb_build_object, json_agg) उपयोगी हैं जब आप कम राउंड ट्रिप्स और एक क्वेरी से प्रेडिक्टेबल शेप चाहते हैं। Go में शेपिंग उपयोगी है जब आपको कंडीशनल लॉजिक चाहिए, structs को री-यूज़ करना है, या SQL को आसान रखना है। अगर आपका JSON-बिल्डिंग SQL पढ़ने में कठिन हो जाए, तो उसे ट्यून करना भी मुश्किल हो जाता है।
एक अच्छा नियम: Postgres को फ़िल्टर, सॉर्ट और एग्रीगेट करने दें। फिर Go अंतिम प्रस्तुति संभाले।
अगर आप तेजी से APIs जेनरेट कर रहे हैं (उदा. Koder.ai के साथ), तो शुरुआती चरण में include फ्लैग जोड़ना उन एंडपॉइंट्स को समय के साथ फूलने से रोकता है। यह आपको एक सुरक्षित तरीका देता है फ़ील्ड जोड़ने का बिना हर रिस्पॉन्स को भारी किए।
आपको अधिकांश परफ़ॉर्मेंस समस्याओं पकड़ने के लिए बड़ा टेस्ट लैब की ज़रूरत नहीं है। एक छोटा, दोहराने योग्य पास वे समस्याएँ उजागर करता है जो ट्रैफ़िक आने पर आउटेज में बदलती हैं, खासकर जब शुरुआती बिंदु जेनरेटेड कोड हो जिसको आप भेजने वाले हैं।
कुछ भी बदलने से पहले एक छोटा बेसलाइन लिखें:
छोटा शुरू करें, एक-एक करके बदलें, और हर परिवर्तन के बाद फिर से टेस्ट करें।
10–15 मिनट का लोड टेस्ट चलाएँ जो असली उपयोग जैसा दिखे। उन्हीं एंडपॉइंट्स को हिट करें जो आपके पहले उपयोगकर्ता हिट करेंगे (लॉगिन, लिस्ट पेज, सर्च, क्रिएट)। फिर रूट्स को p95 लेटेंसी और कुल समय के अनुसार सॉर्ट करें।
SQL ट्यून करने से पहले कनेक्शन प्रेशर जांचें। बहुत बड़ा पूल Postgres को ओवरव्हेल्म कर देता है; बहुत छोटा पूल लंबा इंतज़ार बनाता है। बर्स्ट के दौरान कनेक्शन गिनती और कनेक्शन हासिल करने का बढ़ता इंतज़ार देखें। सबसे पहले पूल और idle लिमिट्स समायोजित करें, फिर वही लोड फिर से चलाएँ।
सबसे धीमी क्वेरियों पर EXPLAIN चलाएँ और सबसे बड़े रेड-फ़्लैग को ठीक करें। सामान्य अपराधी बड़ी टेबल पर फुल स्कैन, बड़े रिज़ल्ट सेट पर सॉर्ट, और जॉइंस जो रो काउंट को बढ़ा देते हैं। सर्वाधिक खराब क्वेरी चुनें और उसे 'बोरिंग' बनाइए।
एक इंडेक्स जोड़ें या समायोजित करें, फिर फिर से टेस्ट करें। इंडेक्स तब मदद करते हैं जब वे आपके WHERE और ORDER BY से मिलते हों। एक बार में पाँच नहीं जोड़ें।
रिस्पॉन्स और पेजिनेशन कसें, फिर फिर से टेस्ट करें। अगर एक एंडपॉइंट बड़े JSON ब्लॉब्स के साथ 50 रो लौटाता है, तो DB, नेटवर्क और क्लाइंट सभी को कीमत चुकानी पड़ती है। केवल UI को चाहिए वही फ़ील्ड लौटाएँ, और पेजिनेशन चुनें जो टेबल बढ़ने पर धीमा न हो।
सरल चेंज-लॉग रखें: क्या बदला, क्यों बदला, और p95 में क्या बदला। अगर कोई बदलाव आपके बेसलाइन में सुधार नहीं करता, तो उसे revert कर दें और आगे बढ़ें।
Go APIs पर Postgres में अधिकांश परफ़ॉर्मेंस समस्याएँ आत्म-निर्मित होती हैं। अच्छी बात यह है कि कुछ जाँचें लॉन्च से पहले कई समस्याएँ पकड़ लेती हैं।
एक क्लासिक ट्रैप पूल साइज को "स्पीड नॉब" समझना है। इसे "जितना बड़ा हो उतना अच्छा" सेट करना अक्सर सब कुछ धीमा कर देता है। Postgres सत्रों, मेमोरी और लॉक को जुगाड़ने में अधिक समय लगाता है, और आपका ऐप लहरों में टाइमआउट करने लगता है। एक छोटा, स्थिर पूल और अनुमानित concurrency ज़्यादातर मामलों में बेहतर रहता है।
एक और आम गलती "सब कुछ इंडेक्स कर देना" है। अतिरिक्त इंडेक्स पढ़ने में मदद कर सकते हैं, पर वे राइट्स को धीमा करते हैं और कभी-कभी क्वेरी प्लान्स को चौंकाने वाले तरीकों से बदल देते हैं। ट्यून करने से पहले मापें, और इंडेक्स जोड़ने के बाद प्लान्स फिर से जांचें।
पेजिनेशन डेट-डेब्ट धीरे-धीरे चुपके से घुस आता है। ऑफसेट पेजिनेशन शुरुआती चरण में ठीक दिखती है, फिर p95 समय के साथ बढ़ता रहता है क्योंकि DB को और अधिक रोज़ के पास से गुजरना पड़ता है।
JSON पेलोड साइज भी एक छिपा हुआ कर है। कम्प्रेशन बैंडविड्थ कम कर सकता है, पर यह बड़े ऑब्जेक्ट बनाने, अलोकेट करने और पार्स करने की लागत को हटाता नहीं है। फ़ील्ड्स कम करें, गहरी नेस्टिंग से बचें, और केवल वही लौटाएँ जो स्क्रीन को चाहिए।
अगर आप केवल औसत प्रतिक्रिया समय देखते हैं, तो आप वास्तविक यूज़र की पीड़ा को मिस कर देंगे। p95 (और कभी-कभी p99) वहां दिखाई देता है जहाँ पूल सैचुरेशन, लॉक वेट्स, और स्लो प्लान्स पहले दिखते हैं।
एक त्वरित प्री-लॉन्च सेल्फ-चेक:
EXPLAIN फिर से चलाएँ।असली उपयोगकर्ता आने से पहले, यह प्रमाण चाहिए कि आपका API दबाव में अनुमानित रहता है। लक्ष्य परफेक्ट नंबर नहीं है—लक्ष्य उन कुछ समस्याओं को पकड़ना है जो टाइमआउट, स्पाइक्स, या डेटाबेस को नया काम स्वीकार करने से रोक देती हैं।
स्टेजिंग वातावरण में जाँचें जो प्रोडक्शन जैसा लगे (समान DB साइज़ रेंज, वही इंडेक्स, वही पूल सेटिंग्स): प्रमुख एंडपॉइंट पर लोड के दौरान p95 लेटेंसी मापें, कुल समय के अनुसार सबसे धीमी क्वेरियों को कैप्चर करें, सबसे खराब क्वेरी पर EXPLAIN (ANALYZE, BUFFERS) चलाकर पुष्टि करें कि यह अपेक्षित इंडेक्स का उपयोग कर रही है, और अपने सबसे व्यस्त रूट्स पर रिस्पॉन्स साइज की सैनीटी-चेक करें।
फिर एक worst-case रन करें जो दिखाता है कि प्रोडक्ट कैसे टूटता है: एक गहरा पेज रिक्वेस्ट करें, सबसे चौड़ा फ़िल्टर लगाएँ, और ठंडे स्टार्ट के साथ आज़माएँ (API रीस्टार्ट करें और वही रिक्वेस्ट पहले हिट करें)। अगर गहरी पेजिनेशन हर पेज पर धीमी हो रही है, तो लॉन्च से पहले cursor-based पेजिनेशन पर जाएँ।
अपनी डिफ़ॉल्ट्स लिख दें ताकि टीम बाद में सुसंगत निर्णय ले: पूल लिमिट्स और टाइमआउट, पेजिनेशन नियम (मैक्स पेज साइज, क्या ऑफसेट की अनुमति है, कर्सर फ़ॉर्मेट), क्वेरी नियम (सिर्फ ज़रूरी कॉलम चुनें, SELECT * से बचें, महंगे फ़िल्टर्स को कैप करें), और लॉगिंग नियम (स्लो क्वेरी थ्रेशोल्ड, सैंपल्स कितनी देर रखें, एंडपॉइंट लैबलिंग)।
अगर आप Koder.ai के साथ Go + Postgres सर्विसेज बनाते और एक्सपोर्ट करते हैं, तो डिप्लॉयमेंट से पहले एक छोटा प्लानिंग पास फ़िल्टर्स, पेजिनेशन, और रिस्पॉन्स शेप इरादतन रखने में मदद करता है। एक बार आप इंडेक्स और क्वेरी शेप ट्यून करने लगें, स्नैपशॉट्स और रोलबैक एक ही जगह से किसी "फिक्स" को जो एक एंडपॉइंट को सुधारे पर दूसरों को नुकसान पहुँचा सकता है—उसे उलटना आसान बनाते हैं। अगर आप इस वर्कफ़्लो पर एक जगह चाहें जहां इटरेट करना है, तो Koder.ai पर koder.ai इस तरह की सेवाओं को जेनरेट और परिष्कृत करने के चारों ओर बनाया गया है और जब आप तैयार हों तो सोर्स एक्सपोर्ट करने की सुविधा देता है।
प्रथमतः DB vänting time और एप्लिकेशन वर्क टाइम को अलग कर के देखें।
साधारण टाइमिंग जोड़ें: “कनेक्शन का इंतज़ार” और “क्वेरी निष्पादन”—देखें कौन सा पहलू ज्यादा समय ले रहा है।
शुरू करने के लिए एक छोटा और दोहराने योग्य बेसलाइन रखें:
एक स्पष्ट लक्ष्य चुनें, जैसे “50 concurrent users पर p95 200 ms से कम, एरर < 0.5%।” फिर एक बार में सिर्फ एक चीज़ बदलें और वही रिक्वेस्ट मिक्स फिर से टेस्ट करें।
हाँ — प्रीलॉन्च टेस्टिंग के दौरान धीमी क्वेरी लॉगिंग चालू कर दें (उदा. 100–200 ms)। पूरा स्टेटमेंट लॉग करें ताकि आप उसे SQL क्लाइंट में कॉपी कर सकें।
ध्यान रखें:
सबसे खराब क्वेरियों को मिलने के बाद आप सैंपलिंग पर स्विच कर सकते हैं या थ्रेशोल्ड बढ़ा सकते हैं।
एक व्यावहारिक डिफ़ॉल्ट है प्रति API इंस्टेंस CPU को एक छोटा गुणक मानना—अक्सर 5–20 max open connections, समान max idle connections के साथ, और कनेक्शनों को हर 30–60 मिनट पर रीसायकल करना।
दो सामान्य विफलता मोड:
याद रखें कि पूल इंस्टेंसों के साथ गुणा होता है (20 × 10 = 200 कनेक्शन)।
DB कॉल को दो हिस्सों में टाइम करें:
अगर अधिकांश समय pool wait में जाता है, तो पूल साइजिंग, टाइमआउट और इंस्टेंस काउंट एडजस्ट करें। अगर अधिकांश समय क्वेरी में है, तो EXPLAIN और इंडेक्स पर ध्यान दें।
सुनिश्चित करें कि आप हमेशा rows बंद कर रहे हैं ताकि कनेक्शन्स जल्दी से पूल में लौटें।
EXPLAIN (ANALYZE, BUFFERS) उस सही SQL पर चलाएँ जो आपका API भेजता है और इन चीज़ों पर ध्यान दें:
ORDER BY के साथ जुड़े हुए Sort जो समय ले रहे होंइंडेक्स वही मदद करते हैं जो आपके API की क्वेरी करती है: filters + sort order।
अच्छा उपाय:
WHERE + ORDER BY पैटर्न के लिए composite index बनाएं।जब अधिक ट्रैफ़िक एक अनुमानित उपसमूह पर आता है तो partial index उपयोगी है।
उदाहरण:
active = true के लिएऐसा partial index (... WHERE active = true) छोटे रहता है, मेमोरी में रहने की संभावना बढ़ती है, और सब कुछ इंडेक्स करने की तुलना में राइट-ओवरहेड कम होता है।
पक्का करने के लिए चलाएँ कि Postgres आपके हाई-ट्रैफ़िक क्वेरीज़ में इसे उपयोग कर रहा है।
LIMIT/OFFSET गहरे पेज पर धीमा हो जाता है क्योंकि Postgres अभी भी उन रोज़ को स्कैन/पार कर रहा होता है जिन्हें आप छोड़ रहे हैं। पेज 1 में कुछ दर्जन रोज़ देखने पड़ सकती हैं, पेज 500 पर हजारों स्कैन और डिसकार्ड करने पड़ सकते हैं।
इसके बजाय keyset (cursor) pagination उपयोग करें:
आम तौर पर हाँ — खासकर लिस्ट एंडपॉइंट्स के लिए। सबसे तेज़ उत्तर वही है जो आप नहीं भेजते।
व्यावहारिक कदम:
SELECT * से बचें)।include= या fields= दें ताकि क्लाइंट भारी फ़ील्ड्स को ऑप्ट-इन करे।सबसे बड़ा रेड फ़्लैग पहले ठीक करें; सब कुछ एक साथ ट्यून न करें।
उदाहरण: यदि आप user_id से फ़िल्टर करते हैं और सबसे नए दिखाते हैं, तो (user_id, created_at DESC) जैसा इंडेक्स अक्सर p95 में बड़ा फर्क लाता है।
EXPLAINid) रखें।ORDER BY एक जैसा रखें।(created_at, id) जैसे मानों को कर्सर में एन्कोड करें।इससे टेबल बढ़ने पर भी हर पेज की लागत लगभग स्थिर रहती है।
अक्सर केवल पेलोड छोटा करके आप Go CPU, मेमोरी दबाव और टेल लेटेंसी घटा देंगे।