PostgreSQL সংযোগ পুলিং: Go ব্যাকএন্ডগুলির জন্য অ্যাপ পুল বনাম PgBouncer তুলনা, মনিটর করার মেট্রিক্স এবং ভুল কনফিগারেশন যা লেটেনসি স্পাইক ট্রিগার করে।

একটি ডাটাবেস কনেকশন আপনার অ্যাপ এবং Postgresের মধ্যে একটি ফোন লাইনের মতো। একটি কনেকশন খুলতে সময় এবং উভয় পাশে কাজ লাগে: TCP/TLS সেটআপ, অথেনটিকেশন, মেমরি, এবং Postgres সাইডে একটি ব্যাকএন্ড প্রসেস। একটি কনেকশন পুল এই "ফোন লাইন"-এর ছোট একটি সেট খোলা রেখে দেয় যাতে আপনার অ্যাপ প্রতিটি অনুরোধে নতুন ডায়াল না করে সেগুলো পুনরায় ব্যবহার করতে পারে।
পুলিং বন্ধ বা ভুল মাপের হলে, আপনি সচরাচর প্রথমে কোনও পরিস্কার ত্রুটি পান না। পরিবর্তে এলোমেলো ধীরতা দেখা দেয়। সাধারণত 20-50 ms নেওয়া অনুরোধগুলো হঠাৎ 500 ms বা 5 সেকেন্ড হয়ে যেতে পারে, এবং p95 নিক্ষিপ্ত হয়। তারপর টাইমআউট দেখা দেয়, পরে "too many connections" বা অ্যাপের ভিতরে একটি কিউ দেখা দেয় যখন এটি একটি ফ্রি কনেকশনের জন্য অপেক্ষা করে।
কনেকশন সীমা ছোট অ্যাপের জন্যও গুরুত্বপূর্ণ কারণ ট্র্যাফিক হল বর্সটি। একটি মার্কেটিং ইমেইল, একটি ক্রন জব, বা কয়েকটি ধীর এন্ডপয়েন্ট ডাটাবেসে একসাথে ডজনগুলি অনুরোধ পাঠাতে পারে। যদি প্রতিটি অনুরোধ একটি নতুন কনেকশন খুলে, তাহলে Postgres সংযোগ গ্রহণ এবং পরিচালনায় অনেক ক্ষমতা ব্যয় করতে পারে প্রকৃত কুয়েরি চালানোর বদলে। আর যদি আপনার পুল থাকে কিন্তু সেটি খুব বড়, আপনি অনেক সক্রিয় ব্যাকএন্ড দিয়ে Postgres-কে ওভারলোড করে দিতে পারেন এবং কন্টেক্সট সুইচিং ও মেমরি প্রেসার ট্রিগার করতে পারেন।
শুরুতে লক্ষণগুলো নজর রাখুন যেমন:
পুলিং কনেকশন চর্ন কমায় এবং Postgres-কে বর্স সামলাতে সাহায্য করে। এটি ধীর SQL ঠিক করবে না। যদি একটি কুয়েরি ফুল টেবিল স্ক্যান করে বা লকের জন্য অপেক্ষা করে, পুলিং মূলত সিস্টেম কিভাবে ফেল করে তা বদলায় (জোরে কিউ করা, পরে টাইমআউট), কিন্তু কুয়েরি দ্রুত করবে না।
কনেকশন পুলিং হলো একসাথে কতগুলি ডাটাবেস কনেকশন থাকবে এবং কীভাবে সেগুলো পুনরায় ব্যবহার হবে তা নিয়ন্ত্রণ করা। আপনি এটি আপনার অ্যাপে (অ্যাপ-লেভেল পুলিং) করতে পারেন বা Postgres-এর সামনে একটি আলাদা সার্ভিস দিয়ে (PgBouncer)। এরা সম্পর্কিত কিন্তু ভিন্ন সমস্যা সমাধান করে।
অ্যাপ-লেভেল পুলিং (Go-তে সাধারণত বিল্ট-ইন database/sql পুল) প্রতি প্রসেসে কনেকশন ম্যানেজ করে। এটি সিদ্ধান্ত নেয় কখন নতুন কনেকশন খুলবে, কখন একটি ব্যবহার করবে এবং কখন আইডলগুলো বন্ধ করবে। এটি প্রতিটি অনুরোধে সেটআপ খরচ এড়ায়। যা এটি করতে পারে না তা হল বহু অ্যাপ ইনস্ট্যান্স জুড়ে সমন্বয় করা। যদি আপনি 10 রেপ্লিকা চালান, আপনার কাছে কার্যত 10 আলাদা পুল থাকবে।
PgBouncer আপনার অ্যাপ এবং Postgres-এর মধ্যে বসে এবং অনেক ক্লায়েন্টের পক্ষ থেকে পুলিং করে। এটি সবচেয়ে দরকারী যখন আপনার অনেক ছোট-স্থায়ী অনুরোধ, অনেক অ্যাপ ইনস্ট্যান্স, বা স্পাইকিং ট্র্যাফিক থাকে। এটি Postgres-এ সার্ভার-সাইড কনেকশন সীমা আরোপ করে এমনকি শত শত ক্লায়েন্ট কনেকশন একসাথে এলে।
দায়িত্বগুলোর সাধারণ বিভাজন:
তারা একসঙ্গে কাজ করতে পারে যতক্ষণ প্রতিটি স্তরের স্পষ্ট উদ্দেশ্য থাকে: প্রতিটি Go প্রসেসে একটি যুক্ত database/sql পুল এবং PgBouncer একটি গ্লোবাল কনেকশন বাজেট বাস্তবায়ন করবে।
একটি সাধারণ ভুল ধারণা হলো “আরও পুল মানে আরও ক্ষমতা।” বেশিরভাগ ক্ষেত্রেই এর বিপরীতই হয়। যদি প্রতিটি সার্ভিস, ওয়ার্কার, এবং রেপ্লিকা নিজ নিজ বড় পুল রাখে, মোট কনেকশন কাউন্ট বিস্তৃত হয়ে যেতে পারে এবং কিউইং, কন্টেক্সট সুইচিং, এবং হঠাৎ লেটেনসি স্পাইক ঘটাতে পারে।
database/sql পুলিং আসলে কীভাবে আচরণ করেGo-তে, sql.DB একটি কনেকশন পুল ম্যানেজার, একটি একক কনেক্সন নয়। যখন আপনি db.Query বা db.Exec কল করেন, database/sql একটি আইডল কনেকশন পুনরায় ব্যবহার করার চেষ্টা করে। যদি পারে না, এটি আপনার সীমা পর্যন্ত একটি নতুন কনেকশন খুলতে পারে বা রিকোয়েস্টটিকে অপেক্ষা করাতে পারে।
এই অপেক্ষাই প্রায়ই “রহস্যজনক লেটেনসি”র উৎস। যখন পুল স্যাচুরেটেড হয়, অনুরোধগুলো আপনার অ্যাপের ভিতরে কিউ করে। বাইরের থেকে এটি মনে হতে পারে যে Postgres ধীর হয়ে গেছে, কিন্তু সময়টা আসলে একটি ফ্রি কনেকশনের জন্য অপেক্ষায় কাটে।
বেশিরভাগ টিউনিং চারটি সেটিংসে নেমে আসে:
MaxOpenConns: ওপেন কনেকশনের উপর হার্ড ক্যাপ (idle + in use)। যখন আপনি এটিতে পৌঁছান, কলাররা ব্লক হয়।MaxIdleConns: কতটি কনেকশন রেডি করে রাখা যাবে পুনরায় ব্যবহারের জন্য। খুব কম রাখা হলে বারবার রিকনেক্ট হবে।ConnMaxLifetime: কনেকশনগুলো সময়সীমার পরে পুনঃচক্র করে—লোড ব্যালেন্সার এবং NAT টাইমআউটের জন্য সহায়ক, কিন্তু খুব কম হলে চর্ন বাড়ে।ConnMaxIdleTime: অনেকক্ষণ বেকায়দায় থাকা কনেকশনগুলো বন্ধ করে দেয়।কনেকশন পুনরায় ব্যবহার সাধারণত লেটেনসি এবং ডাটাবেস CPU কমায় কারণ বারবার সেটআপ (TCP/TLS, auth, session init) এড়ানো যায়। কিন্তু একটি অতিরিক্ত বড় পুল উল্টোটাই করতে পারে: এটি Postgres ভালভাবে হ্যান্ডেল করতে না পারার চেয়ে বেশি একযোগে কুয়েরি চালাতে দেয়, ফলে প্রতিদ্বন্দ্বিতা এবং ওভারহেড বেড়ে যায়।
প্রতি প্রসেস নয়, মোটে ভাবুন। যদি প্রতিটি Go ইনস্ট্যান্স 50 ওপেন কনেকশন অনুমোদন করে এবং আপনি 20 ইনস্ট্যান্সে স্কেল করেন, কার্যত 1,000 কনেকশন অনুমোদন করেছেন। সেই সংখ্যাটি আপনার Postgres সার্ভার বাস্তবে কত ভালোভাবে চালাতে পারে তার সাথে তুলনা করুন।
ব্যবহারিক একটি সূচনা পয়েন্ট হলো MaxOpenConns-কে প্রত্যাশিত প্রতিটি ইনস্ট্যান্সের কনকারেন্সির সাথে বধির করা, তারপর পুল মেট্রিক্স (in-use, idle, wait time) ভ্যালিডেট করে বাড়ানো।
PgBouncer একটি ছোট প্রক্সি যা আপনার অ্যাপ এবং PostgreSQL-এর মধ্যে বসে। আপনার সার্ভিস PgBouncer-এ সংযোগ করে, এবং PgBouncer Postgres-এ সীমিত সংখ্যক বাস্তব সার্ভার কনেকশন ধরে রাখে। স্পাইকের সময়, PgBouncer ক্লায়েন্ট কাজ কিউ করে নতুন সার্ভার ব্যাকএন্ড তৈরি করার বদলে। সেই কিউই নিয়ন্ত্রিত ধীরতা এবং ডাটাবেস ক্র্যাশের মধ্যে পার্থক্য করতে পারে।
PgBouncer-এর তিনটি পুলিং মোড আছে:
Session pooling সরাসরি Postgres-এ ডিরেক্ট কনেকশনের মতো আচরণ করে—এটি সবচেয়ে কম অবাক করা পুরোনো আচরণ দেখায়, কিন্তু বর্সি লোডে সার্ভার কনেকশন কম সেভ করে।
সাধারণ Go HTTP API-র জন্য transaction pooling প্রায়শই একটি শক্ত বিখ্যাত ডিফল্ট। বেশিরভাগ অনুরোধ ছোট কুয়েরি বা সংক্ষিপ্ত ট্রানজেকশন করে, তারপর শেষ হয়ে যায়। transaction pooling অনেক ক্লায়েন্ট কনেকশনকে একটি ছোট Postgres কনেকশন বাজেটে ভাগ করতে দেয়।
বাণিজ্যটি হলো সেশন স্টেট। transaction মোডে, সেই সব জিনিস যারা একটি একক সার্ভার কনেকশনে পিন করা থাকে তা ভাঙতে পারে বা অদ্ভুত আচরণ করতে পারে, যার মধ্যে আছে:
SET, SET ROLE, search_path)আপনার অ্যাপ যদি এমন ধরণের স্টেটের উপর নির্ভর করে, তাহলে session pooling নিরাপদ। Statement pooling সবচেয়ে সীমিত এবং ওয়েব অ্যাপের জন্য বিরলভাবে মানায়।
একটি দরকারী নিয়ম: যদি প্রতিটি অনুরোধ একটি ট্রানজেকশনের মধ্যে যা দরকার তা সেট আপ করতে পারে, তাহলে transaction pooling লোডের সময় লেটেনসি বেশি স্থিতিশীল রাখে। যদি আপনাকে দীর্ঘ-স্থায়ী সেশন আচরণ দরকার হয়, session pooling ব্যবহার করুন এবং অ্যাপের সীমা আরও কড়া করুন।
যদি আপনি database/sql সহ একটি Go সার্ভিস চালান, আপনার কাছে ইতিমধ্যেই অ্যাপ-সাইড পুলিং আছে। বহু টিমের জন্য সেটাই যথেষ্ট: কয়েকটি ইনস্ট্যান্স, স্থির ট্র্যাফিক, এবং এমন কুয়েরি যা অত্যধিক স্পাইকি নয়। সেই সেটআপে, সবচেয়ে সহজ এবং নিরাপদ বিকল্প হলো Go পুল টিউন করা, ডাটাবেস কনেকশন সীমা বাস্তবসম্মত রাখা, এবং সেখানে থামা।
PgBouncer সবচেয়ে উপকারী যখন ডাটাবেস অনেক ক্লায়েন্ট কনেকশনের আঘাতে পড়ছে। এটি দেখা যায় যখন অনেক অ্যাপ ইনস্ট্যান্স (অথবা সার্ভারলেস-স্টাইল স্কেলিং), বর্সি ট্র্যাফিক, এবং অনেক ছোট কুয়েরি থাকে।
PgBouncer ভুল মোডে ব্যবহার করলে ক্ষতি করতে পারে। যদি আপনার কোড সেশন স্টেটে নির্ভর করে (temporary tables, prepared statements অমনভাবে পুনরায় ব্যবহার, advisory locks কলের বাইরে ধরে রাখা, বা সেশন-লেভেল সেটিংস), transaction pooling বিভ্রান্তিকর ব্যর্থতা ঘটাতে পারে। যদি সত্যিই সেশন আচরণ দরকার হয়, session pooling ব্যবহার করুন অথবা PgBouncer এড়িয়ে অ্যাপ পুলগুলো সাবধানে মাপুন।
এ নিয়মটি অনুসরণ করুন:
কনেকশন সীমা হলো একটি বাজেট। যদি আপনি একসাথে সব খরচ করে ফেলেন, প্রতিটি নতুন অনুরোধ অপেক্ষা করে এবং টেইল লেটেনসি ঝাঁপিয়ে ওঠে। লক্ষ্য হলো কনকারেন্সি নিয়ন্ত্রণ করা একটি নিয়ন্ত্রিত উপায়ে যতক্ষণ থ্রুপুট স্থিতিশীল থাকে।
আজকের পিক এবং টেইল লেটেনসি মাপুন। পিক অ্যাকটিভ কনেকশনের সংখ্যা (গড় নয়), এবং অনুরোধ ও কী কুয়েরির p50/p95/p99 রেকর্ড করুন। কোনও কনেকশন ত্রুটি বা টাইমআউট নোট করুন।
অ্যাপের জন্য নিরাপদ Postgres কনেকশন বাজেট সেট করুন। max_connections থেকে শুরু করে অ্যাডমিন অ্যাক্সেস, মাইগ্রেশন, ব্যাকগ্রাউন্ড জব, এবং স্পাইকগুলোর জন্য হেডরুম বাদ দিন। যদি একাধিক সার্ভিস ডাটাবেস ভাগ করে, বাজেটটি ইচ্ছাকৃতভাবে ভাগ করুন।
বাজেটকে Go সীমায় ম্যাপ করুন ইনস্ট্যান্স অনুযায়ী। অ্যাপ বাজেটকে ইনস্ট্যান্স সংখ্যায় ভাগ করুন এবং MaxOpenConns সেট করুন (বা একটু কম)। MaxIdleConns এতটুকু রাখুন যাতে বারবার রিকনেক্ট না লাগে, এবং লাইফটাইমগুলো এমন রাখুন যাতে কনেকশন মাঝে মাঝে রিসাইকেল হয় কিন্তু চর্ন না বাড়ে।
প্রয়োজন হলে PgBouncer যোগ করুন এবং একটি মোড বাছুন। session state দরকার হলে session pooling ব্যবহার করুন। অ্যাপ সামঞ্জস্যপূর্ণ থাকলে এবং সার্ভার কনেকশন কমাতে চান তাহলে transaction pooling ব্যবহার করুন।
ধীরে রোলআউট করুন এবং আগে-বাতিল তুলনা করুন। একেবারে এক সময়ে একটিই পরিবর্তন করুন, canary করে দেখুন, তারপর টেইল লেটেনসি, পুল ওয়েট টাইম এবং ডাটাবেস CPU তুলনা করুন।
উদাহরণ: যদি Postgres নিরাপদে আপনার সার্ভিসকে 200 কনেকশন দিতে পারে এবং আপনি 10 Go ইনস্ট্যান্স চালান, প্রতিটি ইনস্ট্যান্সে MaxOpenConns=15-18 দিয়ে শুরু করুন। এতে বর্সের জন্য জায়গা থাকে এবং সম্ভাবনা কমে যে প্রতিটি ইনস্ট্যান্স একসাথে সিলিং ছুঁবে।
পুলিং সমস্যাগুলো খুব কমই প্রথমে “অনেক কনেকশন” হিসেবে দেখা দেয়। সাধারণত আপনি ওয়েট সময়ে ধীরে ধীরে বাড়া দেখেন এবং তারপর p95/p99 হঠাৎ জাম্প করে।
প্রথমে আপনার Go অ্যাপ রিপোর্ট করা মেট্রিক্স দেখে শুরু করুন। database/sql দিয়ে open connections, in-use, idle, wait count, এবং wait time মনিটর করুন। যদি ট্র্যাফিক স্থির থাকার সময় wait count বাড়ে, আপনার পুল undersized বা কনেকশনগুলো বেশি সময় ধরে রাখা হচ্ছে।
ডাটাবেস সাইডে, active connections বনাম max, CPU, এবং lock activity ট্র্যাক করুন। যদি CPU কম কিন্তু লেটেনসি বেশি, সাধারণত এটি কিউইং বা লক হওয়া—কাঁচা কম্পিউট নয়।
আপনি যদি PgBouncer চালান, তাহলে একটি তৃতীয় ভিউ যোগ করুন: client connections, server connections to Postgres, এবং queue depth। একটি বাড়তে থাকা কিউ যখন সার্ভার কনেকশন স্থির থাকে, এটা স্পষ্ট চিহ্ন যে বাজেট স্যাচুরেট হয়েছে।
ভাল অ্যালার্ট সিগন্যাল:
পুলিং সমস্যা প্রায়শই বর্সগুলোর সময় দেখা দেয়: অনুরোধগুলো কনেকশনের জন্য অপেক্ষা করে জমা হয়, তারপর সবকিছু আবার ঠিকঠাক থাকে। মূল কারণ প্রায়শই এমন একটি সেটিং যা একটি ইনস্ট্যান্সে যুক্তিসংগত হলেও বহু কপি চালালে বিপজ্জনক হয়ে ওঠে।
সাধারণ কারণগুলো:
MaxOpenConns সেট করা কিন্তু গ্লোবাল বাজেট না রাখা। প্রতি ইনস্ট্যান্সে 100 কনেকশন এবং 20 ইনস্ট্যান্স মানে 2,000 সম্ভাব্য কনেকশন।ConnMaxLifetime / ConnMaxIdleTime খুব ছোট। একবারে অনেক কনেকশন রিসাইকেল হলে reconnect storms ঘটতে পারে।স্পাইক কমানোর সহজ উপায় হলো পুলিংকে একটি ভাগ করা সীমা হিসাবে বিবেচনা করা, অ্যাপ-লোকাল ডিফল্ট নয়: মোট কনেকশনগুলো কেপ করুন, একটি শালীন আইডল পুল রাখুন, এবং পুনঃসংযোগ সমসময়ে ঘটার মত ছোট লাইফটাইম এড়ান।
যখন ট্র্যাফিক বাড়ে, সাধারণত তিনটি ফলাফল দেখা যায়: অনুরোধগুলো ফ্রি কনেকশনের জন্য কিউ করে, অনুরোধগুলো টাইমআউট করে, অথবা সবকিছু এত ধীর হয়ে যায় যে রিট্রাই গুলো জমে যায়।
কিউইং হল সাবধানে ছদ্মবেশী একটি সমস্যা। আপনার হ্যান্ডলার এখনও চলছে, কিন্তু এটি কনেকশনের জন্য পার্ক করা আছে। সেই অপেক্ষা প্রতিক্রিয়া সময়ের অংশ হয়ে যায়, তাই একটি ছোট পুল 50 ms কুয়েরি থেকে কয়েক সেকেন্ডের এন্ডপয়েন্টে পরিণত করতে পারে লোডের সময়।
একটি সহায়ক মানসিক মডেল: যদি আপনার পুলে 30 টি ব্যবহারযোগ্য কনেকশন থাকে এবং হঠাৎ 300 concurrent অনুরোধ আসে যা সবকটি ডাটাবেস দরকার, তাহলে 270টি অপেক্ষা করতে হবে। যদি প্রতিটি অনুরোধ একটি কনেকশন 100 ms ধরে রাখে, টেইল লেটেনসি দ্রুত সেকেন্ডে পৌঁছে যাবে।
একটি স্পষ্ট টাইমআউট বাজেট সেট করুন এবং তাতে অনড় থাকুন। অ্যাপ টাইমআউট হওয়া উচিত ডাটাবেস টাইমআউটের তুলনায় সামান্য ছোট যাতে আপনি দ্রুত ব্যর্থ হন এবং চাপ কমান, কখনই অনির্দিষ্ট সময় অপেক্ষা না করে।
statement_timeout যাতে একটি খারাপ কুয়েরি কনেকশন ধরে না রাখতে পারেতারপর ব্যাকপ্রেশার যোগ করুন যাতে আপনি পুল ওভারলোড না করেন। এক বা দুইটি প্রত্যাশিত মেকানিজম বেছে নিন, যেমন এন্ডপয়েন্ট প্রতি কনকারেন্সি সীমা, লোড শেডিং স্পষ্ট ত্রুটির মাধ্যমে (যেমন 429), অথবা ব্যাকগ্রাউন্ড জবগুলোকে ব্যবহারকারী ট্র্যাফিক থেকে আলাদা করা।
সবশেষে, ধীর কুয়েরিগুলো প্রথমে ঠিক করুন। পুলিং চাপের অধীনে ধীর কুয়েরি বেশি সময় ধরে কনেকশন ধরে রাখে, যা অপেক্ষা বাড়ায়, টাইমআউট বাড়ায়, রিট্রাই ট্রিগার করে। সেই ফিডব্যাক লুপেই "অল্প ধীর" থেকে "সবকিছু ধীর" হয়ে যায়।
লোড টেস্টিংকে কেবল থ্রুপুট যাচাই হিসেবে দেখবেন না—এটি আপনার কনেকশন বাজেট ভ্যালিডেট করার উপায় ভাবুন। লক্ষ্য হলো পুলিং চাপের অধীনে কীভাবে আচরণ করে তা স্টেজিং ও প্রোডাকশনে একই আছে কিনা নিশ্চিত করা।
বাস্তবসম্মত ট্রাফিক দিয়ে টেস্ট করুন: একই রিকোয়েস্ট মিক্স, বর্স প্যাটার্ন এবং একই সংখ্যক অ্যাপ ইনস্ট্যান্স যা আপনি প্রোডাকশনে চালান। "একটি এন্ডপয়েন্ট" বেঞ্চমার্কগুলো প্রায়শই পুল সমস্যাগুলো লঞ্চ দিবস পর্যন্ত লুকিয়ে রাখে।
ওয়ার্ম-আপ অন্তর্ভুক্ত করুন যাতে আপনি ঠাণ্ডা ক্যাশ ও র্যাম্প-আপ প্রভাব মাপেন না। পুলগুলো তাদের স্বাভাবিক আকারে পৌঁছাতে দিন, তারপর রেকর্ড করা শুরু করুন।
যদি আপনি কৌশলগুলি তুলনা করে দেখতে চান, একই ওয়ার্কলোড রেখে চালান:
database/sql, কোন PgBouncer নেই)প্রতি রানে একটি ছোট স্কোরকার্ড রেকর্ড করুন যা আপনি প্রতিটি রিলিজের পর পুনরায় ব্যবহার করতে পারবেন:
সময়ের সাথে এটি ক্যাপাসিটি প্ল্যানিংকে অনুমানভিত্তিক না করে পুনরাবৃতযোগ্য একটি প্রক্রিয়ায় পরিণত করে।
পুল সাইজ টাচ করার আগে একটি নম্বর লিখে রাখুন: আপনার কনেকশন বাজেট। এটি সেই পরিবেশের জন্য সর্বোচ্চ নিরাপদ সক্রিয় Postgres কনেকশনের সংখ্যা (dev, staging, prod), ব্যাকগ্রাউন্ড জব এবং অ্যাডমিন অ্যাক্সেসসহ। আপনি যদি এটি বলতে না পারেন, আপনি অনুমান করছেন।
দ্রুত চেকলিস্ট:
MaxOpenConns) বাজেটের মধ্যে পড়ে (অথবা PgBouncer cap-এর অধীনে)।max_connections এবং সংরক্ষিত কোন কনেকশনগুলো আপনার পরিকল্পনার সাথে সমন্বয় আছে।রোলআউট প্ল্যান যা রোলব্যাক সহজ রাখে:
আপনি যদি Koder.ai (koder.ai)-এ Go + PostgreSQL অ্যাপ তৈরি এবং হোস্ট করেন, Planning Mode আপনাকে পরিবর্তনটি মানচিত্র করতে এবং আপনি কী মাপবেন তা নির্ধারণে সাহায্য করতে পারে, এবং স্ন্যাপশট এবং রোলব্যাক দ্রুত করার ফলে টেইল লেটেনসি খারাপ হলে ফিরে যাওয়া সহজ হয়।
পরবর্তী ধাপ: পরের ট্র্যাফিক বৃদ্ধির আগে একটি মাপ নেওয়া শুরু করুন। অ্যাপ-এ "কনেকশনের জন্য অপেক্ষায় কাটানো সময়" প্রায়ই সবচেয়ে দরকারী, কারণ এটি ব্যবহারকারীরা অনুভব করার আগেই পুলিং চাপ দেখায়।
একটি পুল PostgreSQL কনেকশনের একটি ছোট সেট খোলা রেখে তা অনুরোধগুলোর মধ্যে পুনরায় ব্যবহার করে। এতে TCP/TLS, অথেনটিকেশন এবং ব্যাকএন্ড প্রসেস সেটআপের খরচ বারবার দিতে হয় না, ফলে ট্র্যাফিক বর্ধনের সময় টেইল লেটেনসি স্থিতিশীল রাখতে সহায়তা করে।
যখন পুল স্যাটুরেটেড হয়, অনুরোধগুলো অ্যাপের ভিতরে খালি একটি কনেকশনের জন্য অপেক্ষা করে এবং সেই অপেক্ষার সময় ধীর প্রতিক্রিয়া হিসেবে দেখা যায়। ফলে মাঝে মাঝে দ্রুতগতির অনুরোধগুলো হঠাৎ পীড়াদায়ক ধীর হয়ে যায়—গড় ভালো থাকলেও p95/p99 ঝাঁপিয়ে উঠতে পারে।
না—পুলিং মূলত পুনরায় সংযোগের চাপ কমায় এবং কনকারেন্সি নিয়ন্ত্রণ করে যে কিভাবে সিস্টেম লোডে আচরণ করবে। যদি কোন কুয়েরি ধীর হয় (ফুল টেবিল স্ক্যান, লক বা খারাপ ইনডেক্সিং), পুলিং সেটা দ্রুত করবে না; এটি শুধুমাত্র ধীর কুয়েরিগুলো এক সাথে কতটি চলবে তা সীমিত করে।
অ্যাপ-লেভেল পুলিং প্রতি প্রসেসে সংযোগ পরিচালনা করে, তাই প্রতিটি অ্যাপ ইনস্ট্যান্সের নিজস্ব পুল ও সীমা থাকে। PgBouncer Postgres-এর সামনে বসে একটি গ্লোবাল কনেকশন বাজেট জারি করে এবং অনেক ক্লায়েন্টের জন্য সার্ভার-সাইড সংযোগ সীমাবদ্ধ করে, যা বেশ উপকারী যখন অনেক রেপ্লিকা বা স্পাইকিং ট্র্যাফিক থাকে।
যদি ইনস্ট্যান্স সংখ্যা কম এবং মোট ওপেন কনেকশন ডেটাবেস সীমার মধ্যে থাকে, তখন Go-এর database/sql পুল টিউন করা যথেষ্ট। যখন অনেক ইনস্ট্যান্স, অটোস্কেলিং বা স্পাইকিং ট্র্যাফিক মোট কনেকশনকে ডাটাবেসের সহনসীমা ছাড়িয়ে যেতে পারে, তখন PgBouncer যোগ করুন।
একটি ভাল ডিফল্ট হলো সার্ভিসের জন্য একটি মোট কনেকশন বাজেট নির্ধারণ করা, তারপর সেটিকে ইনস্ট্যান্স সংখ্যায় ভাগ করে প্রতিটি ইনস্ট্যান্সের জন্য MaxOpenConns সেট করা—প্রতি ইনস্ট্যান্সে সামান্য কম রাখতে পারেন। ছোট থেকে শুরু করুন, ওয়েট টাইম এবং p95/p99 দেখুন, এবং শুধুমাত্র ডাটাবেসে হেডরুম নিশ্চিত হলে বাড়ান।
বহুল HTTP API-র জন্য transaction pooling প্রায়শই একটি শক্ত ডিফল্ট কারণ এটি অনেক ক্লায়েন্ট কনেকশনকে কম সার্ভার কনেকশনে ভাগ করতে দেয় এবং স্পাইকিং অবস্থায় স্থিতিশীল থাকে। যদি আপনার কোড সার্ভার সেশন-স্টেট ধরে রাখার উপর নির্ভর করে, তাহলে session pooling ব্যবহার করুন।
prepared statements, temporary tables, advisory locks, এবং সেশন-লেভেলের সেটিংস ভিন্নভাবে আচরণ করতে পারে কারণ ক্লায়েন্ট পরের বার একই সার্ভার কনেকশন পাবে না। যদি এসব ফিচার দরকার হয়, তাহলে প্রতিটি অনুরোধের মধ্যে সবকিছু একটিমাত্র ট্রানজেকশনের মধ্যে রাখুন বা session pooling ব্যবহার করুন।
p95/p99 লেটেনসি এবং অ্যাপ পুল ওয়েট টাইম একসাথে দেখুন—ওয়েট টাইম সাধারণত ব্যবহারকারীরা শিখতে আগেই বাড়তে থাকে। Postgres-এ active connections, CPU, এবং locks ট্র্যাক করুন; PgBouncer-এ client connections, server connections এবং queue depth দেখুন—একটি বাড়তে থাকা কিউ সহ স্থিতিশীল সার্ভার কনেকশন স্পষ্ট সংকেত যে বাজেট স্যাচুরেট হচ্ছে।
প্রথমে অনির্দিষ্ট অপেক্ষা বন্ধ করুন: রিকোয়েস্ট ডেডলাইন এবং DB স্টেটমেন্ট টাইমআউট সেট করুন যাতে একটি ধীর কুয়েরি কনেকশন চিরকাল ধরে রাখতে না পারে। তারপর ব্যাকপ্রেশার যোগ করুন—DB-ভারি এন্ডপয়েন্টগুলোর কনকারেন্সি সীমাবদ্ধ করা বা লোড শেডিং (যেমন 429) করা। এবং reconnect storms এড়াতে অত্যন্ত ছোট কানেকশন লাইফটাইম পরিহার করুন।