ঘুম থেকে জেগে ওঠা ডেটাবেস, মৃত connection ছাড়াই
Mythex-এ published app এবং তার database কেউ ব্যবহার না করলে ঘুমিয়ে পড়ে। কোনো app জেগে উঠলে তার প্রথম অনুরোধ ১০–১২ সেকেন্ড আটকে থাকত, কারণ সে এমন database connection আবার ব্যবহার করতে চাইত, যেগুলো সবকিছু ঘুমিয়ে থাকার সময় মরে গিয়েছিল। এখন আমরা Spring Boot app-কে এমন database সেটিং দিই, যা ঘুম থেকে জাগার পরেও নিরাপদ, এবং এজেন্টকে বলি যে যেকোনো stack-এর app একই উপায়ে বানাতে হবে। hosting platform-এ ঘুম থেকে জাগার পর প্রথম অনুরোধ ১২.৭ সেকেন্ড থেকে ২.৩ সেকেন্ডে নেমে এসেছে।
কেন app আর database ঘুমায়
Mythex-এ একটি published app এমন একটি machine-এ চলে, যা কয়েক মিনিট অলস থাকলে স্থগিত হয়ে যায়। আর তার database হলো serverless Postgres, যা পাঁচ মিনিট অলস থাকলে ঘুমায় এবং পরের query-তে জেগে ওঠে। এর ফলেই শান্ত একটি app সস্তা থাকে: database যা আসলে ব্যবহার করেছে তার জন্য আপনার চার্জ হয়, আর ঘুমন্ত app compute-এর পেছনে কিছু খরচ করে না। billing-এর দিক নিয়ে আমরা লিখেছি database-এর billing কীভাবে আসল ব্যবহার অনুযায়ী হয়।
সমস্যা হলো, দুটো আলাদাভাবে ঘুমায় এবং connection কী, সে বিষয়ে তাদের মত এক নয়।
লক্ষণ: প্রথম ভিজিট ধীর
বেশির ভাগ server app একটি connection pool রাখে: কয়েকটি database connection আগে থেকে খোলা থাকে, যাতে প্রতিটি অনুরোধকে নতুন connection খোলার খরচ বহন করতে না হয়। যে app সবসময় চালু থাকে, তার জন্য এটি সঠিক ডিজাইন।
ঘুমের সঙ্গে যা ঘটে:
- App চলছে, pool-এ কয়েকটি অলস connection আছে।
- কেউ ভিজিট করছে না। App-এর machine স্থগিত হয়, জমে থাকা অবস্থায় pool অক্ষত থাকে।
- Database ঘুমিয়ে পড়ে এবং প্রতিটি connection-এর তার দিকটি বাদ দেয়।
- একজন ভিজিটর আসে। App ঠিক যেমন ছিল তেমনভাবে জেগে ওঠে, এখনও এমন connection ধরে আছে যেগুলো database আর চেনে না।
- Pool তাদের একটি connection দিতে যায়। সেটি মৃত, এটি বুঝতে হলে আগে চেষ্টা করে অপেক্ষা করতে হয়, তারপর সেটি বাদ দিতে হয়।
Spring Boot app-এ এই মৃত connection যাচাই করতে গিয়ে প্রথম অনুরোধ প্রায় ১০ সেকেন্ড আটকে থাকত, তারপর “Connection is not available” বলে ব্যর্থ হতো। ভিজিটরের কাছে মনে হতো সাইটটি ঝুলে আছে, তারপর হয়তো একটি error দেখাচ্ছে।
Spring app-এর ক্ষেত্রে সমস্যা কেন সবচেয়ে বেশি ছিল
Spring Boot একটি জনপ্রিয় Java framework, এবং এটি ডিফল্টভাবে HikariCP নামের একটি connection pool ব্যবহার করে। Spring সেটিং থেকে এই pool কনফিগার করতে পারে, যার মধ্যে আছে কতগুলো অলস connection খোলা রাখা হবে।
কিন্তু Mythex প্রতিটি app-কে তার database একটি একক DATABASE_URL হিসেবে দেয়, postgresql://user:pass@host/db আকারে। Spring এই ফরম্যাট সরাসরি পড়তে পারে না। তাই Mythex-এর উপর তৈরি Spring app গুলো এই URL পার্স করে কোডে নিজের pool বানাত। আর হাতে বানানো pool, Spring-এর spring.datasource.* সেটিং উপেক্ষা করে। কেউ সঠিক pool সেটিং জানলেও, সেগুলো কার্যকর হওয়ার কোনো জায়গা ছিল না।
সমাধান, ধাপ ১: Spring যে সেটিং ব্যবহার করতে পারে
কোনো project-এর database থাকলে এখন আমরা তাকে Spring-এর নেটিভভাবে পড়া environment variable-ও দিই:
SPRING_DATASOURCE_URL,SPRING_DATASOURCE_USERNAMEওSPRING_DATASOURCE_PASSWORD, যেগুলোDATABASE_URLথেকে রূপান্তরিত হয় (URL-এ অন্য কিছু না বলা থাকলে SSL আবশ্যক)।- ঘুম থেকে জাগার পরেও টিকে থাকে এমন pool সেটিং:
| সেটিং | মান | কেন |
|---|---|---|
| সর্বনিম্ন idle connection | 0 | অনুরোধের মাঝে কিছুই খোলা না রাখা, তাই মৃত হওয়ার কিছু থাকে না |
| Idle timeout | 30 সেকেন্ড | ব্যবহার না হওয়া connection দ্রুত বন্ধ করা |
| সর্বোচ্চ lifetime | 2 মিনিট | কোনো connection পুরোনো হওয়ার আগেই সেটি অবসর দেওয়া |
| Connection timeout | 5 সেকেন্ড | database ধীরে সাড়া দিলে অনুরোধকে দীর্ঘক্ষণ ঝুলিয়ে না রাখা |
কোনো idle connection না রাখলে জেগে ওঠা app কয়েকটি মৃত connection ঘেঁটে না গিয়ে একটি নতুন connection খোলে।
এই variable গুলো থাকলে Spring নিজেই pool কনফিগার করে এবং সেটিংগুলো কার্যকর হয়। প্রজেক্ট নিজে যা সেট করেছে, তা সবসময় জেতে; আমরা কেবল যা নেই তা পূরণ করি। Spring নয় এমন app এই variable গুলো উপেক্ষা করে।
যে খুঁটিনাটি প্রায় কিছুই করতে দিচ্ছিল না
Spring environment variable-কে তার সেটিংয়ের সঙ্গে মেলানোর নিয়মটি সহজে ভুল হয়। minimum-idle সেটিংটি লিখতে হবে SPRING_DATASOURCE_HIKARI_MINIMUMIDLE হিসেবে, যেখানে dash বাদ যায় এবং শব্দগুলো জোড়া লাগে। SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE লিখলে, যা দেখতে বেশি স্বাভাবিক লাগে, সেটি একটি ভিন্ন, অস্তিত্বহীন সেটিং (minimum.idle)-এ যায় এবং নীরবে উপেক্ষিত হয়।
এই ধরনের ভুল প্রতিটি review পাস করে, অথচ প্রোডাকশনে কিছুই করে না। তাই একটি test দেখে, মানগুলো সেট আছে কি না এবং কোনো variable underscore-বিভাজিত রূপে নেই কি না।
সমাধান, ধাপ ২: বাকি সব stack
Spring-ই একমাত্র framework নয় যার pool আছে, আর আমরা প্রতিটি ভাষার প্রতিটি library আগে থেকে কনফিগার করতে পারি না। তাই Mythex এজেন্ট, অর্থাৎ যে AI আপনার app লেখে, এখন যেকোনো stack-এর জন্য একটি স্থায়ী নির্দেশনা পায়: published app ও তার database অলস থাকলে ঘুমায়, তাই খোলা রাখা একটি connection পরের অনুরোধে মৃত থাকতে পারে। তাকে এগুলো করতে হবে:
- কোনো idle database connection খোলা না রাখা, অথবা ব্যবহারের আগে প্রতিটি connection যাচাই করা,
- connection error হলে একবার retry করা, এবং
- পরিবেশে প্ল্যাটফর্ম যে database সেটিং দেয়, তা ব্যবহার করা,
DATABASE_URLপার্স করে নিজের pool না বানানো।
শেষ বিন্দুটি ধাপ ১-এর সঙ্গে সম্পর্ক গড়ে: প্ল্যাটফর্মের দেওয়া সেটিং ব্যবহার করা app স্বয়ংক্রিয়ভাবে ঘুম-নিরাপদ কনফিগারেশন পায়।
ফলাফল
যে hosting platform-এ আমরা published app চালাই, সেখানে মাপা হয়েছে: ঘুম থেকে জাগার পর প্রথম অনুরোধ ১২.৭ সেকেন্ড থেকে ২.৩ সেকেন্ডে নেমেছে।
এ থেকে আমরা যা নিয়েছি
- প্রতিটি ঘুমন্ত স্তরকে একমত হতে হবে কী টিকে থাকবে। জমে থাকা app ও ঘুমন্ত database প্রত্যেকে একটি যুক্তিসঙ্গত সিদ্ধান্ত নিয়েছিল। একসঙ্গে তার ফল হলো: app জেগে উঠল এমন অবস্থা নিয়ে, যা আর সত্য ছিল না।
- যে ডিফল্টগুলো সবসময়-চালু সার্ভারের জন্য তৈরি, তা শূন্যে নেমে যাওয়া app-এর জন্য মানানসই নয়। Connection গরম রাখা ভালো ধারণা, যতক্ষণ না তার চারপাশের সবকিছু ঠান্ডা হয়ে যায়।
- Framework-কে তার ডেটা তার নিজের ফরম্যাটে দিন। আমাদের জন্য একটি URL সহজ ছিল, কিন্তু প্রতিটি Spring app-কে হাতে pool বানাতে বাধ্য করেছিল, যা তার নিজের কনফিগারেশন উপেক্ষা করত।
- পরীক্ষা করুন, framework যেভাবে পড়ে সেভাবেই সেটিংটি লেখা আছে কি না। ভুল নামে সঠিক মান কিছুই করে না, নীরবে।
Mythex-এ database কীভাবে কাজ করে তা নতুন হলে, database docs এবং আমাদের গাইড Postgres কী দেখুন।