Handle Duplicate Messages With Idempotent Consumers | Idempotency

  Рет қаралды 17,347

Milan Jovanović

Milan Jovanović

Күн бұрын

Пікірлер: 89
@MilanJovanovicTech
@MilanJovanovicTech 3 ай бұрын
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
@CRBarchager
@CRBarchager 2 жыл бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you, glad you liked it! Was my explanation easy to follow?
@murat.yuceer
@murat.yuceer 2 жыл бұрын
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
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
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.yuceer
@murat.yuceer 2 жыл бұрын
@@MilanJovanovicTech actually if you use update skip lock when you get outbox messages problem will solved
@nove1398
@nove1398 2 жыл бұрын
+1 for explaining a good method of enforcing idempotency
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you. Did you work with something similar?
@nove1398
@nove1398 2 жыл бұрын
@@MilanJovanovicTech yes and though i implemented it differently it was the same principle.
@Kyogunaik
@Kyogunaik 2 жыл бұрын
With your awesome contents and knowledge sharing. I am feeling awesome 🤩
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you very much! I'm glad you like the content 😁
@salemmaglic
@salemmaglic 2 жыл бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
How did you run a pipeline behavior with events (INotification)?
@salemmaglic
@salemmaglic 2 жыл бұрын
@@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.
@emanuelrodriguez3155
@emanuelrodriguez3155 7 ай бұрын
Quick question if i want to implement Idempotency but with massTransit i should decorete the IConsumer interface i asume, right?
@MilanJovanovicTech
@MilanJovanovicTech 7 ай бұрын
MassTransit has an Outbox, which will prevent double publish: masstransit.io/documentation/patterns/transactional-outbox
@emanuelrodriguez3155
@emanuelrodriguez3155 7 ай бұрын
@@MilanJovanovicTech thx for the feedback! appreciate it !
@garylee867
@garylee867 2 жыл бұрын
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_li
@marna_li 2 жыл бұрын
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)
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Yes, there were some changes Marina
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
- 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?
@garylee867
@garylee867 2 жыл бұрын
​@@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.
@fernandocalmet
@fernandocalmet 2 жыл бұрын
Very interesting, you just opened my mind, thank you very much for the content Milan🤩
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Awesome, glad I could be of help Fernando!
@sauravbhatta5303
@sauravbhatta5303 2 жыл бұрын
Awesome content. You earned a Pateron
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Happy to have you, champ! 🏆🏆🏆
@pollyielderah1736
@pollyielderah1736 2 жыл бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
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?
@pollyielderah1736
@pollyielderah1736 2 жыл бұрын
@@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
@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
@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 🤔
@AboutCleanCode
@AboutCleanCode 2 жыл бұрын
Perfect job for the decorator pattern ;-) interesting video - thx!
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thank you. Did you work with Decorator before?
@AboutCleanCode
@AboutCleanCode 2 жыл бұрын
@@MilanJovanovicTech many times - it often fits when adding cross cutting concerns like logging or caching
@kodindoyannick5328
@kodindoyannick5328 8 ай бұрын
Great content! Thank Milan.
@MilanJovanovicTech
@MilanJovanovicTech 8 ай бұрын
You're almost done with all the older videos 😁
@hsyn28tkr
@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
@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
@r75shell Жыл бұрын
what can we do in case SaveChangesAsync fail? for example, after success payment?
@MilanJovanovicTech
@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.
@vinr
@vinr 2 жыл бұрын
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)
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Indeed, it would run again. But how likely is that to happen?
@vekzdran
@vekzdran 2 жыл бұрын
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?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
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
@vinetabozic4870 Жыл бұрын
Good video!
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Glad you enjoyed it
@moinaziz8189
@moinaziz8189 2 жыл бұрын
Nice Video keep it up bro.
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Thanks a lot!
@batressc
@batressc 9 ай бұрын
What is the workaround to follow when fail to save the OutboxMessageConsumer record after the execution of the DomainEvent?
@MilanJovanovicTech
@MilanJovanovicTech 9 ай бұрын
Retry, send an alert, move to dead-letter queue
@CSharp_Dev2022
@CSharp_Dev2022 2 жыл бұрын
Nico work you are a very good programmer but can you explain why you use INotificationHandler
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
It's from the MediatR library, allows you to publish an INotification that can have multiple INotificationHandlers
@microtech2448
@microtech2448 3 ай бұрын
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?
@MilanJovanovicTech
@MilanJovanovicTech 3 ай бұрын
Outbox is on the producer side. Idempotent consumers are the consumer side. These are two different sides of the same pipe (the queue).
@microtech2448
@microtech2448 3 ай бұрын
@@MilanJovanovicTech but same event is handled twice, is that expected or I am missing something
@MrCraick0
@MrCraick0 2 жыл бұрын
Hello, thank you for the videos, is there a GitHub repo?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
The code in the video is shared with my Patreons. But you can find similar examples on my GitHub
@kostasgkoutis8534
@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
@MilanJovanovicTech Жыл бұрын
We'll see, not high up on my priority list
@peymanebrahimi7756
@peymanebrahimi7756 2 жыл бұрын
What is the nugget package for Decorate method?
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Scrutor
@microtech2448
@microtech2448 3 ай бұрын
Hello, what is the definition of IDomainEventHandler and IDomainEvent? How TDomainEvent gets its Id to set into the outboxMessageConsumer?
@MilanJovanovicTech
@MilanJovanovicTech 3 ай бұрын
IDomainEvent { Guid Id } IDomainEventHandler : INotificationHandler where TDomainEvent : IDomainEvent
@microtech2448
@microtech2448 3 ай бұрын
@@MilanJovanovicTech Thanks Milan
@microtech2448
@microtech2448 3 ай бұрын
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
@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
@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
@MilanJovanovicTech Жыл бұрын
With MediatR 12 you have an option to publish in parallel
@fredyboy162
@fredyboy162 11 ай бұрын
Hi Milan, what is the code for IDomainEventHandler please ? thanks
@MilanJovanovicTech
@MilanJovanovicTech 11 ай бұрын
using MediatR; public interface IDomainEventHandler : INotification;
@fredyboy162
@fredyboy162 10 ай бұрын
@@MilanJovanovicTech more exactly public interface IDomainEventHandler : INotification ?
@fredyboy162
@fredyboy162 10 ай бұрын
@@MilanJovanovicTech where come from the property "Id" of TDomainEvent notification please ? I try to improve Pragmatic Clean Architecture I recently bought with this feature
@danielohirsch
@danielohirsch 5 ай бұрын
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?
@MilanJovanovicTech
@MilanJovanovicTech 5 ай бұрын
What if you want to retry or replay some messages?
@lasindadilshan7860
@lasindadilshan7860 2 жыл бұрын
Is there any non payable source code available ? 🥺
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
You can always check out my GitHub
@microtech2448
@microtech2448 3 ай бұрын
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?
@MilanJovanovicTech
@MilanJovanovicTech 3 ай бұрын
Do you mean the consumer is running twice? I think that's a DI issue, can't recall exactly how to fix it though.
@microtech2448
@microtech2448 3 ай бұрын
@@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.
@microtech2448
@microtech2448 3 ай бұрын
​@@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.
@microtech2448
@microtech2448 3 ай бұрын
@@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
@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
@MilanJovanovicTech Жыл бұрын
But the consistency is a concern. And what will happen in a distributed environment?
@gcm10000
@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
@gcm10000 Жыл бұрын
@@MilanJovanovicTech Also, can you have a community in Discord?
@alameenboss
@alameenboss 2 жыл бұрын
Nice content, pls do share the code
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
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.
@10199able
@10199able 2 жыл бұрын
TIL that you can cheat at Visual Studio
@MilanJovanovicTech
@MilanJovanovicTech 2 жыл бұрын
Interesting that some haven't tried that while debugging
Unit Testing CQRS Handlers With Moq, Fluent Assertions, and xUnit
13:53
Milan Jovanović
Рет қаралды 37 М.
How To Make Your API Idempotent To Stop Duplicate Requests
14:26
Milan Jovanović
Рет қаралды 26 М.
НИКИТА ПОДСТАВИЛ ДЖОНИ 😡
01:00
HOOOTDOGS
Рет қаралды 3,1 МЛН
Colorful Pasta Painting for Fun Times! 🍝 🎨
00:29
La La Learn
Рет қаралды 308 МЛН
Handling Duplicate Messages (Idempotent Consumers)
12:08
CodeOpinion
Рет қаралды 24 М.
What Is A Message Queue + RabbitMQ and MassTransit Integration
15:15
Milan Jovanović
Рет қаралды 32 М.
Apple, Stop Putting Things On the Bottom Please
9:16
TechLinked
Рет қаралды 468 М.
Solve Logging as a Cross-Cutting Concern with MediatR
9:42
Milan Jovanović
Рет қаралды 28 М.
5 Useful Python Decorators (ft. Carberra)
14:34
Indently
Рет қаралды 104 М.
How To Create Smart Enums in C# With Rich Behavior
17:31
Milan Jovanović
Рет қаралды 55 М.
Idempotency in APIs: you should be aware of this!
7:31
Software Developer Diaries
Рет қаралды 15 М.
Using Domain Events To Build A Decoupled System The Scales
14:02
Milan Jovanović
Рет қаралды 25 М.
WATCH: Niall Ferguson Stuns World Leaders at ARC Australia - "Are we the Soviets now?"
19:44
Alliance for Responsible Citizenship
Рет қаралды 194 М.
НИКИТА ПОДСТАВИЛ ДЖОНИ 😡
01:00
HOOOTDOGS
Рет қаралды 3,1 МЛН