Handle Duplicate Messages With Idempotent Consumers | Idempotency

แชร์
ฝัง
  • เผยแพร่เมื่อ 3 ธ.ค. 2024

ความคิดเห็น •

  • @MilanJovanovicTech
    @MilanJovanovicTech  4 หลายเดือนก่อน

    Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
    Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt

  • @nove1398
    @nove1398 2 ปีที่แล้ว +4

    +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.

  • @CRBarchager
    @CRBarchager 2 ปีที่แล้ว +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 ปีที่แล้ว +1

      Thank you, glad you liked it! Was my explanation easy to follow?

  • @murat.yuceer
    @murat.yuceer 2 ปีที่แล้ว +8

    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 ปีที่แล้ว +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

  • @sauravbhatta5303
    @sauravbhatta5303 2 ปีที่แล้ว +1

    Awesome content. You earned a Pateron

  • @Kyogunaik
    @Kyogunaik 2 ปีที่แล้ว +2

    With your awesome contents and knowledge sharing. I am feeling awesome 🤩

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +2

      Thank you very much! I'm glad you like the content 😁

  • @fernandocalmet
    @fernandocalmet 2 ปีที่แล้ว +1

    Very interesting, you just opened my mind, thank you very much for the content Milan🤩

  • @kodindoyannick5328
    @kodindoyannick5328 9 หลายเดือนก่อน

    Great content! Thank Milan.

    • @MilanJovanovicTech
      @MilanJovanovicTech  9 หลายเดือนก่อน

      You're almost done with all the older videos 😁

  • @salemmaglic
    @salemmaglic 2 ปีที่แล้ว +1

    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 ปีที่แล้ว +1

      How did you run a pipeline behavior with events (INotification)?

    • @salemmaglic
      @salemmaglic 2 ปีที่แล้ว +1

      @@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.

  • @AboutCleanCode
    @AboutCleanCode 2 ปีที่แล้ว +1

    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

  • @emanuelrodriguez3155
    @emanuelrodriguez3155 8 หลายเดือนก่อน +1

    Quick question if i want to implement Idempotency but with massTransit i should decorete the IConsumer interface i asume, right?

    • @MilanJovanovicTech
      @MilanJovanovicTech  8 หลายเดือนก่อน +1

      MassTransit has an Outbox, which will prevent double publish: masstransit.io/documentation/patterns/transactional-outbox

    • @emanuelrodriguez3155
      @emanuelrodriguez3155 8 หลายเดือนก่อน

      @@MilanJovanovicTech thx for the feedback! appreciate it !

  • @r75shell
    @r75shell ปีที่แล้ว +2

    what can we do in case SaveChangesAsync fail? for example, after success payment?

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +1

      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.

  • @moinaziz8189
    @moinaziz8189 2 ปีที่แล้ว +2

    Nice Video keep it up bro.

  • @hsyn28tkr
    @hsyn28tkr 2 ปีที่แล้ว +2

    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  2 ปีที่แล้ว +1

      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

  • @vinr
    @vinr 2 ปีที่แล้ว +3

    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 ปีที่แล้ว +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 ปีที่แล้ว +1

      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 ปีที่แล้ว +1

    Good video!

  • @batressc
    @batressc 10 หลายเดือนก่อน +1

    What is the workaround to follow when fail to save the OutboxMessageConsumer record after the execution of the DomainEvent?

    • @MilanJovanovicTech
      @MilanJovanovicTech  10 หลายเดือนก่อน +1

      Retry, send an alert, move to dead-letter queue

  • @garylee867
    @garylee867 2 ปีที่แล้ว +8

    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 ปีที่แล้ว +1

      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 ปีที่แล้ว +7

      ​@@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.

  • @MrCraick0
    @MrCraick0 2 ปีที่แล้ว +1

    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

  • @simonjohnstone9159
    @simonjohnstone9159 ปีที่แล้ว +1

    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  ปีที่แล้ว +1

      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 🤔

  • @CSharp_Dev2022
    @CSharp_Dev2022 2 ปีที่แล้ว +1

    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

  • @pollyielderah1736
    @pollyielderah1736 2 ปีที่แล้ว +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 ปีที่แล้ว +1

      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 ปีที่แล้ว +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.

  • @peymanebrahimi7756
    @peymanebrahimi7756 2 ปีที่แล้ว +1

    What is the nugget package for Decorate method?

  • @microtech2448
    @microtech2448 4 หลายเดือนก่อน

    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  4 หลายเดือนก่อน

      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 4 หลายเดือนก่อน

      @@MilanJovanovicTech but same event is handled twice, is that expected or I am missing something

  • @microtech2448
    @microtech2448 4 หลายเดือนก่อน

    Hello, what is the definition of IDomainEventHandler and IDomainEvent? How TDomainEvent gets its Id to set into the outboxMessageConsumer?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 หลายเดือนก่อน

      IDomainEvent { Guid Id }
      IDomainEventHandler : INotificationHandler where TDomainEvent : IDomainEvent

    • @microtech2448
      @microtech2448 4 หลายเดือนก่อน

      @@MilanJovanovicTech Thanks Milan

    • @microtech2448
      @microtech2448 4 หลายเดือนก่อน

      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

  • @kostasgkoutis8534
    @kostasgkoutis8534 2 ปีที่แล้ว +1

    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?

  • @fredyboy162
    @fredyboy162 ปีที่แล้ว

    Hi Milan, what is the code for IDomainEventHandler please ? thanks

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว +1

      using MediatR;
      public interface IDomainEventHandler : INotification;

    • @fredyboy162
      @fredyboy162 ปีที่แล้ว

      @@MilanJovanovicTech more exactly public interface IDomainEventHandler : INotification ?

    • @fredyboy162
      @fredyboy162 ปีที่แล้ว

      @@MilanJovanovicTech where come from the property "Id" of TDomainEvent notification please ? I try to improve Pragmatic Clean Architecture I recently bought with this feature

  • @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

  • @microtech2448
    @microtech2448 4 หลายเดือนก่อน

    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  4 หลายเดือนก่อน

      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 4 หลายเดือนก่อน

      @@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 4 หลายเดือนก่อน

      ​@@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 4 หลายเดือนก่อน

      @@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!

  • @lasindadilshan7860
    @lasindadilshan7860 2 ปีที่แล้ว +1

    Is there any non payable source code available ? 🥺

  • @danielohirsch
    @danielohirsch 7 หลายเดือนก่อน

    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  7 หลายเดือนก่อน

      What if you want to retry or replay some messages?

  • @alameenboss
    @alameenboss 2 ปีที่แล้ว +2

    Nice content, pls do share the code

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +1

      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.

  • @gcm10000
    @gcm10000 ปีที่แล้ว +1

    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?

  • @10199able
    @10199able 2 ปีที่แล้ว +1

    TIL that you can cheat at Visual Studio

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      Interesting that some haven't tried that while debugging