🧩 الگوی Idempotent Consumer در NET. (و چرا به آن نیاز دارید)
سیستمهای توزیعشده ذاتاً غیرقابل اعتماد هستند. ⚠️
من همیشه توصیه میکنم برای درک بهتر اشتباهات رایج، مقالهی معروف Fallacies of Distributed Computing را مطالعه کنید.
یکی از چالشهای کلیدی در این سیستمها، اطمینان از این است که هر پیام دقیقاً یکبار پردازش شود که از نظر تئوری در بیشتر سیستمها غیرممکن است. 😅
در اینجا وارد مباحثی مثل CAP Theorem یا Two Generals Problem نمیشویم، اما کافی است بدانید که در دنیای واقعی:
پیامها ممکن است خارج از ترتیب برسند 🌀
پیامها ممکن است تکراری شوند 🔁
تحویل پیامها ممکن است با تأخیر انجام شود 🕒
اگر سیستم خود را طوری طراحی کنید که فرض کند هر پیام دقیقاً یکبار پردازش میشود، در واقع دارید زمینه را برای خرابی دادههای پنهان فراهم میکنید. 💥
اما میتوانیم سیستم خود را طوری طراحی کنیم که اثرات جانبی (side effects) فقط یکبار اعمال شوند با استفاده از الگوی قدرتمند Idempotent Consumer. 💡
بیایید با هم بررسی کنیم:
🔹 چه خطاهایی ممکن است رخ دهند
🔹 چگونه message brokerها در مدیریت idempotency کمک میکنند
🔹 و چطور میتوانیم یک Idempotent Consumer در NET. بسازیم
🚨 چه چیزی ممکن است هنگام انتشار پیام اشتباه پیش برود؟
فرض کنید سرویس شما زمانی که یک یادداشت جدید ایجاد میشود، یک event منتشر میکند:
await publisher.PublishAsync(new NoteCreated(note.Id, note.Title, note.Content));
در اینجا اهمیتی ندارد که publisher یا message broker شما چه پیادهسازیای دارد میتواند RabbitMQ، SQS، یا Azure Service Bus باشد.
حالا تصور کنید سناریوی زیر رخ دهد:
• Publisher
پیام را به broker ارسال میکند
• Broker
پیام را ذخیره کرده و یک ACK (تأیید دریافت) برمیگرداند
• یک اختلال شبکه باعث میشود ACK هرگز به producer نرسد 🌐
• Producer timeout
میشود و انتشار را دوباره تلاش میکند 🔁
حالا broker دو event از نوع NoteCreated دارد 😬
از دید producer، فقط یک timeout رفع شده است.
اما از دید consumer، دو پیام دریافت شده که هر دو مربوط به ایجاد یک یادداشت هستند.
و این تنها یکی از مسیرهای خرابی ممکن است!
ممکن است پیامهای تکراری به دلایل زیر هم اتفاق بیفتند:
• ارسال مجدد توسط broker
• خرابی consumer و اجرای مجدد در retryها
بنابراین حتی اگر در سمت publisher همه چیز را “درست” انجام دهید، باز هم consumer باید محافظهکارانه طراحی شود تا در برابر تکرارها مقاوم باشد. 🧱