Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
@CRBarchager2 жыл бұрын
Funny. Until today I've never heard of Idempotency and coincidentally I clicked on a video earlier explaining this and now Milan have made a video about it. - Very nice explination, Milan.
@MilanJovanovicTech2 жыл бұрын
Thank you, glad you liked it! Was my explanation easy to follow?
@murat.yuceer2 жыл бұрын
If you scale your application, still multiple handler could consumer... Second consumer will throw exception because of duplicate key for consumer table but mail was gone
@MilanJovanovicTech2 жыл бұрын
The question is, if you scale your application. Do you want all instances to be consuming your message at all? It's a typical race-condition situation. You would have to think about this differently if you want to scale it.
@murat.yuceer2 жыл бұрын
@@MilanJovanovicTech actually if you use update skip lock when you get outbox messages problem will solved
@nove13982 жыл бұрын
+1 for explaining a good method of enforcing idempotency
@MilanJovanovicTech2 жыл бұрын
Thank you. Did you work with something similar?
@nove13982 жыл бұрын
@@MilanJovanovicTech yes and though i implemented it differently it was the same principle.
@Kyogunaik2 жыл бұрын
With your awesome contents and knowledge sharing. I am feeling awesome 🤩
@MilanJovanovicTech2 жыл бұрын
Thank you very much! I'm glad you like the content 😁
@salemmaglic2 жыл бұрын
A great approach using the decorator pattern, I used pipeline behavior to achieve the same, with different base event class. I have mixed idempotent and non-idempotent calls. But overall, great video.
@MilanJovanovicTech2 жыл бұрын
How did you run a pipeline behavior with events (INotification)?
@salemmaglic2 жыл бұрын
@@MilanJovanovicTech I didn't. It was for IRequest commands only. I didn't notice that this is for notifications only. You have point. We can't use my approach for MediatR notifications, but I learned something after all. Thank you for your replay.
@emanuelrodriguez31557 ай бұрын
Quick question if i want to implement Idempotency but with massTransit i should decorete the IConsumer interface i asume, right?
@MilanJovanovicTech7 ай бұрын
MassTransit has an Outbox, which will prevent double publish: masstransit.io/documentation/patterns/transactional-outbox
@emanuelrodriguez31557 ай бұрын
@@MilanJovanovicTech thx for the feedback! appreciate it !
@garylee8672 жыл бұрын
There are something you didn't mention: - There must be an unique constraint on the OutboxMessageConsumer table. - The idempotent handler and the decorated handler must be within the same DB transaction. Otherwise, the idempotent handler won't work properly in case multiple instances consuming the same message concurrently.
@marna_li2 жыл бұрын
Just a note: Each DomainEvent has its unique Id. I reuse it for the OutboxMessage. That I changed now. Then the Id of OutboxMessageConsumer makes sense. I got a bit confused because there were stuff made to Gathering that wasn't there in earlier video (or did I miss something)
@MilanJovanovicTech2 жыл бұрын
Yes, there were some changes Marina
@MilanJovanovicTech2 жыл бұрын
- Unique constraint isn't necessary, as I made the PK to have 2 columns (Id, Name) - I can't understand why do they need to be inside the same transaction?
@garylee8672 жыл бұрын
@@MilanJovanovicTech If they are not in the same DB transaction and there are two consumer instances consuming the same event concurrently, this may happen: - Instance 1 check the OutboxMessageConsumer table (return false, which means the event is not consumed yet) - Instance 2 check the OutboxMessageConsumer table (also return false) - Instance 1 call the decorated handler and commit to DB - Instance 2 call the decorated handler and commit to DB - Instance 1 save the OutboxMessageConsumer entry (success) - Instance 2 save the OutboxMessageConsumer entry (fail, but the changes made by decorated handler are still persisted) You need to put them into the same transaction so that all changes made by instance 2 are rolled back at the last step.
@fernandocalmet2 жыл бұрын
Very interesting, you just opened my mind, thank you very much for the content Milan🤩
@MilanJovanovicTech2 жыл бұрын
Awesome, glad I could be of help Fernando!
@sauravbhatta53032 жыл бұрын
Awesome content. You earned a Pateron
@MilanJovanovicTech2 жыл бұрын
Happy to have you, champ! 🏆🏆🏆
@pollyielderah17362 жыл бұрын
Good pattern and great explanation, thank you. One small nitpick: I believe you should not be passing cancellation token to SaveChangesAsync in the IdempotentDomainEventHandler, or rather passing CancellationToken.None. Once the underlying event handler has finished work (in this case, email sent), from my understanding we are past the point of no cancellation, since the work is handled but we need to save to reach consistent state. This could probably lead to double handling in rare cases.
@MilanJovanovicTech2 жыл бұрын
That's a fair argument, indeed. I pass it mostly by convention. But would it be realistic for the cancellation to even happen given that this is a background job?
@pollyielderah17362 жыл бұрын
@@MilanJovanovicTech Hard to say, since I am not familiar with how quartz manages their cancellation tokens. Though the way I see it, there are two scenarios: - CancellationToken will never be cancelled (ex: perhaps CanBeCancelled is always false for background jobs), so there is not much point propogating the token to begin with. - CancellationToken can be cancelled (ex: perhaps cancellation is done on app SIGTERM or maybe different background job library is used, etc), so then handlers should be implemented with cancellation in mind. Either way this is a nitpick, since the timing would probably have to be very unfortunate. Though its something that is easy to fix (at least in this case), so if decision is made to have the token - might as well keep point of no cancellation in mind.
@simonjohnstone9159 Жыл бұрын
Excellent content as always Milan. I have a suggestion for another video not too dissimilar to this one. Here you're using an idempotent strategy to prevent processing back-end messages multiple times, but I'd like to see a 'clean architecture' design for tackling idempotency in an API. As an API vendor I might want to allow a consuming client to provide an idempotency key (GUID) via say an HttpHeader (Idempotency-Key) when making non idempotent API calls (i.e. POST). This key would be used to offer consumers a guarantee that a given operation would only mutate state once and once only. My thinking is that you could create an idempotent unit of work that in turn could insert the idempotency key into a table (where the key is constrained to be unique), and by including this in the transaction scope for a command, you'd achieve the desired result?
@MilanJovanovicTech Жыл бұрын
Definitely a good idea, and numerous ways to implement it. Agree on the Idempotency header for API side. Internally, maybe a pipeline behavior for commands implementing IIdempotentCommand 🤔
@AboutCleanCode2 жыл бұрын
Perfect job for the decorator pattern ;-) interesting video - thx!
@MilanJovanovicTech2 жыл бұрын
Thank you. Did you work with Decorator before?
@AboutCleanCode2 жыл бұрын
@@MilanJovanovicTech many times - it often fits when adding cross cutting concerns like logging or caching
@kodindoyannick53288 ай бұрын
Great content! Thank Milan.
@MilanJovanovicTech8 ай бұрын
You're almost done with all the older videos 😁
@hsyn28tkr Жыл бұрын
Nice solution but what if two instance gets same data on same time? In this case, won't the race condition occur? I think yes. In my opinion most safer waye is using distributed lock with RedLock algorithm etc or If your db supports the select lock mechanism(SELECT ....... FOR UPDATE SKIP LOCKED) ) it will be safer to use and data consistency will be ensured.
@MilanJovanovicTech Жыл бұрын
Yes, you'll get a race condition. But this is rarely going to happen. In the event it does, you need some sort of locking mechanism, as you suggested
@r75shell Жыл бұрын
what can we do in case SaveChangesAsync fail? for example, after success payment?
@MilanJovanovicTech Жыл бұрын
Chicken or the egg problem... If you think about it hard enough, there's no 100% guaranteed way to prevent that. Best you can do is add as much idempotency as possible. You'll need a way to check with the payment provider if payment X was already sent.
@vinr2 жыл бұрын
What happens saving the notification to db fails after the _decorated.Handle is completed, next time you will perform _decorated.Handle again, how to avoid it (at 6:50)
@MilanJovanovicTech2 жыл бұрын
Indeed, it would run again. But how likely is that to happen?
@vekzdran2 жыл бұрын
Nice work Milan. I think you can enter a bad state if after acking the event saving to outbox fails, meaning youd retry it. This should be under tran or two pahse commites I guess?
@MilanJovanovicTech2 жыл бұрын
I think that would be overkill honestly. You'll dig yourself into a bigger and bigger hole if you go down that route. When would committing the ACK actually fail? Apart from the database being down.
@vinetabozic4870 Жыл бұрын
Good video!
@MilanJovanovicTech Жыл бұрын
Glad you enjoyed it
@moinaziz81892 жыл бұрын
Nice Video keep it up bro.
@MilanJovanovicTech2 жыл бұрын
Thanks a lot!
@batressc9 ай бұрын
What is the workaround to follow when fail to save the OutboxMessageConsumer record after the execution of the DomainEvent?
@MilanJovanovicTech9 ай бұрын
Retry, send an alert, move to dead-letter queue
@CSharp_Dev20222 жыл бұрын
Nico work you are a very good programmer but can you explain why you use INotificationHandler
@MilanJovanovicTech2 жыл бұрын
It's from the MediatR library, allows you to publish an INotification that can have multiple INotificationHandlers
@microtech24483 ай бұрын
Hi, in background job video you already published event and updated outbox message, till this point event has been published once. Now in this video you are publishing event through decorator, though you are checking it has not already been consumed but outbox message consumer entry was not present for event being checked by this time and it causes same event to be published twice?
@MilanJovanovicTech3 ай бұрын
Outbox is on the producer side. Idempotent consumers are the consumer side. These are two different sides of the same pipe (the queue).
@microtech24483 ай бұрын
@@MilanJovanovicTech but same event is handled twice, is that expected or I am missing something
@MrCraick02 жыл бұрын
Hello, thank you for the videos, is there a GitHub repo?
@MilanJovanovicTech2 жыл бұрын
The code in the video is shared with my Patreons. But you can find similar examples on my GitHub
@kostasgkoutis8534 Жыл бұрын
Milan, great video content as always. Are you by any chance planning to show the usage of Entity Framework with the Hi-lo algorithm as part of this series?
@MilanJovanovicTech Жыл бұрын
We'll see, not high up on my priority list
@peymanebrahimi77562 жыл бұрын
What is the nugget package for Decorate method?
@MilanJovanovicTech2 жыл бұрын
Scrutor
@microtech24483 ай бұрын
Hello, what is the definition of IDomainEventHandler and IDomainEvent? How TDomainEvent gets its Id to set into the outboxMessageConsumer?
@MilanJovanovicTech3 ай бұрын
IDomainEvent { Guid Id } IDomainEventHandler : INotificationHandler where TDomainEvent : IDomainEvent
@microtech24483 ай бұрын
@@MilanJovanovicTech Thanks Milan
@microtech24483 ай бұрын
Hi, I am getting error "Could not find any registered services for type 'MediatR.InotificationHandlwr When using services.Decorate from scrutor. I am using latest version of scrutor and . net 8
@lukaskuchta1010 Жыл бұрын
Hi, is there any settings for publising strategy? When i made 5 dummy handlers and 3 trhows expcetion 4 and 5 not hits. It seems that when error occure next publishing is stopped.
@lukaskuchta1010 Жыл бұрын
Ok, it is NotificationPublisher settings. By default is publishing loop interrupt when exception occure. It is valid behaviour for production ? I expected o consume all possible handlers if can.
@MilanJovanovicTech Жыл бұрын
With MediatR 12 you have an option to publish in parallel
@fredyboy16211 ай бұрын
Hi Milan, what is the code for IDomainEventHandler please ? thanks
@MilanJovanovicTech11 ай бұрын
using MediatR; public interface IDomainEventHandler : INotification;
@fredyboy16210 ай бұрын
@@MilanJovanovicTech more exactly public interface IDomainEventHandler : INotification ?
@fredyboy16210 ай бұрын
@@MilanJovanovicTech where come from the property "Id" of TDomainEvent notification please ? I try to improve Pragmatic Clean Architecture I recently bought with this feature
@danielohirsch5 ай бұрын
Why not just use local memory cache to track this with some cache invalidation time on items so that these tracked "handles" (which are transiently important) don't have to hit a db to check or save - and they aren't so important after success?
@MilanJovanovicTech5 ай бұрын
What if you want to retry or replay some messages?
@lasindadilshan78602 жыл бұрын
Is there any non payable source code available ? 🥺
@MilanJovanovicTech2 жыл бұрын
You can always check out my GitHub
@microtech24483 ай бұрын
Hello, can anyone please clarify, idempotency consumers along with publishing events through background job in another video in this series, are raising the same event twice? How can we avoid that?
@MilanJovanovicTech3 ай бұрын
Do you mean the consumer is running twice? I think that's a DI issue, can't recall exactly how to fix it though.
@microtech24483 ай бұрын
@@MilanJovanovicTech hi, no consumer is running once but I am using both background job to publish event and idempotent consumer together, which results in publishing events twice, one from background job and another from idempotent consumer. And further I could not use scruter library to register idempotent handler so I used inbuilt services.Scoped(...) method to register it, if it makes any difference.
@microtech24483 ай бұрын
@@MilanJovanovicTechNo but I am running a background job which publishes events and idempotent consumer together. So event is processed first when background job fetches outbox message and second time when idempotent consumer handles it. This way, an event is processing two times.
@microtech24483 ай бұрын
@@MilanJovanovicTech it seems resolving now without additional changes. I just tried again scrutor library which was first day giving an issue, this time same version of library seems working as it should. Though not sure, what happened but it works at the end as it should. Thanks!
@gcm10000 Жыл бұрын
I would have add this name and Id in cache like key. After proccessing, remove this Key. The speed is better and don't accumulate database with multiple connections by request.
@MilanJovanovicTech Жыл бұрын
But the consistency is a concern. And what will happen in a distributed environment?
@gcm10000 Жыл бұрын
@@MilanJovanovicTech I think that be variable by scenarios. For example, I add that idea in invoice system to town hall, and work it. If persistence is really needed, I think Redis solve this problem.
@gcm10000 Жыл бұрын
@@MilanJovanovicTech Also, can you have a community in Discord?
@alameenboss2 жыл бұрын
Nice content, pls do share the code
@MilanJovanovicTech2 жыл бұрын
I share the code with my Patreons! Consider joining this month? I share both the *before* and the *after* versions of the code, so that you can follow along with me during the video.
@10199able2 жыл бұрын
TIL that you can cheat at Visual Studio
@MilanJovanovicTech2 жыл бұрын
Interesting that some haven't tried that while debugging