স্থানীয় Redis-এর বিপরীতে 200 ms-এ উত্তর দেওয়া একটি পৃষ্ঠা ক্লায়েন্ট VPN-এর পেছনে বসার সঙ্গে সঙ্গে চার সেকেন্ড নেয়, অথচ সার্ভারের কাউন্টার নিষ্ক্রিয় থাকে। ধীর লগ এক মিলিসেকেন্ডের বেশি কিছু দেখায় না, CPU গ্রাফ সমতল, এবং ইনস্ট্যান্সটি অভিযোগ ছাড়াই অন্য ক্লায়েন্টদের সেবা দেয়। যা বদলেছে তা হলো দূরত্ব, আর দূরত্বকে গুণ করে আপনার কোড যতবার থেমে অপেক্ষা করে। ক্লায়েন্ট থেকে বাইরের দিকে অনুরোধের পথ ধরে হাঁটুন, দেখবেন সমাধান প্রায় সবসময়ই দ্রুততর সার্ভার নয়, বরং কম ধারাবাহিক অপেক্ষা।

সার্ভারের মেট্রিক নয়, ক্লায়েন্ট থেকে শুরু করুন

প্রথম কমান্ড থেকে শেষ কমান্ড পর্যন্ত ক্লায়েন্ট প্রক্রিয়া লক্ষ্য করুন। যদি ট্রেসে একটি উত্তর, একটি বিরতি, তারপর পরের কমান্ড দেখায়, তাহলে আপনি প্রতি কমান্ডে পূর্ণ একটি রাউন্ড ট্রিপ দিচ্ছেন এবং সার্ভার টিউনিংয়ের কোনো পরিমাণই তা বদলাবে না। ধীর লগ এখানে ভুল যন্ত্র, কারণ এটি নির্বাহের সময় রেকর্ড করে এবং যোগাযোগ I/O ইচ্ছাকৃতভাবে বাদ দেয়। ধীর এন্ডপয়েন্টের পাশে পরিষ্কার ধীর লগই রাউন্ড-ট্রিপ সমস্যার স্বাক্ষর।

কোড স্পর্শ করার আগে পথটি নিজেই মাপুন। redis-cli -h redis.example.internal --latency বারবার PING পাঠায় এবং গড় অনুরোধ ও উত্তর সময় ছাপে, যা আপনার প্রতি-বিনিময় খরচ। এটিকে এক ব্যবহারকারী ক্রিয়ার ধারাবাহিক কমান্ডের সংখ্যা দিয়ে গুণ করুন, এন্ডপয়েন্টের জন্য একটি নিম্নসীমা পাবেন।

একটি ব্যবহারকারী ক্রিয়ার আসলে কত রাউন্ড ট্রিপ লাগে তা গুনুন

একটি এন্ডপয়েন্ট নিন এবং প্রতিটি Redis কল ক্রমে লিখুন, যেগুলো ORM, সেশন মিডলওয়্যার বা ফিচার-ফ্ল্যাগ লুকআপের ভেতরে লুকানো সেগুলোসহ। বেশিরভাগ ধীর এন্ডপয়েন্ট দেখা যায় যেখানে তিনটি কলই যথেষ্ট সেখানে বিশ বা ত্রিশটি কল করে, আর লুকানোগুলোই সাধারণত সংখ্যাগরিষ্ঠ।

নির্ভরশীলতার শৃঙ্খল ভাঁজ করা যায় না: যদি দ্বিতীয় কমান্ডের প্রথমটির ফেরত দেওয়া কী দরকার হয়, তবে অপেক্ষাটি সত্যি এবং একমাত্র বিকল্প হলো ভিন্ন ডেটা মডেল বা একটি সার্ভার-সাইড অপারেশন যা পুরো ধাপটি একসঙ্গে প্রকাশ করে। স্বাধীন পাঠ ভিন্ন বিষয়, কারণ দ্বিতীয়টি পাঠানোর আগে প্রথম উত্তরের জন্য অপেক্ষা করতে আপনাকে কিছুই বাধ্য করে না। কিছু সম্পাদনার আগে প্রতিটি কলকে নির্ভরশীল বা স্বাধীন হিসেবে চিহ্নিত করুন, সমাধানের আকৃতি স্পষ্ট হয়ে যাবে।

গুরুত্বপূর্ণ সংখ্যা হলো ধারাবাহিক অপেক্ষা, মোট কমান্ড নয়। একটি ব্যাচে ত্রিশটি কমান্ডও এক অপেক্ষায় চলে।

শুধু সেই কমান্ডগুলো ব্যাচ করুন যেগুলো একে অপরের ওপর নির্ভর করে না

একটি পাইপলাইন স্বাধীন কমান্ড সারিবদ্ধ করে এবং উত্তরগুলো ক্রমে সংগ্রহ করে, N অপেক্ষাকে একটিতে পরিণত করে। বেশিরভাগ ক্লায়েন্ট এটি আলাদা সংযোগ মোড নয়, বরং একটি ব্লক, কলব্যাক বা কনটেক্সট ম্যানেজার হিসেবে দেয়, তাই আপনার ইনস্টল করা সংস্করণ যে API দেয় তা দেখে নিন। লাভ রাউন্ড-ট্রিপ সময়ের সঙ্গে বাড়ে, ঠিক এ কারণেই VPN-এ এটি গুরুত্বপূর্ণ এবং localhost-এ প্রায় টের পাওয়া যায় না।

import redis

r = redis.Redis(host="redis.example.internal", port=6379, socket_timeout=2)
pipe = r.pipeline(transaction=False)
for key in ("user:1001", "user:1002", "user:1003"):
    pipe.get(key)
print(pipe.execute())

শুধু ব্যাচিং চাইলে transaction=False দিন। ডিফল্টে ক্লায়েন্ট ব্যাচটিকে MULTI ও EXEC-এ মুড়ে দেয়, যা এটিকে পারমাণবিক করে এবং অন্য ক্লায়েন্টরা মাঝপথে কী দেখতে পাবে তা বদলে দেয়। পারমাণবিকতা কর্মক্ষমতার নব নয়, সঠিকতার সিদ্ধান্ত, আর দুটোই রাউন্ড ট্রিপ কমায় বলে পাইপলাইন লেনদেন হয়ে যায় না।

শুধু ব্যাচ জমা নয়, প্রতিটি উত্তর পড়ুন। সারিবদ্ধ কমান্ড একটি ত্রুটি ফেরত দিতে পারে যদিও ব্যাচটি সামগ্রিকভাবে সম্পন্ন হয়, এবং যে ক্লায়েন্ট উত্তর তালিকা ফেলে দেয় সে ব্যর্থ লেখাকে নীরব লেখায় পরিণত করে। উত্তর তালিকার দৈর্ঘ্য আপনার সারিবদ্ধ করা কমান্ডের সংখ্যার সঙ্গে তুলনা করুন।

ক্রিয়াগুলোর মধ্যে সংযোগ সচল রাখুন

প্রতিটি নতুন সংযোগ নাম রেজলিউশন, TCP হ্যান্ডশেক, TLS হ্যান্ডশেক এবং AUTH-এর জন্য খরচ দেয়, আর VPN-এর ওপর এর প্রত্যেকটি এমন রাউন্ড ট্রিপ যা আপনি ব্যাচ করে সরাতে পারবেন না। প্রতি প্রক্রিয়ায় একবার ক্লায়েন্ট তৈরি করুন এবং পুনরায় ব্যবহার করুন, বা আপনার লাইব্রেরি দেওয়া সীমিত আকারের পুল ব্যবহার করুন। প্রতি অনুরোধে পুনঃসংযোগ এই লক্ষণের দ্বিতীয় সবচেয়ে সাধারণ কারণ, প্রতি-কমান্ড অপেক্ষার পরে।

কনফিগারেশনে আক্ষরিক ঠিকানা নয়, ব্যক্তিগত হোস্টনাম রাখুন। ব্যক্তিগত নেটওয়ার্কেও সার্টিফিকেট যাচাই ও পরিষেবা পরিচয় প্রযোজ্য, আর একটি IP পিন করা সেই মুহূর্তেই ভেঙে যায় যখন প্রদানকারী একটি সাবনেট পুনঃসংখ্যায়িত করে। যদি রেজলিউশনই ধীর অংশ হয়, সংযোগ স্ট্রিং নয়, রেজলভার পথ ঠিক করুন।

স্পষ্ট সকেট টাইমআউট দিন যাতে আটকে থাকা টানেল একটি ওয়ার্কার আটকে রাখার বদলে দ্রুত ব্যর্থ হয়। তারপর ক্লায়েন্টে সংযোগ তৈরির লগ দিয়ে নিশ্চিত করুন পুলটি সত্যিই পুনরায় ব্যবহৃত হচ্ছে।

মেমরি সমস্যা হওয়ার আগেই ব্যাচের আকার নির্ধারণ করুন

Redis ব্যাচের পুরো সময়ের জন্য সারিবদ্ধ উত্তর ধরে রাখে, আর ক্লায়েন্ট পরে পার্স করা ফলাফল ধরে রাখে। দশ হাজার বড় মানের একটি পাইপলাইন নেটওয়ার্ক অপেক্ষার বদলে মেমরি স্পাইক এবং দুই প্রান্তে দীর্ঘ বিরতি নেয়।

কমান্ড সংখ্যা নয়, উত্তর আয়তনের ভিত্তিতে ব্যাচের আকার বাছুন। একটি ব্যাচে কয়েকশো ছোট কমান্ড সাধারণত আরামদায়ক, কিন্তু একশো মাল্টি-কিলোবাইট পেলোড নয়। বাইট এবং সংখ্যা দুটো দিয়েই ভাগ করুন, এবং প্রতিটি ব্যাচ এন্ডপয়েন্টের প্রকৃত লেটেন্সি বাজেটের মধ্যে রাখুন।

পরিবর্তনের পর শুরু থেকে শেষ পর্যন্ত মাপুন: redis-cli -h redis.example.internal --latency-history দেখায় পথটি নিজেই স্থিতিশীল কি না, আর আপনার নিজের অনুরোধ টাইমার দেখায় এন্ডপয়েন্ট উন্নত হয়েছে কি না। যদি লেটেন্সি ইতিহাস সমতল থাকা সত্ত্বেও এন্ডপয়েন্ট ধীর থাকে, বাকি খরচ সার্ভার-সাইড কাজ বা আপনার নিজের সিরিয়ালাইজেশন, আর পরের ধাপ আরেকটি ব্যাচিং পরিবর্তন নয়, হ্যান্ডলারের প্রোফাইল।

সম্পর্কিত নিবন্ধ