ক্যাশিংকে সাধারণভাবে বর্ণনা করা যায়: কিছু ডেটা সংরক্ষণ করা যাতে পরে দ্রুত এটি রিট্রিভ করা যায়। কখনও কখনও এর অর্থ হল কম্পিউটেড ডেটা সংরক্ষণ করা যাতে এটিকে আবার কম্পিউট করতে না হয়, তবে এটি লোকালি ডেটা সংরক্ষণ করার জন্যও বোঝাতে পারে যাতে আবার ফেচ করতে না হয়। আপনার কম্পিউটার এটি সবসময় করে, কারণ আপনার অপারেটিং সিস্টেম চেষ্টা করে আপনার ফ्रीকোয়েন্টলি অ্যাক্সেস করা ডেটা RAM-এ রাখে যাতে হার্ড ড্রাইভ বা SSD থেকে আবার ফেচ করতে না হয়।
সেভাবে, আপনার ব্রাউজার চেষ্টা করে ইতিমধ্যে ডাউনলোড করা রিসোর্সগুলো রি-ইউজ করতে। আপনি احتمالত এটা নিজে অভিজ্ঞ করেছেন যখন প্রথমবার একটি নতুন ওয়েবসাইট ভিজিট করেছেন। প্রথম লোড বেশি সময় নেয় কারণ আপনার ব্রাউজারকে সবকিছু পুল ডাউন করতে হয়, সব ছবি, জাভাস্ক্রিপ্ট, এবং স্টাইলশীট সহ। একটি মজার ফ্যাক্ট হলো যখন আপনি ফ্রেশলি CNN হোমপেজ ডাউনলোড করেন, আপনার ব্রাউজার ১৯৯৩ সালের মূল Doom গেমের চেয়ে বেশি ডেটা ফেচ করে। কৌতূহলীদের জন্য, এই ব্লগ পোস্ট লেখার সময়, CNN আমার মেশিনে ৩MB এর বেশি ডাউনলোড করে, ~15MB থেকে কমপ্রেস্ড, এবং এড ব্লকার সক্রিয় থাকা সত্ত্বেও, যখন মূল Doom ইনস্টলার ছিল ~2.2MB।
ব্রাউজার এই ডেটা ক্যাশ করার জন্য, সার্ভারের সাথে কিছু কো-অর্ডিনেশন থাকতে হয়। ব্রাউজারকে জানতে হবে সে কী ক্যাশ করতে পারে এবং কতক্ষণ; অন্যথায়, এটি পুরানো কন্টেন্ট দেখাতে পারে যখন সার্ভারে nieu wersie উপলব্ধ। এই আর্টিকেলে, আমরা দেখব এই ক্লায়েন্ট-সার্ভার ক্যাশিং কো-অর্ডিনেশন কীভাবে llevado হয় এবং রেইলস কী প্রদান করে এটিকে পরিবর্তন করার জন্য।
যদিও ফোকাস রুবি অন রেইলস কীভাবে এটি হ্যান্ডেল করে সেটার ওপর, আসলে মেকানিজমটি HTTP স্পেসিফিকেশন-এর অংশ। অর্থাৎ, আমরা যে ক্যাশিং সম্পর্কে কথা বলছি, এটি ইন্টারনেটের ইনফ্রাস্ট্রাকচারে বেক করা हुआ, যা আধুনিক ওয়েবসাইট এবং ফ্রেমওয়ার্ক ডেভেলপমেন্টের একটি কورنারস্টোন। বিভিন্ন ফ্রেমওয়ার্ক, যেমন রেইলস, সিঙ্গেল-পেজ অ্যাপ্লিকেশন (SPAs), এবং এমনকি স্ট্যাটিক সাইটস, সব এই মেকানিজম ব্যবহার করে পারফরম্যান্স উন্নত করতে পারে।
HTTP রিকোয়েস্ট-রেসপন্স
আপনি সম্ভবত রিকোয়েস্ট-রেসপন্স লাইফসাইক্লের সাথে পরিচিত, কমপক্ষে হাই লেভেলে: আপনি একটি ওয়েবসাইটে একটি লিংকে ক্লিক করেন, আপনার ব্রাউজার সার্ভারে সেই কন্টেন্টের জন্য একটি রিকোয়েস্ট পাঠায়, এবং সার্ভার সেই কন্টেন্টটি ফেরত পাঠায় (নোট করুন যে আমি ইচ্ছাকৃতভাবে অনেক জটিলতা এখানে এড়িয়ে যাচ্ছি)।
চলুন এই ব্যাক-অ্যান্ড-ফোর্থ ট্রানজ্যাকশনে যাওয়া আসলে ডেটা 좀 গভীরে দেখি। প্রতিটি HTTP মেসেজের একটি হেডার এবং একটি বডি থাকে (এটিকে HTML <head> এবং <body> ট্যাগের সাথে গুলিয়ে ফেলবেন না)। রিকোয়েস্ট হেডার সার্ভারকে বলে আপনি কোন পাথ অ্যাক্সেস করতে চাচ্ছেন এবং কোন HTTP মেথড ব্যবহার করতে হবে (যেমন GET/PUT/PATCH/POST)। যদি প্রয়োজন হয়, তবে আপনি আপনার ব্রাউজারের ডেভেলপার টুলস বা কমান্ড-লাইন টুল, যেমন curl ব্যবহার করে এই হেডারগুলোতে খুঁজে বের করতে পারেন:
# curl -v honeybadger.io
...
> GET / HTTP/1.1
> Host: honeybadger.io
> User-Agent: curl/7.64.1
> Accept: */*
আউটপুটের এই প্রথম অংশটি হল রিকোয়েস্ট হেডার। আমরা honeybadger.io-এ একটি GET ইস্যু করছি। এরপর সার্ভার যা ফেরত পাঠায় ( "রেসপন্স হেডার" ):
< HTTP/1.1 301 Moved Permanently
< Cache-Control: public, max-age=0, must-revalidate
< Content-Length: 39
< Content-Security-Policy: frame-ancestors 'none'
...
< Content-Type: text/plain
রেসপন্সে HTTP কোড থাকে (যেমন 200 সাকসেসের জন্য বা 404 নট ফাউন্ডের জন্য)। এই উদাহরণে, এটি একটি পারমানেন্ট রিডাইরেক্ট (301) কারণ curl http URL-এ কন্ট্যাক্ট করার চেষ্টা করছে, যা সিকিউর https URL-এ রিডাইরেক্ট করে। রেসপন্স হেডারও কন্টেন্ট টাইপ অন্তর্ভুক্ত করে, যা এখানে text/plain, কিন্তু কিছু অন্যান্য সাধারণ অপশন হলো text/html, text/css, text/javascript, এবং application/json।
রেসপন্স বডি হেডারের পর আসে। আমাদের কেসে, বডি খালি কারণ একটি 301 রিডাইরেক্টের বডি দরকার নেই। যদি আমরা আবার ট্রাই করি curl -v https://www.honeybadger.io দিয়ে, আপনি হোমপেজ কন্টেন্টটি এখানে দেখবেন, ঠিক ব্রাউজারে সোর্স ভিউ করার মত।
আপনি যদি নিজে এটি এক্সপেরিমেন্ট করতে চান, তবে দুটি টিপস:
- কেবল রেসপন্স হেডার দেখতে curl-এ (যেমন নো রিকোয়েস্ট হেডার বা রেসপন্স বডি),
-Iঅপশন ব্যবহার করুন, যেমনcurl -I localhost:3000। - ডিফল্টলি, রেইলস ডেভেলপমেন্ট এনভায়রনমেন্টে ক্যাশ করে না; আপনাকে প্রথমে
rails dev:cacheচালাতে হতে পারে।
Cache-Control HTTP হেডার
ক্যাশিং-এর দিক থেকে আমরা যে প্রধান হেডার সম্পর্কে চিন্তা করি, সেটা হল Cache-Control হেডার। এটি নির্ধারণ করে কোন মেশিনগুলো আমাদের রেইলস সার্ভার থেকে রেসপন্স ক্যাশ করতে পারে এবং কখন সেই ক্যাশড ডেটা এক্সপায়ার হবে। Cache-Control হেডারের মধ্যে, বেশ কিছু ফিল্ড আছে, অধিকাংশ অপশনাল। আমরা এখানে সবচেয়ে রিলেভেন্ট কিছু এন্ট্রি যাব, আরও ইনফরমেশনের জন্য, আপনি অফিশিয়াল HTTP স্পেসিফিকেশন w3.org-এ চেক করতে পারেন।
এখানে একটি বেসিক আউট-অফ-দ-বক্স রেইলস রেসপন্স হেডার থেকে নমুনা:
< Content-Type: text/html; charset=utf-8
< Etag: W/"b41ce6c6d4bde17fd61a09e36b1e52ad"
< Cache-Control: max-age=0, private, must-revalidate
max-age
max-age ফিল্ডটি একটি ইন্টিজার যা রেসপন্সটি কত সেকেন্ড বৈধ থাকবে তা ধারণ করে। ডিফল্টলি, একটি ভিউর জন্য রেইলস রেসপন্সে এটি ০ সেট থাকে (অর্থাৎ, রেসপন্স তাৎক্ষণিক এক্সপায়ার হয়, এবং ব্রাউজার সবসময় একটি নতুন ভার্সন পেতে হবে)।
public/private
হেডারে public বা private ইনক্লুড করা সেট করে কোন সার্ভারগুলো রেসপন্স ক্যাশ করার অনুমতি পাবে। যদি হেডারে private থাকে, তবে এটি শুধু রিকোয়েস্টিং ক্লায়েন্ট (যেমন ব্রাউজার) দ্বারা ক্যাশ করা হবে, অন্য কোনো সার্ভার দ্বারা না যা সেখানে পৌঁছানোর পথে পড়েছে, যেমন কন্টেন্ট ডেলিভারি নেটওয়ার্ক (CDN) বা প্রক্সি। যদি হেডারে public থাকে, তবে এই ইন্টারমিডিয়ারি সার্ভারগুলোর ক্যাশ করার অনুমতি থাকে। রেইলস ডিফল্টলি প্রতিটি হেডারকে private সেট করে।
must-revalidate
রেইলস ডিফল্টলি must-revalidate ফিল্ডও সেট করে। এর মানে হলো ক্লায়েন্টকে সার্ভারের সাথে যোগাযোগ করতে হবে কনফার্ম করতে যে এর ক্যাশড ভার্সন এখনও বৈধ কিনা তা ব্যবহার করার আগে। ক্যাশড ভার্সনটি বৈধ কিনা তা নির্ধারণ করতে, ক্লায়েন্ট এবং সার্ভার ETags ব্যবহার করে।
ETags
ETags হলো একটি অপশনাল HTTP হেডার যা সার্ভার যোগ করে যখন এটি ক্লায়েন্টকে রেসপন্স পাঠায়। সাধারণত, এটি রেসপন্সটার ওপর একটি চেকসাম। যখন ক্লায়েন্ট (আপনার ব্রাউজার) এই রিসোর্সটি আবার রিকোয়েস্ট করে, তবে এটি পূর্বের রেসপন্স থেকে পাওয়া Etag-টিকে (যদি এর একটি পূর্বের রেসপন্স ক্যাশ থাকে) If-None-Match HTTP হেডারে ইনক্লুড করে। সার্ভার তারপর 304 HTTP কোড ("Not Modified") এবং একটি খালি বডি দিয়ে রেসপন্ড করতে পারে। এর মানে হলো সার্ভারে ভার্সনটি পরিবর্তন হয়নি, তাই ক্লায়েন্ট তার ক্যাশড ভার্সন ব্যবহার করা উচিত।
ETags দুটি প্রকারের: স্ট্রং এবং উইক (একটি উইক ট্যাগ W/ প্রিফিক্স দিয়ে চিহ্নিত)। তারা একইভাবে বিহেভ করে, কিন্তু একটি স্ট্রং ETag মানে সার্ভারে রিসোর্সের দুটি কপি (সার্ভারে ভার্সন এবং লোকাল ক্যাশে वाला) ১০০% বাইট-ফর-বাইট আইডেন্টিক্যাল। উইক ETags, যাইহোক, ইন্ডিকেট করে যে দুটি কপি বাইট-ফর-বাইট আইডেন্টিক্যাল নাও হতে পারে, কিন্তু ক্যাশড ভার্সনটি ব্যবহার করা যেতে পারে। এর একটি সাধারণ উদাহরণ হলো রেইলসের csrf_meta_tags হেল্পার, যা একটি টোকেন তৈরি করে যা ধারাবাহিক পরিবর্তন হয়; তাই, আপনার অ্যাপ্লিকেশনে একটি স্ট্যাটিক পেজ থাকলেও, এটি বাইট-ফর-বাইট আইডেন্টিক্যাল হবে না যখন রিফ্রেশ করা হবে Cross-Site-Request-Forgery (CSRF) টোকেনের কারণে। রেইলস ডিফল্টলি উইক ETags ব্যবহার করে।
রেইলসে ETags
রেইলস ভিউজের ওপর ETags অটোমেটিক্যালি হ্যান্ডেল করে। এটি আউটগোইং হেডারে ETag ইনক্লুড করে এবং ইনকামিং ETag হেডার চেক করার জন্য মিডলওয়্যার আছে এবং উপযুক্ত হলে 304 (Not Modified) কোড রিটার্ন করে। уديале, যেহেতু রেইলস ডাইনামিক্যালি ভিউ জেনারেট করে, এটি এখনও সকল রেন্ডারিং কাজ করতে হবে ETag বের করার আগে। এর মানে হলো এমনকি ETags ম্যাচ করলেও, আপনি শুধু নেটওয়ার্ক জুড়ে ডেটা পাঠানোর সময় এবং ব্যান্ডউইডথ বাঁচাচ্ছেন, ভিউ ক্যাশিং-এর মত নয়, যেখানে আপনি রেন্ডারিং ধাপটি সম্পূর্ণরূপে স্কিপ করতে পারেন যদি একটি ক্যাশড ভার্সন থাকে। যাইহোক, রেইলস জেনারেটেড ETag টিউন করার জন্য কিছু উপায় প্রদান করে।
stale?
এverw-ver-changing CSRF টোকেনকে ETag পরিবর্তন করা থেকে বাঁচানোর একটি উপায় হলো stale? হেল্পার ActionController-এ। এটি আপনাকে ETag (স্ট্রং বা উইক) সরাসরি সেট করার অনুমতি দেয়। যাইহোক, আপনি শুধু একটি অবজেক্ট পাস করতে পারেন, যেমন একটি ActiveRecord মডেল, এবং এটি ETag কম্পিউট করবে অবজেক্টের updated_at টাইমস্ট্যাম্পের উপর ভিত্তি করে বা maksimum updated_at ব্যবহার করবে যদি আপনি একটি কলেকশন পাস করেন:
class UsersController < ApplicationController
def index
@users = User.includes(:posts).all
render :index if stale?(@users)
end
end
curl দিয়ে পেজ হিট করলে, আমরা ফলাফল দেখতে পাই:
# curl -I localhost:3000 -- প্রথম পেজ লোড
ETag: W/"af9ae8f2d66b9b6c4d0513f185638f1a"
# curl -I localhost:3000 -- রিলোড (CSRF টোকেনের কারণে পরিবর্তন)
ETag: W/"f06158417f290334f47ea2124e08d89d"
-- কন্ট্রোলার কোডে stale? যোগ করুন
# curl -I localhost:3000 -- রিলোড
ETag: W/"04b9b99835c359f36551720d8e3ca6fe" -- এখন `@users` ব্যবহার করে ETag জেনারেট
# curl -I localhost:3000 -- রিলোড
ETag: W/"04b9b99835c359f36551720d8e3ca6fe" -- কোনো পরিবর্তন নেই
এটি আমাদের আরও কন্ট্রোল দেয় ক্লায়েন্ট কখন ফুল পেইলোড আবার ডাউনলোড করবে, কিন্তু এখনও এটি সার্ভারের সাথে চেক করতে হচ্ছে প্রতিবার এর ক্যাশ এখনও বৈধ কিনা তা নির্ধারণ করতে। কী হবে যদি আমরা সেই চেকটিই সম্পূর্ণরূপে স্কিপ করতে চাই? এখানেই max-age ফিল্ড হেডারে কাজে আসে।
expires_in এবং http_cache_forever
রেইলস আমাদের ActionController-এ কিছু হেল্পার মেথড দেয় max-age ফিল্ড অ্যাডজাস্ট করার জন্য: expires_in এবং http_cache_forever। তারা দুটোই তাদের নাম অনুযায়ী কাজ করে:
class UsersController < ApplicationController
def index
@users = User.includes(:posts).all
expires_in 10.minutes
end
end
# curl -I localhost:3000
Cache-Control: max-age=600, private
রেইলস max-age-কে ৬০০ (১০ মিনিট সেকেন্ডে) সেট করেছে এবং must-revalidate ফিল্ডটি রিমুভ করেছে। আপনি private ফিল্ডও পরিবর্তন করতে পারেন public: true নামযুক্ত আর্গুমেন্ট পাস করে।
http_cache_forever মূলত expires_in-এর একটি র্যাপার যা max-age-কে ১০০ বছর সেট করে এবং একটি ব্লক নেয়:
class UsersController < ApplicationController
def index
@users = User.includes(:posts).all
http_cache_forever(public: true) do
render :index
end
end
end
# curl -I localhost:3000
Cache-Control: max-age=3155695200, public
এই ধরণের এক্সট্রিম-লং-টার্ম-ক্যাশিং হলো কেন রেইলস অ্যাসেটের "ফিংগারপ্রিন্ট" যুক্ত থাকে, যা ফাইলের কন্টেন্টের একটি হ্যাশ এবং ফাইলনেম তৈরি করে, যেমন packs/js/application-4028feaf5babc1c1617b.js। শেষে "ফিংগারপ্রিন্ট" কার্যকরভাবে ফাইলের কন্টেন্টকে ফাইলের নামের সাথে লিঙ্ক করে। যদি কন্টেন্ট কখনো পরিবর্তন হয়, ফাইলনেমটি পরিবর্তন হবে। এর মানে ব্রাউজার এই ফাইলটি সদা নিরাপদে ক্যাশ করতে পারে কারণ যদি এটি কখনো পরিবর্তন হয়, এমনকি ছোটভাবে, ফিংগারপ্রিন্ট পরিবর্তন হবে; برাউজারের দৃষ্টিতে, এটি একটি সম্পূর্ণ আলাদা ফাইল যা ডাউনলোড করা দরকার।
প্রভাবের সফিয়ার
এখন কিছু ক্যাশিং অপশন কভার করার পর, আমার পরামর্শ কিছুটা অদ্ভুত লাগতে পারে, কিন্তু আমি সাজেস্ট করব এই আর্টিকেলের যেকোনো মেথড ব্যবহার না করার চেষ্টা করুন! ETags এবং HTTP ক্যাশিং জানা ভালো, এবং রেইলস আমাদের কিছু নির্দিষ্ট টুল দেয় নির্দিষ্ট সমস্যা সমাধানের জন্য। কিন্তু চेतাবনীটি, এবং এটি বড়, হলো এই সব ক্যাশিং আপনার অ্যাপ্লিকেশনের বাইরে ঘটে এবং তাই বড় মাত্রায় আপনার কন্ট্রোলের বাইরে। যদি আপনি ভিউ ক্যাশিং বা লো-লেভেল ক্যাশিং রেইলসে ব্যবহার করেন, যেমন এই সিরিজের আগের অংশগুলে কভার করা হয়েছে, এবং ইনভ্যালিডেশন ইস্যু সম্মুখীন হন, আপনার অপশন আছে; আপনি মডেল touch করতে পারেন, আপডেটেড কোড পুশ করতে পারেন, বা এমনকি কনসোল থেকে Rails.cache-এ সরাসরি রিচ করতে পারেন যদি প্রয়োজন হয়, কিন্তু HTTP ক্যাশিং-এ নয়। ব্যক্তিগতভাবে, আমি প্রোডাকশনে Rails.cache.clear চালাতে ভালোবাসি, বরং একটি সমস্যা সম্মুখীন হতে যেখানে সাইট ইউজারদের জন্য ভাঙা থাকে যতক্ষণ তারা তাদের ব্রাউজার ক্যাশ ক্লিয়ার করে না (আপনার কাস্টমার সার্ভিস টিমও আপনাকে পছন্দ করবে)।
উপসংহার
এটি রেইলসে ক্যাশিং-র সিরিজের শেষ; আশা করি এটি উপকারী এবং ইনফরমেটিভ ছিল। আমার ক্যাশিং-র জন্য পরামর্শ এইভাবে থাকে: যত কম পারো उतনো কম কর, কিন্তু যত করতেই হয় তত কর। আপনি পারফরম্যান্স সমস্যা অনুভব করলে, শুরু করুন সেই মেথডগুলো খুঁজে বের করে যেগুলো často হিট হয়; হয়তো সেগুলো মেমোআইজ করা যায়। ভ্যালুটি রিকোয়েস্ট জুড়ে পাবলিশ করতে হবে? হয়তো সেই hevily-used মেথডটি কিছু লো-লেভেল ক্যাশিং ব্যবহার করতে পারে। অথবা, হয়তো, এটি কোনো নির্দিষ্ট মেথড নয়; এটি শুধু সেই নেস্টেড পারশিয়ালগুলো কران্চ করে যেগুলো স্লো করছে; এই কেসে, হয়তো ভিউ লেভেল ক্যাশিং সাহায্য করতে পারে। রেইলস আমাদের "শার্প ক্নাইভ" দেয় এই প্রতিটি ইস্যু টার্গেট করার জন্য, এবং আমাদের শুধু জানতে হবে কখন কোনটি ব্যবহার করব।