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

Database Transaction: ACID ও row locking

Transactions, ACID, and Row Locking

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

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

  • ACID-এর practical অর্থ
  • Stock update-এ transaction ও row lock
  • Database transaction-এর বাইরের side effect বোঝা

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

একটি বইয়ের stock ১। দুই customer একই সময়ে checkout করলেন। দুজনই stock ১ পড়ে order confirm করলেন। সাধারণ validation ঠিক ছিল; তবু overselling হলো। Read ও write-এর মাঝে concurrent request ঢুকে পড়েছে। Correctness-এর জন্য atomic operation ও isolation দরকার।

সহজ করে বুঝি

Transaction একাধিক database operation-কে একটি unit হিসেবে commit বা rollback করে। Atomicity মানে সব operation সফল অথবা rollback। Consistency মানে declared constraints ও application invariants বজায় রাখা। Isolation concurrent transaction-এর interaction নিয়ন্ত্রণ করে। Durability committed data persistence-এর প্রতিশ্রুতি, নির্দিষ্ট database configuration ও failure model-এর মধ্যে।

Transaction ব্যবহার করলেই সব race বন্ধ হয় না। Isolation level, query condition এবং locking strategy গুরুত্বপূর্ণ। Stock পড়ে পরে decrement করলে row lock বা atomic conditional update দরকার হতে পারে।

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

গুদাম থেকে শেষ বই বের করার সময় clerk stock খাতাটি নিজের কাছে নিয়ে যাচাই করেন এবং বিক্রি লিখে তবেই খাতা ছাড়েন। দ্বিতীয় clerk অপেক্ষা করে updated stock দেখেন। Lock খাতার exclusive access-এর মতো; অতিরিক্ত সময় ধরে রাখলে অন্যদের line বাড়ে।

নকশাটি দেখুন

Diagram লোড হচ্ছে…

একই row-এর concurrent checkout lock release পর্যন্ত অপেক্ষা করবে, তারপর নতুন stock অনুযায়ী সিদ্ধান্ত নেবে।
Diagram-এর text flow দেখুন
flowchart TD
 A[Begin transaction] --> B[SELECT product FOR UPDATE]
 B --> C{Stock available?}
 C -->|No| D[Throw and rollback]
 C -->|Yes| E[Decrement stock]
 E --> F[Create order]
 F --> G[Commit and release lock]

PostgreSQL বা MySQL InnoDB ধরে একই database connection-এর transaction-এর ভিতরে row lock নিন। Stock যাচাই, decrement ও order insert একই transaction-এ করুন। Exception হলে rollback হবে। Payment provider-এর network call transaction-এর ভিতরে ধরে রাখবেন না।

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

নিচের action-method fragment-এ quantity positive integer হিসেবে Form Request-এ validated, user authorized এবং Product/Order model fillable configuration আছে ধরে নেওয়া হয়েছে। order schema-তে user_id, product_id, quantity ও status থাকবে। এটি payment capture বা duplicate request prevention বাস্তবায়ন করে না।

php
use App\Models\Order;
use App\Models\Product;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

$order = DB::transaction(function () use ($productId, $quantity, $userId) {
    $product = Product::query()->lockForUpdate()->findOrFail($productId);

    if ($product->stock < $quantity) {
        throw ValidationException::withMessages([
            'quantity' => 'পর্যাপ্ত stock নেই।',
        ]);
    }

    $product->decrement('stock', $quantity);

    return Order::create([
        'user_id' => $userId,
        'product_id' => $product->id,
        'quantity' => $quantity,
        'status' => 'pending',
    ]);
}, attempts: 3);

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

Row locking overselling-এর নির্দিষ্ট race সামলায়, কিন্তু contention ও deadlock-এর সম্ভাবনা বাড়ায়। Short transaction রাখুন এবং একাধিক row lock করলে consistent order মানুন। Laravel deadlock retry করতে পারে; closure-এর non-database side effect বারবার চলতে পারে, তাই সেখানে email বা charge পাঠাবেন না।

সাধারণ ভুল

  • Transaction-এর বাইরে lockForUpdate query চালানো।
  • Stock validation শুধু request শুরুর সময় করা।
  • External payment সফল হলে DB rollback সেটিও undo করবে ভাবা।
  • SQLite test দিয়ে PostgreSQL/MySQL-এর concurrent locking semantics যাচাই হয়েছে বলা।

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

Database constraint শেষ প্রতিরক্ষা। উপযুক্ত CHECK, foreign key ও unique constraint যোগ করুন। Idempotency key-র unique index duplicate business request সামলাতে সাহায্য করে, কিন্তু সেটি row lock-এর বিকল্প নয়। Database + external provider জুড়ে atomicity-এর জন্য explicit workflow, reconciliation বা Saga pattern দরকার।

মনে রাখুন

  • Transaction boundary business invariant ঘিরে রাখুন।
  • Concurrency control আলাদাভাবে design করুন।
  • DB rollback external network side effect ফেরায় না।

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

আলাদা দুটি PostgreSQL connection দিয়ে একই শেষ বইয়ের checkout চালান। একটি order-ই সফল এবং stock কখনও negative নয়—এটি পরীক্ষা করুন। Laravel transaction test-এর সঙ্গে এই concurrency integration test-এর পার্থক্য লিখুন।

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

1. ACID-এর A কী?

Atomicity: transaction-এর operation-গুলো একসঙ্গে commit বা rollback হয়।

2. Transaction থাকলেই overselling বন্ধ?

সবসময় নয়। Concurrent read-modify-write-এর locking বা conditional update strategy দরকার।

3. lockForUpdate কোথায় নেব?

সংশ্লিষ্ট write-গুলোর একই transaction ও connection-এর ভিতরে।

4. DB rollback কি payment refund করে?

না। External provider আলাদা system; compensating workflow বা reconciliation লাগে।

5. Deadlock retry-তে email কেন ঝুঁকি?

Transaction closure আবার চালতে পারে, ফলে external side effect duplicate হতে পারে।

আরও পড়ুন

এই পাঠের কুইজ

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

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

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

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

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