مصرفکننده Idempotent مشتاق (Eager) 💪
مصرفکننده idempotent مشتاق کمی با پیادهسازی تنبل متفاوت است، اما نتیجه نهایی یکسان است.
در این نسخه، ما مشتاقانه شناسه پیام را در دیتابیس ذخیره کرده و سپس به مدیریت پیام ادامه میدهیم.
اگر handler یک استثنا پرتاب کند، ما باید در دیتابیس پاکسازی انجام داده و شناسه پیام ذخیره شده مشتاقانه را حذف کنیم.
در غیر این صورت، ما با ریسک رها کردن سیستم در یک وضعیت ناسازگار مواجه میشویم، زیرا پیام هرگز به درستی مدیریت نشده است.
در اینجا شکل پیادهسازی آمده است:
public class IdempotentConsumer<T> : IHandleMessages<T>
where T : IMessage
{
private readonly IMessageRepository _messageRepository;
private readonly IHandleMessages<T> _decorated;
public IdempotentConsumer(
IMMessageRepository messageRepository,
IHandleMessages<T> decorated)
{
_messageRepository = messageRepository;
_decorated = decorated;
}
public async Task Handle(T message)
{
try
{
if (_messageRepository.IsProcessed(message.Id))
{
return;
}
_messageRepository.Store(message.Id);
await _decorated.Handle(message);
}
catch (Exception e)
{
_messageRepository.Remove(message.Id);
throw;
}
}
}
به طور خلاصه 📝
Idempotency
یک مشکل جالب برای حل کردن در یک سیستم نرمافزاری است.
برخی عملیات به طور طبیعی idempotent هستند و ما به سربار (overhead) الگوی مصرفکننده Idempotent نیازی نداریم.
با این حال، برای آن دسته از عملیاتی که به طور طبیعی idempotent نیستند، مصرفکننده Idempotent یک راهحل عالی است.
الگوریتم سطح بالا ساده است و شما میتوانید دو رویکرد را در پیادهسازی در پیش بگیرید:
🔹 ذخیرهسازی تنبل (Lazy) شناسههای پیام
🔹 ذخیرهسازی مشتاق (Eager) شناسههای پیام
من ترجیح میدهم از رویکرد تنبل استفاده کنم، و فقط شناسه پیام را در دیتابیس زمانی ذخیره کنم که handler با موفقیت کامل شود.
استدلال در مورد آن آسانتر است و یک فراخوانی کمتر به دیتابیس وجود دارد.
ممنون برای مطالعه.
امیدوارم که مفید بوده باشد. 👋