ইনক্রিমেন্টাল স্ট্যাটিক রিজেনারেশন ও হাইব্রিড রেন্ডারিং
এই পাঠে যা শিখবেন
- ISR কীভাবে SSG-এর "একবারই render(), চিরকাল ক্যাশ" সমস্যার সমাধান করে
- Python দিয়ে একটি real সময়-ভিত্তিক স্টেলনেস-চেক লেখা ও তার প্রতিটি সিদ্ধান্ত হাতে-কলমে ট্রেস করা
- ১০টি রিকোয়েস্টে ঠিক কোনগুলো re-render ট্রিগার করে ও কোনগুলো ক্যাশ থেকে সার্ভ হয় তা নির্ভুলভাবে যাচাই করা
- "হাইব্রিড রেন্ডারিং" ধারণা -- একই সাইটে বিভিন্ন পেজে বিভিন্ন স্ট্র্যাটেজি প্রযোজ্য হতে পারা
১ · SSG-এর ক্যাশে একটি স্টেলনেস-চেক যোগ করা
ISRIncremental Static RegenerationSSG-এর ক্যাশে একটি সময়-ভিত্তিক স্টেলনেস-চেক যোগ করা একটি স্ট্র্যাটেজি -- ক্যাশ একটি নির্দিষ্ট সময়ের বেশি পুরনো হলে পরবর্তী রিকোয়েস্টে স্বয়ংক্রিয়ভাবে আবার রেন্ডার হয়।
L40-এ দেখা গিয়েছিল SSG-তে render() একবারই কল হয়, ফলে কনটেন্ট বদলালেও পরবর্তী বিল্ড না হওয়া পর্যন্ত
পুরনো ডেটা সার্ভ হতেই থাকে। ISR এই সমস্যা সমাধান করে একটি সাধারণ নিয়ম দিয়ে: প্রতিটি রিকোয়েস্টে চেক করা হয়
ক্যাশ কতটা পুরনো -- যদি একটি নির্ধারিত REVALIDATE_INTERVAL-এর চেয়ে বেশি পুরনো হয়, তাহলে
সেই রিকোয়েস্টেই render() আবার কল হয়ে ক্যাশ নতুন করে তৈরি হয়; নাহলে বিদ্যমান ক্যাশই সরাসরি
সার্ভ হয়।
কতটা সময় পার হলে ক্যাশ "stale" ধরা হবে -- একটি সংখ্যা (সেকেন্ড বা যেকোনো সিমুলেটেড সময়-একক)।
শেষবার
render() কবে কল হয়েছিল তার টাইমস্ট্যাম্প -- প্রতিটি re-render-এর পর আপডেট হয়।২ · ISR সিমুলেশন -- ঠিক কোন রিকোয়েস্টে render() হলো
নিচের কোড সেলে REVALIDATE_INTERVAL = 10 নির্ধারণ করা হয়েছে। ১০টি সিমুলেটেড রিকোয়েস্ট আসছে
বাড়তে-থাকা টাইমস্ট্যাম্পে: t = 0, 2, 5, 8, 11, 14, 19, 23, 28, 33। প্রতিটি রিকোয়েস্টে
current_time - last_built_at >= REVALIDATE_INTERVAL শর্তটি চেক করা হয় -- সত্য হলে
render() আবার কল হয় ও last_built_at আপডেট হয়, নাহলে ক্যাশ সরাসরি সার্ভ হয়।
render_call_log = [] # (রিকোয়েস্ট নম্বর, সময়, "RE-RENDER"/"CACHE")
def render(template, data):
"""L38-L40-এর একই ধারণা -- str.format() ভিত্তিক টেমপ্লেট-রেন্ডার।"""
return template.format(**data)
def load_content_from_cms(version):
# প্রতিটি নতুন "ভার্সন" নতুন কনটেন্ট বহন করে -- বাস্তবে CMS আপডেট হলে যা হতো
return {"title": f"পণ্য পাতা (ডেটা ভার্সন v{version})", "stock": 40 - version * 3}
template = "<div class='product-page'><h1>{title}</h1><p>স্টকে আছে: {stock}</p></div>"
REVALIDATE_INTERVAL = 10 # এই সময়-একক পার হলে ক্যাশ "stale" ধরা হবে
cache = {"html": None, "last_built_at": None, "version": 0}
def handle_isr_request(request_number, current_time):
is_stale = (
cache["html"] is None
or (current_time - cache["last_built_at"]) >= REVALIDATE_INTERVAL
)
if is_stale:
cache["version"] += 1
data = load_content_from_cms(cache["version"])
cache["html"] = render(template, data)
cache["last_built_at"] = current_time
render_call_log.append((request_number, current_time, "RE-RENDER"))
action = "RE-RENDER (ক্যাশ stale বা প্রথমবার)"
else:
render_call_log.append((request_number, current_time, "CACHE"))
action = "CACHE (এখনো fresh)"
print(f"রিকোয়েস্ট #{request_number} (t={current_time}) -> {action}")
print(f" HTML: {cache['html']}")
return cache["html"]
request_timestamps = [0, 2, 5, 8, 11, 14, 19, 23, 28, 33]
for i, t in enumerate(request_timestamps, start=1):
handle_isr_request(i, t)
re_renders = [entry for entry in render_call_log if entry[2] == "RE-RENDER"]
served_from_cache = [entry for entry in render_call_log if entry[2] == "CACHE"]
print(f"\nমোট রিকোয়েস্ট: {len(request_timestamps)}")
print(f"render() ইনভোকেশন (RE-RENDER): {len(re_renders)} -- রিকোয়েস্ট নম্বর {[e[0] for e in re_renders]}")
print(f"ক্যাশ থেকে সরাসরি সার্ভ: {len(served_from_cache)} -- রিকোয়েস্ট নম্বর {[e[0] for e in served_from_cache]}")
last_built_at=0। রিকোয়েস্ট
#২,#৩,#৪ (t=2,5,8) প্রতিটির ব্যবধান (২, ৫, ৮) এখনো ১০-এর কম, তাই CACHE। রিকোয়েস্ট #৫ (t=11) ব্যবধান
১১−০=১১ ≥ ১০, তাই RE-RENDER, last_built_at=11। #৬,#৭ (t=14,19) ব্যবধান (৩, ৮) ১০-এর কম, CACHE।
#৮ (t=23) ব্যবধান ২৩−১১=১২ ≥ ১০, RE-RENDER, last_built_at=23। #৯ (t=28) ব্যবধান ৫, CACHE। #১০
(t=33) ব্যবধান ৩৩−২৩=১০ ≥ ১০, RE-RENDER। মোট ৪টি RE-RENDER (#১, #৫, #৮, #১০), ৬টি CACHE (#২, #৩, #৪, #৬, #৭, #৯)
-- ঠিক ১০টি রিকোয়েস্টের সমষ্টি।
৩ · হাইব্রিড রেন্ডারিং -- একই সাইটে একাধিক স্ট্র্যাটেজি
আধুনিক ফ্রেমওয়ার্কগুলোতে (যেমন Next.js) একটি সাইটের ভিন্ন ভিন্ন পেজে ভিন্ন ভিন্ন রেন্ডারিং স্ট্র্যাটেজি বেছে নেওয়া যায় -- একে হাইব্রিড রেন্ডারিং বলা হয়। যেমন একটি ই-কমার্স সাইটের হোমপেজ SSG (কমই পাল্টায়), পণ্যের পাতা ISR (মাঝে মাঝে স্টক/মূল্য পাল্টায়, রিভ্যালিডেশন ব্যবধান সহনীয়), এবং ব্যবহারকারীর নিজের ড্যাশবোর্ড SSR বা CSR (সবসময় সর্বশেষ, ব্যক্তিগত ডেটা দরকার) দিয়ে সার্ভ হতে পারে। M9-এর তিনটি আগের পাঠ (CSR, SSR, SSG) আসলে একে অপরের বিপরীত নয় -- একই টুলবক্সের ভিন্ন হাতিয়ার, প্রতিটি পেজের প্রয়োজন অনুযায়ী বেছে নেওয়া হয়।
ISR SSG-এর ক্যাশে একটি সময়-ভিত্তিক শর্ত যোগ করে -- render() শুধু তখনই আবার কল হয় যখন
current_time - last_built_at >= REVALIDATE_INTERVAL সত্য হয়, বাকি সব রিকোয়েস্ট ক্যাশ থেকে
সার্ভ হয়। উপরের সিমুলেশনে ১০টি রিকোয়েস্টের মধ্যে render() কল হয়েছে ঠিক ৪ বার, নির্দিষ্ট, পূর্বানুমেয় সময়ে --
SSR-এর মতো প্রতিবার নয়, SSG-এর মতো একবারই-চিরকাল নয়, দুইয়ের মাঝামাঝি একটি নিয়ন্ত্রিত সমঝোতা।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন -- তারপর "→ উত্তর" চাপুন।
প্র ০১ রিকোয়েস্ট #৬ (t=14) কেন RE-RENDER না করে CACHE থেকে সার্ভ হলো, যেখানে রিকোয়েস্ট #৫ (t=11) মাত্র RE-RENDER করেছিল?
কারণ #৫-এ RE-RENDER হওয়ার পর last_built_at ১১-তে আপডেট হয়ে গিয়েছিল। #৬-এ ব্যবধান হিসাব হয়
বর্তমান সময় (১৪) বনাম সেই নতুন last_built_at (১১) -- অর্থাৎ ১৪−১১=৩, যা
REVALIDATE_INTERVAL (১০)-এর অনেক কম। তাই ক্যাশ তখনো "fresh" ধরা হয়, CACHE থেকে সার্ভ হয়।
প্র ০২
রিকোয়েস্ট #১০ (t=33)-এ ব্যবধান ঠিক ১০ (৩৩−২৩), REVALIDATE_INTERVAL-এর সমান -- এটি কেন
RE-RENDER হিসেবে গণনা হলো, CACHE নয়?
কারণ শর্তটি লেখা হয়েছে >= (গ্রেটার-দ্যান-অর-ইকুয়াল) দিয়ে, কঠোর > দিয়ে নয় --
তাই ব্যবধান REVALIDATE_INTERVAL-এর সমান হলেও তা "stale" ধরা হয়। এই ছোট্ট বিস্তারিত
বিষয়টি (>= বনাম >) বাস্তব সিস্টেমেও গুরুত্বপূর্ণ -- সীমানার ঠিক উপরে থাকা
রিকোয়েস্ট কী আচরণ পাবে তা নির্ধারণ করে।
প্র ০৩
যদি REVALIDATE_INTERVAL-কে ১০ থেকে ৫-এ কমানো হয়, তাহলে একই ১০টি রিকোয়েস্টে
RE-RENDER সংখ্যা কি বাড়বে, না কমবে -- কেন?
বাড়বে -- কারণ ছোট REVALIDATE_INTERVAL মানে ক্যাশ দ্রুত "stale" হয়ে যায়, তাই কম সময়ের মধ্যেই
পরবর্তী রিকোয়েস্ট আবার RE-RENDER ট্রিগার করবে। এটি একটি সরাসরি trade-off: ছোট ব্যবধান মানে বেশি সতেজ ডেটা
কিন্তু বেশি render() কল (বেশি সার্ভার খরচ); বড় ব্যবধান মানে কম খরচ কিন্তু বেশি সময় স্টেল থাকার ঝুঁকি।
অনুশীলন
-
চিন্তা করুন: একটি খবরের সাইটের হোমপেজের জন্য
REVALIDATE_INTERVALকত রাখা যুক্তিসঙ্গত মনে হয় -- কয়েক সেকেন্ড, কয়েক মিনিট, নাকি কয়েক ঘণ্টা? আপনার উত্তরের পেছনের কারণ কী?সাধারণত কয়েক মিনিট যুক্তিসঙ্গত -- খবরের সাইট নিয়মিত আপডেট হয়, তাই খুব বড় ব্যবধান (কয়েক ঘণ্টা) পুরনো হেডলাইন দেখাতে পারে। আবার প্রতি কয়েক সেকেন্ডে re-render করা সার্ভারে অপ্রয়োজনীয় চাপ ফেলবে, যেখানে হোমপেজ কন্টেন্ট সেকেন্ডে সেকেন্ডে পাল্টায় না। কয়েক মিনিটের ব্যবধান সতেজতা ও সার্ভার-খরচের মধ্যে একটি বাস্তবসম্মত সমঝোতা দেয়।
-
পরীক্ষা করুন: উপরের কোড সেলে
request_timestamps-এ আরেকটি রিকোয়েস্ট40যোগ করুন (তালিকার শেষে), Run চাপুন, এবং হাতে-কলমে হিসাব করে যাচাই করুন এটি RE-RENDER নাকি CACHE হবে -- তারপর আউটপুটের সাথে মিলিয়ে দেখুন।রিকোয়েস্ট #১০ (t=33)-এ সর্বশেষ
last_built_atহয়েছিল ৩৩। নতুন রিকোয়েস্ট #১১ (t=40)-এ ব্যবধান ৪০−৩৩=৭, যাREVALIDATE_INTERVAL(১০)-এর কম -- তাই এটি CACHE হবে, RE-RENDER নয়। কোড চালিয়েrender_call_log-এর শেষ এন্ট্রি দেখলে এটি নিশ্চিত হবে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ L42 হাইড্রেশন -- সার্ভার-রেন্ডার করা HTML কীভাবে ক্লায়েন্টে ইন্টারেক্টিভ হয়ে ওঠে দেখুন।
- আগের পাঠ আবার দেখুন L40 SSG-এর মূল ক্যাশ প্যাটার্ন আবার দেখে এই পাঠের রিভ্যালিডেশন-চেকের সাথে তুলনা করুন।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics ও Full-Stack Web Frameworks -- সব এক জায়গায়।