পাঠ্যক্রম খুলুন
প্রাথমিক পর্যায়

Cache Stampede: একসঙ্গে miss হলে atomic lock

Cache Stampede and Atomic Locks

এই পাঠের কুইজ ↓৬ মিনিট পড়াPHP 8.3+ · Laravel 12
এই পাঠের সূচিপত্র

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

  • Hot cache miss-এর concurrent refill
  • Atomic lock ও double-check
  • Lock lease ও failure-এর সীমা

সমস্যা দিয়ে শুরু

Campaign-এর সবচেয়ে জনপ্রিয় product key একই সময়ে expire হলো। পাঁচশ request একসঙ্গে database থেকে একই product query করল। Cache স্বাভাবিক অবস্থায় load বাঁচিয়েছিল, কিন্তু synchronized miss মুহূর্তে database বেশি চাপে পড়ল। এই thundering herd বা cache stampede-এ একটিমাত্র loader এবং bounded waiting উপকারী হতে পারে।

সহজ করে বুঝি

Atomic lock একটি shared store-এ নির্দিষ্ট সময়ের জন্য ownership দেয়। Loader lock পেলে cache আবার পড়ে; আগের loader ইতোমধ্যে fill করে থাকতে পারে। এটিই double-check। তারপর source query করে TTL-সহ value রাখে এবং lock ছাড়ে। অন্য request অল্প সময় অপেক্ষা বা controlled failure পায়। Lock lease শেষ হওয়ার আগে loader শেষ হবে এমন assumption স্পষ্ট করতে হবে। Process pause বা slow query-তে lease শেষ হলে দুই loader চলতে পারে।

পরিচিত একটি উপমা

দোকানে একটি খালি shelf ভরতে সব কর্মী warehouse-এ না গিয়ে একজন token নেন। বাকিরা shelf আবার দেখে অপেক্ষা করেন। Token-এর মেয়াদ শেষ হলে আরেকজন যেতে পারেন; প্রথমজন দেরিতে ফিরে এলে দুজনই shelf ভরতে পারেন। Token business ledger-এর একমাত্র protection নয়।

নকশাটি দেখুন

Diagram লোড হচ্ছে…

প্রথম miss lock-এর দাবি করে।
Diagram-এর text flow দেখুন
flowchart TD
 R[Cache miss] --> L{Acquire loader lock}
 L -->|Yes| C[Check cache again]
 C --> D[Load source if still missing]
 D --> P[Put value with TTL]
 P --> U[Release lock]
 L -->|Bounded wait expires| F[Return controlled retry response]

প্রথম miss lock-এর দাবি করে। Lock নেওয়ার পরে দ্বিতীয় read unnecessary duplicate query কমায়। Source work bounded হওয়া দরকার। Waiting timeout হলে fallback হিসেবে সবাই database-এ গেলে stampede ফিরে আসে; নিচের example controlled 503 বেছে নিয়েছে। Stale acceptable হলে last-known value serve করার আলাদা design করা যায়।

PHP ও Laravel-এ প্রয়োগ

Controller-method fragment; Product model ও Redis cache configured। দশ সেকেন্ড lock lease, দুই সেকেন্ড wait এবং data TTL উদাহরণ policy; source query-এর bounded runtime তার চেয়ে ছোট হতে হবে। এটি read cache optimization, payment correctness lock নয়।

php
use App\Models\Product;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Support\Facades\Cache;

$cache = Cache::store('redis');
$key = "product:v1:{$productId}";
$data = $cache->get($key);
if ($data === null) {
    try {
        $data = $cache->lock("load:{$key}", 10)->block(2, function () use ($cache, $key, $productId) {
            return $cache->remember($key, 60 + random_int(0, 15), function () use ($productId) {
                return Product::query()->findOrFail($productId)->only(['id', 'name']);
            });
        });
    } catch (LockTimeoutException $exception) {
        return response()->json(['message' => 'অল্প পরে চেষ্টা করুন'], 503)
            ->header('Retry-After', '2');
    }
}
return response()->json($data);

সুবিধা, সীমাবদ্ধতা ও trade-off

Lock source load কমায়, কিন্তু shared lock store ও wait latency যোগ করে। TTL jitter অনেক key-এর simultaneous expiry ছড়ায়; এক key-এর প্রতি miss concurrency নিজে থামায় না। Stale-while-revalidate দ্রুত response দিতে পারে, কিন্তু stale window গ্রহণযোগ্য হতে হবে। Cache outage-এ fail-open করলে database protection rate limit/concurrency cap দরকার। Fail-closed availability কমায়, dependency বাঁচাতে পারে।

সাধারণ ভুল

  • Laravel remember নিজেই single-flight নিশ্চয়তা দেয় ভাবা।
  • Lock timeout হলে প্রত্যেকে সরাসরি expensive query চালানো।
  • Lock lease পেরিয়ে গেলেও exclusive ownership আছে ধরে নেওয়া।

আরও একটু গভীরে

Lock owner token ছাড়া forceRelease অন্য process-এর নতুন lock মুছে দিতে পারে। Closure-based lock API normal path-এ release সহজ করে। Process crash-এ TTL recovery দেয়, তবে premature expiry overlap ঘটায়। Cache refill duplicate হলে সাধারণত performance ক্ষতি; inventory decrement duplicate হলে correctness ক্ষতি। এই দুই requirement আলাদা lock ও database invariant দিয়ে বিচার করুন। Null result cache করতে হলে sentinel representation দিন, কারণ null miss হিসেবে ব্যাখ্যা হতে পারে।

মনে রাখুন

এক loader, second cache check এবং bounded wait—stampede mitigation-এর তিন অংশ। Lease ও fallback failure policy পরীক্ষা করুন। Cache lock-কে exactly-once business guarantee বলবেন না।

নিজে চেষ্টা করুন

পঞ্চাশ concurrent request দিয়ে একটি expired key load করুন। Query count, wait timeout ও latency দেখুন। Source query lock TTL-এর বেশি delay দিয়ে overlap সম্ভব কি না পরীক্ষা করুন; production database-এ intentional delay চালাবেন না।

নিজের বোঝা যাচাই করুন

1. Double-check কেন?

Lock পেতে অপেক্ষার সময় অন্য loader cache পূরণ করতে পারে। Lock নেওয়ার পরেও আবার miss কি না যাচাই করলে অতিরিক্ত source query এড়ানো যায়।

2. Jitter একাই যথেষ্ট?

অনেক key-এর expiry ছড়ায়, কিন্তু একটি hot key miss-এর সব caller আলাদা query করতে পারে। Single-loader coordination আলাদা।

3. Lease শেষ হলে কী হয়?

নতুন owner lock পেতে পারে, যদিও পুরোনো process চলছে। তাই TTL-কে unconditional exclusive execution guarantee বলা যাবে না।

4. Timeout-এ DB fallback?

Policy-dependent। সবাই fallback করলে মূল stampede ফিরে আসে। Bounded concurrency বা stale response/controlled error বিবেচনা করুন।

5. এই lock payment নিরাপদ করবে?

না। Durable idempotency key, transaction ও provider contract লাগবে। Cache lease volatile coordination mechanism।

আরও পড়ুন

এই পাঠের কুইজ

এই পাঠ থেকে ২টি প্রশ্ন। উত্তর দিয়ে ব্যাখ্যা দেখুন; অধ্যায়ের কুইজ আলাদাভাবে দিতে হবে।

এই পাঠের ২টি প্রশ্নের উত্তর দিন। পাস করতে দুটিই সঠিক হতে হবে।

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

সব বিষয় মিলিয়ে অনুশীলন করুন →

অগ্রগতি এই ব্রাউজারে সংরক্ষিত থাকে। শুধু পাঠ খুললে সম্পন্ন হয় না।