Class InMemoryIdempotencyStore

java.lang.Object
org.acemq.amqp.patterns.InMemoryIdempotencyStore
All Implemented Interfaces:
IdempotencyStore

public final class InMemoryIdempotencyStore extends Object implements IdempotencyStore
An idempotency store held in this process's memory, bounded by size and age.

Honest about what it is: this deduplicates within one process and forgets everything when that process restarts. That is enough for a single consumer whose duplicates arrive seconds apart, which is the common case, and it is not enough for a consumer group spread across several instances, where a duplicate can land on a different machine from the original. For that the store has to be shared — Redis or a database table — and this class is the shape such an implementation takes.

Both bounds exist to stop the obvious failure. Unbounded, this is a memory leak with a business purpose: every message identifier ever seen, retained forever. Entries expire after a retention period, and the map is capped, because a store that runs a service out of memory has done more damage than the duplicates it prevented.

  • Constructor Details

    • InMemoryIdempotencyStore

      public InMemoryIdempotencyStore(Duration retention)
      Parameters:
      retention - how long a confirmed identifier is remembered; duplicates arriving later than this are handled again
    • InMemoryIdempotencyStore

      public InMemoryIdempotencyStore(Duration retention, int maxEntries)
      Parameters:
      retention - how long a confirmed identifier is remembered
      maxEntries - hard cap on remembered identifiers
    • InMemoryIdempotencyStore

      public InMemoryIdempotencyStore(Duration retention, int maxEntries, Duration claimTimeout)
      Parameters:
      retention - how long a confirmed identifier is remembered
      maxEntries - hard cap on remembered identifiers
      claimTimeout - how long a claim that was never confirmed or released holds the identifier before another delivery may take it over. While it holds, redeliveries are sent back to the queue as in progress rather than handled or acknowledged
  • Method Details

    • forOneDay

      public static InMemoryIdempotencyStore forOneDay()
      Returns:
      a store that remembers identifiers for a day, a reasonable default when the alternative is a duplicate charge or a duplicate email
    • claim

      public boolean claim(String messageId)
      Description copied from interface: IdempotencyStore
      Takes ownership of a message identifier, if nobody else has it.
      Specified by:
      claim in interface IdempotencyStore
      Parameters:
      messageId - the identifier, normally Envelope.id()
      Returns:
      true when the caller now owns this identifier and should do the work; false when it is already confirmed or claimed elsewhere. Which of the two it is matters — only a confirmed one is a duplicate — so consumers call IdempotencyStore.tryClaim(java.lang.String)
    • confirm

      public void confirm(String messageId)
      Description copied from interface: IdempotencyStore
      Records that the work for a claimed identifier completed.

      After this, IdempotencyStore.claim(java.lang.String) returns false for the identifier until the store forgets it, which is what stops a redelivery from repeating the work.

      Specified by:
      confirm in interface IdempotencyStore
      Parameters:
      messageId - the identifier being confirmed
    • release

      public void release(String messageId)
      Description copied from interface: IdempotencyStore
      Gives up a claim without recording completion, so the message can be tried again.

      Called when a handler fails. Without it a failed attempt would poison the identifier and the retry would be discarded as a duplicate, which turns a transient failure into permanent message loss.

      Specified by:
      release in interface IdempotencyStore
      Parameters:
      messageId - the identifier being released
    • isConfirmed

      public boolean isConfirmed(String messageId)
      Specified by:
      isConfirmed in interface IdempotencyStore
      Parameters:
      messageId - the identifier to test
      Returns:
      whether the work for this identifier is already known to have completed
    • size

      public int size()
      Returns:
      how many identifiers are currently remembered
    • evictions

      public long evictions()
      Returns:
      how many identifiers have been dropped to stay within the size cap