পাঠ্যক্রম খুলুন
মধ্যবর্তী পর্যায়

Idempotency: একই request আবার এলে নিরাপদ ফল

Idempotency and Deduplication

এই পাঠের কুইজ ↓৫ মিনিট পড়াPHP 8.3+ · Laravel 12
আগে পড়ে নিনDatabase Transaction: ACID ও row locking ↗
এই পাঠের সূচিপত্র

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

  • Retry-safe operation-এর contract লিখুন
  • Key payload ও stored result মিলান
  • Database uniqueness দিয়ে race ঠেকান

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

ক্রেতা Pay button দুইবার চাপলেন। Browser timeout-এর পরে mobile app-ও retry করল। তিন request একই উদ্দেশ্য বহন করলেও তিন order তৈরি হলো। Frontend button disable করলেই network retry বা concurrent client ঠেকে না।

সহজ করে বুঝি

Idempotent operation একই input identity-তে বারবার প্রয়োগ হলেও intended effect একবারের সমতুল্য রাখে। Deduplication duplicate চিনে বাদ দেয়; শুধু কিছু সময় cache-এ key রাখার guarantee সীমিত। Key-এর scope, payload fingerprint, retention এবং processing state contract-এর অংশ।

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

এক আবেদন জমার receipt number দেখিয়ে আবার খোঁজ নিলে নতুন আবেদন খোলা হয় না। একই receipt number-এ অন্য ব্যক্তির আবেদন দিলে দপ্তর তা reject করে। Receipt হারিয়ে নতুন number নিলে আগের আবেদন খুঁজে পাওয়া কঠিন; client retry-তেও key স্থায়ী থাকতে হবে।

নকশাটি দেখুন

Diagram লোড হচ্ছে…

Retry-safe operation-এর contract লিখুন
Diagram-এর text flow দেখুন
flowchart TD
 C[Client stable key] --> A[Laravel API]
 A --> D[(Unique tenant and key)]
 D --> N[Create local order once]
 D --> R[Return stored result]
 A --> X[Reject changed payload]

API authenticated tenant-এর সঙ্গে key scope করে। Unique constraint concurrent insertion serialize করে। প্রথম request local order ও result এক transaction-এ লেখে; duplicate stored result পায়। একই key-তে বদলানো payload আলাদা error হয়, পুরোনো result নীরবে ফেরে না।

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

Fragment-এর requests table-এ unique tenant_id,key এবং orders-এ request identity unique আছে। এখানে শুধু local database effect; external charge transaction-এর মধ্যে করবেন না। PostgreSQL/MySQL-এ insertOrIgnore ও row lock ব্যবহার করা হয়েছে; request body hash server-normalized payload থেকে নিতে হবে।

php
use Illuminate\Support\Facades\DB;

$result = DB::transaction(function () use ($tenantId, $key, $hash) {
    DB::table('operation_requests')->insertOrIgnore([
        'tenant_id' => $tenantId, 'key' => $key,
        'payload_hash' => $hash, 'order_id' => null,
    ]);
    $row = DB::table('operation_requests')
        ->where('tenant_id', $tenantId)->where('key', $key)
        ->lockForUpdate()->first();
    abort_unless(hash_equals($row->payload_hash, $hash), 409);
    if ($row->order_id) return $row->order_id;
    $id = DB::table('orders')->insertGetId([
        'tenant_id' => $tenantId, 'status' => 'pending',
    ]);
    DB::table('operation_requests')->where('id', $row->id)
        ->update(['order_id' => $id]);
    return $id;
});

এটি PHP 8.3+ ও Laravel 12-এর শিক্ষামূলক fragment; সম্পূর্ণ application নয়। সংশ্লিষ্ট route-এ authentication ও resource authorization প্রয়োগ করতে হবে। নিচের অনুশীলনে failure ইচ্ছা করে তৈরি করুন; শুধু সফল response দেখে correctness ধরে নেবেন না।

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

Durable dedupe storage ও cleanup লাগে; key চিরকাল রাখলে খরচ বাড়ে। Retention শেষ হলে পুরোনো retry আবার operation হতে পারে, তাই client contract দরকার। Cache lock দ্রুত হলেও database atomic result-এর সমান crash guarantee দেয় না।

সাধারণ ভুল

  • প্রতি retry-তে নতুন UUID বানানো।
  • Key আছে কিনা SELECT করে পরে INSERT করা, unique constraint ছাড়া।
  • এক tenant-এর key অন্য tenant-এর response ফেরানো।

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

Idempotency payment provider boundary পর্যন্ত পৌঁছানো চাই। Local order একবার হলেও charge দুবার হতে পারে। External provider key ও local operation identity সংযুক্ত করুন; crash-এর পরে response lost হলে reconciliation রাখুন। Request cancellation কাজটি rollback করেছে ধরে নেবেন না।

এক key-তে দশ concurrent request পাঠান। Order row একটি এবং সব response-এ একই identity চাই। এরপর amount বদলে একই key পাঠিয়ে conflict দেখুন। Transaction commit-এর পরে response হারালে পুনরায় একই result ফেরত আসা পরীক্ষা করুন।

মনে রাখুন

  • Identity client intention অনুসরণ করে।
  • Unique constraint race বন্ধ করে।
  • Payload mismatch ও retention প্রকাশ করুন।

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

Order তৈরির idempotency API contract লিখুন: key scope, maximum length, retention, pending response এবং changed payload-এর status code দিন।

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

1. POST কি idempotent হতে পারে?

হ্যাঁ। Application key ও durable result দিয়ে retry-safe semantics দিতে পারে; HTTP method নাম একা সেটি দেয় না।

2. Key কার?

Client-এর একই business intention-এর; retry-তে পরিবর্তন হবে না এবং tenant scope থাকবে।

3. Cache যথেষ্ট?

কিছু best-effort dedupe-তে চলে; expiry, eviction ও crash-এর পরে durable effect পুনরায় হতে পারে।

4. Payload বদলালে?

এক key-তে অন্য অর্থ লুকিয়ে যাবে, তাই fingerprint mismatch reject করা উচিত।

5. External charge কীভাবে?

Provider-এর documented idempotency contract এবং status reconciliation দরকার; local transaction একা দূরের effect নিয়ন্ত্রণ করে না।

আরও পড়ুন

এই পাঠের কুইজ

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

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

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

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

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