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

Message Ordering: ক্রম, acknowledgement ও concurrency

Ordering, Acknowledgements, and Concurrency

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

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

  • Delivery order ও completion order আলাদা করুন
  • Aggregate version দিয়ে stale event ধরুন
  • Retry ও concurrency একসঙ্গে পরীক্ষা করুন

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

OrderCreated message আগে এবং OrderCancelled পরে queue-তে গেছে। দুই worker কাজ নিল। Cancellation দ্রুত শেষ হলেও create handler দেরিতে এসে status আবার active করল। FIFO enqueue থাকলেও business effect ভুল order-এ হয়েছে।

সহজ করে বুঝি

Ordering-এর scope স্পষ্ট করতে হয়: এক producer, এক partition, এক queue নাকি এক customer? Concurrent consumers এবং retry completion order বদলাতে পারে। Aggregate version event-এর business sequence চেনায়। Consumer expected version না পেলে event buffer, retry অথবা authoritative state fetch করতে পারে।

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

সিরিয়াল নম্বরে রোগী ডাকলেও দুই চিকিৎসকের পরীক্ষা শেষ হয় ভিন্ন সময়ে। Register-এ “শেষ দেখা রোগী” লিখতে শুধু ডাকের order যথেষ্ট নয়। কাজ শুরু, শেষ এবং record update তিনটি আলাদা সময়।

নকশাটি দেখুন

Diagram লোড হচ্ছে…

Delivery order ও completion order আলাদা করুন
Diagram-এর text flow দেখুন
sequenceDiagram
 participant Q as Queue
 participant A as Worker A
 participant B as Worker B
 participant D as Projection DB
 Q->>A: Order v1 created
 Q->>B: Order v2 cancelled
 B->>D: v2 waits for v1
 A->>D: Apply v1
 B->>D: Retry and apply v2

Queue আগে v1 পাঠায়, কিন্তু A ধীর। B v2 নিয়ে আসে; projection v0 হওয়ায় gap বুঝে অপেক্ষা করে। A v1 লিখলে B retry-তে v2 apply করে। Duplicate v1 পরে এলে version check বাদ দেয়। এই policy missing event-এর alert ছাড়া অনন্ত অপেক্ষায় যেতে পারে।

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

Laravel job body fragment। order_projections row creation আগে হয়েছে; version column integer। Transaction-এর ভিতরে row lock একই order-এর projection serialize করে। Gap exception queue retry করবে, তবে bounded attempts ও dead-letter handling দরকার।

php
use Illuminate\Support\Facades\DB;

DB::transaction(function () use ($orderId, $eventVersion, $newStatus) {
    $row = DB::table('order_projections')->where('order_id', $orderId)
        ->lockForUpdate()->first();
    if ($eventVersion <= $row->version) return;
    if ($eventVersion !== $row->version + 1) {
        throw new RuntimeException('Version gap; retry or repair.');
    }
    DB::table('order_projections')->where('order_id', $orderId)
        ->update([
            'status' => $newStatus,
            'version' => $eventVersion,
        ]);
});

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

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

Per-order ordering unrelated order-কে parallel রাখে। Global ordering সহজ মনে হলেও throughput সীমিত করে। Version gap handling correctness বাড়ায় কিন্তু missing event repair দরকার। কিছু event commutative হলে strict order বাদ দিয়েও ফল ঠিক রাখা যায়।

সাধারণ ভুল

  • FIFO queue মানেই side effect completion FIFO বলা।
  • এক poison event-এর জন্য সব customer-এর queue আটকে রাখা।
  • Duplicate এবং missing version একইভাবে নীরবে বাদ দেওয়া।

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

Key-based partitioning এক aggregate-এর event একই partition-এ রাখতে পারে। Partition সংখ্যা বদলালে key mapping ও migration ভাবতে হয়। Database version থেকে প্রকাশিত event এবং consumer checkpoint-এর atomicity না থাকলে ordered transport-ও যথেষ্ট নয়।

প্রথম event-এ কৃত্রিম delay দিন, দ্বিতীয়টি দ্রুত দিন। Projection শেষে cancelled হওয়া চাই। v3-এর আগে v2 স্থায়ীভাবে হারালে কী alert হবে লিখুন। Unlimited immediate retry broker ও database দুটোই overload করতে পারে।

মনে রাখুন

  • Ordering-এর scope লিখুন।
  • Aggregate version business order জানায়।
  • Gap আর duplicate আলাদা ফল।

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

এক customer-এর balance events এবং অন্য customer-এর events কীভাবে parallel করবেন আঁকুন। একই customer-এর জন্য ordering key নির্ধারণ করুন।

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

1. FIFO যথেষ্ট?

না। Multiple workers এবং retries completion order বদলাতে পারে।

2. Version ছোট হলে?

আগে apply হয়েছে বা stale; একই event identity ও contract সাপেক্ষে safely skip করা যায়।

3. Version gap হলে?

অন্ধভাবে apply নয়; missing event retry, buffer বা repair করতে হবে।

4. Global order চাই?

যেখানে invariant cross-aggregate order দাবি করে শুধু সেখানে; অন্যথায় অপ্রয়োজনীয় serialization।

5. Ack কখন?

Local effect স্থায়ী হওয়ার পরে; framework driver behavior এবং exception handling যাচাই করতে হবে।

আরও পড়ুন

এই পাঠের কুইজ

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

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

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

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

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