Message Delivery: at-most-once, at-least-once ও exactly-once
At-Most-Once, At-Least-Once, and Exactly-Once Delivery
এই পাঠের সূচিপত্র
এই পাঠে যা শিখবেন
- Delivery ও processing আলাদা করুন
- Ack crash window আঁকুন
- Exactly-once claim-এর boundary যাচাই করুন
সমস্যা দিয়ে শুরু
Receipt email পাঠানোর পরে worker crash করল, queue acknowledgment পেল না। Queue message আবার দিল এবং email আবার গেল। Team বলছে broker duplicate দিয়েছে; কিন্তু broker জানত না প্রথম worker side effect শেষ করেছে কি না।
সহজ করে বুঝি
At-most-once delivery পুনরায় না দিয়ে হারানোর ঝুঁকি নেয়। At-least-once retry করে, duplicate হতে পারে। Exactly-once কথাটি কোন boundary-তে প্রযোজ্য তা বলা জরুরি: broker log transaction, database effect, নাকি external email? Message delivery একবার হলেও client নিজে operation আবার পাঠাতে পারে।
পরিচিত একটি উপমা
চিঠি পৌঁছানোর স্বাক্ষর হারালে ডাকঘর আবার পাঠাতে পারে। একই চিঠি দুবার এলেও অফিস যদি চিঠির নম্বর দেখে একটি আবেদন চালায়, business effect একবার থাকতে পারে। ডাকঘরের নিশ্চয়তা আর অফিসের কাজের নিশ্চয়তা আলাদা।
নকশাটি দেখুন
Diagram লোড হচ্ছে…
Diagram-এর text flow দেখুন
sequenceDiagram participant Q as Queue participant W as Worker participant D as Database Q->>W: Event E17 W->>D: Atomic effect and inbox E17 W--xQ: Ack lost Q->>W: Event E17 again W->>D: Inbox says already handled W-->>Q: Ack
Worker প্রথম delivery-তে inbox marker ও local effect একই transaction-এ লেখে। Ack হারালে broker আবার দেয়। দ্বিতীয় delivery marker পেয়ে effect repeat করে না। Effect যদি email provider-এ হয়, এই local atomic boundary ভেঙে যায়; provider contract দরকার।
PHP ও Laravel-এ প্রয়োগ
Laravel job handle method-এর body fragment। processed_events.event_id-এ unique constraint এবং wallets row আগে আছে ধরে নিন। একই transaction-এ marker ও balance increment আছে, তাই exception হলে দুটোই rollback হবে। Currency amount integer minor units।
use Illuminate\Support\Facades\DB;
DB::transaction(function () use ($eventId, $walletId, $amountMinor) {
$inserted = DB::table('processed_events')->insertOrIgnore([
'event_id' => $eventId,
'processed_at' => now(),
]);
if ($inserted === 0) {
return;
}
$changed = DB::table('wallets')->where('id', $walletId)
->increment('amount_minor', $amountMinor);
if ($changed !== 1) {
throw new RuntimeException('Wallet missing; roll back inbox too.');
}
});এটি PHP 8.3+ ও Laravel 12-এর শিক্ষামূলক fragment; সম্পূর্ণ application নয়। সংশ্লিষ্ট route-এ authentication ও resource authorization প্রয়োগ করতে হবে। নিচের অনুশীলনে failure ইচ্ছা করে তৈরি করুন; শুধু সফল response দেখে correctness ধরে নেবেন না।
সুবিধা, সীমাবদ্ধতা ও trade-off
At-least-once plus idempotent consumer সাধারণত বাস্তবসম্মত। Inbox storage বাড়ে এবং retention বুঝে করতে হয়। At-most-once telemetry sample-এ মানাতে পারে, financial event-এ হারানো গ্রহণযোগ্য নয়। Exactly-once processing platform-এর বাইরে side effect গেলে আলাদা বিশ্লেষণ দরকার।
সাধারণ ভুল
- Message receive হলেই ack করে পরে কাজ করা।
- Inbox marker commit করে পরে effect চালানো।
- Kafka transaction আছে বলে external email exactly-once বলা।
আরও একটু গভীরে
Broker transaction কিছু read-process-write flow-এ consumed offsets ও produced records একসঙ্গে commit করে। Database transaction ভিন্ন boundary। Atomicity না থাকলে crash window কোথাও থাকবে; outbox, inbox ও idempotent provider দিয়ে প্রতিটি boundary আলাদা সামলাতে হয়।
প্রথম transaction-এর আগে, মাঝখানে এবং commit-এর পরে worker kill করুন। Retry-এর পরে balance একবার বাড়া চাই। Marker retention কমিয়ে পুরোনো event আবার পাঠালে কী হয় দেখুন। এই পরীক্ষা guarantee-এর সময়সীমাও স্পষ্ট করে।
মনে রাখুন
- Delivery আর side effect পৃথক।
- Ack হারানো duplicate-এর স্বাভাবিক কারণ।
- Atomic marker ও effect এক boundary-তে রাখুন।
নিজে চেষ্টা করুন
Email, wallet credit এবং analytics counter-এর জন্য delivery guarantee নির্বাচন করুন। প্রতিটিতে duplicate বা loss-এর ক্ষতি এবং সুরক্ষা লিখুন।
নিজের বোঝা যাচাই করুন
1. At-least-once কী দেয়?
Retry করে delivery চেষ্টা চালায়; duplicate handling consumer-এর কাজ, delivery eventual হবে এমন অন্যান্য assumptions-ও থাকে।
2. Ack আগে দিলে?
Worker পরে crash করলে broker সম্পন্ন ভেবে message হারাতে পারে।
3. Inbox আলাদা commit কেন ভুল?
Marker আছে কিন্তু effect হয়নি এমন অবস্থায় retry কাজটি বাদ দিয়ে দিতে পারে।
4. Exactly-once কোথায়?
যে transactional boundary documentation সংজ্ঞায়িত করে সেখানে; সব external side effect-এ নয়।
5. Retention কেন জরুরি?
Marker মুছে গেলে পুরোনো duplicate নতুন মনে হবে; replay window-এর সঙ্গে retention মিলতে হবে।
আরও পড়ুন
এই পাঠের কুইজ
এই পাঠ থেকে ২টি প্রশ্ন। উত্তর দিয়ে ব্যাখ্যা দেখুন; অধ্যায়ের কুইজ আলাদাভাবে দিতে হবে।
এই পাঠের ২টি প্রশ্নের উত্তর দিন। পাস করতে দুটিই সঠিক হতে হবে।
জমা দেওয়ার পর সঠিক উত্তর ও ব্যাখ্যা দেখবেন। ফলাফল এই ব্রাউজারে সংরক্ষিত থাকবে। অনুশীলনের ফল অধ্যায় সম্পন্নের হিসাবে যোগ হয় না।
অগ্রগতি এই ব্রাউজারে সংরক্ষিত থাকে। শুধু পাঠ খুললে সম্পন্ন হয় না।