পাঠ ৪১ · ৫৮-এর মধ্যে · মডিউল ৯
Home / Courses / Full-Stack Web Frameworks / ইনক্রিমেন্টাল স্ট্যাটিক রিজেনারেশন ও হাইব্রিড রেন্ডারিং

ইনক্রিমেন্টাল স্ট্যাটিক রিজেনারেশন ও হাইব্রিড রেন্ডারিং

ISR -- re-render only when the cache has gone stale, otherwise serve from cache
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • ISR কীভাবে SSG-এর "একবারই render(), চিরকাল ক্যাশ" সমস্যার সমাধান করে
  • Python দিয়ে একটি real সময়-ভিত্তিক স্টেলনেস-চেক লেখা ও তার প্রতিটি সিদ্ধান্ত হাতে-কলমে ট্রেস করা
  • ১০টি রিকোয়েস্টে ঠিক কোনগুলো re-render ট্রিগার করে ও কোনগুলো ক্যাশ থেকে সার্ভ হয় তা নির্ভুলভাবে যাচাই করা
  • "হাইব্রিড রেন্ডারিং" ধারণা -- একই সাইটে বিভিন্ন পেজে বিভিন্ন স্ট্র্যাটেজি প্রযোজ্য হতে পারা

১ · SSG-এর ক্যাশে একটি স্টেলনেস-চেক যোগ করা

ISRIncremental Static RegenerationSSG-এর ক্যাশে একটি সময়-ভিত্তিক স্টেলনেস-চেক যোগ করা একটি স্ট্র্যাটেজি -- ক্যাশ একটি নির্দিষ্ট সময়ের বেশি পুরনো হলে পরবর্তী রিকোয়েস্টে স্বয়ংক্রিয়ভাবে আবার রেন্ডার হয়। L40-এ দেখা গিয়েছিল SSG-তে render() একবারই কল হয়, ফলে কনটেন্ট বদলালেও পরবর্তী বিল্ড না হওয়া পর্যন্ত পুরনো ডেটা সার্ভ হতেই থাকে। ISR এই সমস্যা সমাধান করে একটি সাধারণ নিয়ম দিয়ে: প্রতিটি রিকোয়েস্টে চেক করা হয় ক্যাশ কতটা পুরনো -- যদি একটি নির্ধারিত REVALIDATE_INTERVAL-এর চেয়ে বেশি পুরনো হয়, তাহলে সেই রিকোয়েস্টেই render() আবার কল হয়ে ক্যাশ নতুন করে তৈরি হয়; নাহলে বিদ্যমান ক্যাশই সরাসরি সার্ভ হয়।

REVALIDATE_INTERVAL
কতটা সময় পার হলে ক্যাশ "stale" ধরা হবে -- একটি সংখ্যা (সেকেন্ড বা যেকোনো সিমুলেটেড সময়-একক)।
last_built_at
শেষবার 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 আপডেট হয়, নাহলে ক্যাশ সরাসরি সার্ভ হয়।

Python
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]}")

    
লাইন-বাই-লাইন ট্রেস: রিকোয়েস্ট #১ (t=0) ক্যাশ খালি থাকায় RE-RENDER, 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) আসলে একে অপরের বিপরীত নয় -- একই টুলবক্সের ভিন্ন হাতিয়ার, প্রতিটি পেজের প্রয়োজন অনুযায়ী বেছে নেওয়া হয়।

মূল কথা · Key takeaway

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() কল (বেশি সার্ভার খরচ); বড় ব্যবধান মানে কম খরচ কিন্তু বেশি সময় স্টেল থাকার ঝুঁকি।

অনুশীলন

  1. চিন্তা করুন: একটি খবরের সাইটের হোমপেজের জন্য REVALIDATE_INTERVAL কত রাখা যুক্তিসঙ্গত মনে হয় -- কয়েক সেকেন্ড, কয়েক মিনিট, নাকি কয়েক ঘণ্টা? আপনার উত্তরের পেছনের কারণ কী?

    সাধারণত কয়েক মিনিট যুক্তিসঙ্গত -- খবরের সাইট নিয়মিত আপডেট হয়, তাই খুব বড় ব্যবধান (কয়েক ঘণ্টা) পুরনো হেডলাইন দেখাতে পারে। আবার প্রতি কয়েক সেকেন্ডে re-render করা সার্ভারে অপ্রয়োজনীয় চাপ ফেলবে, যেখানে হোমপেজ কন্টেন্ট সেকেন্ডে সেকেন্ডে পাল্টায় না। কয়েক মিনিটের ব্যবধান সতেজতা ও সার্ভার-খরচের মধ্যে একটি বাস্তবসম্মত সমঝোতা দেয়।

  2. পরীক্ষা করুন: উপরের কোড সেলে 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 -- সব এক জায়গায়।
আগের পাঠ
স্ট্যাটিক সাইট জেনারেশন (SSG)