Class CommandDedupeSet

java.lang.Object
com.ultikits.ultitools.utils.CommandDedupeSet

final class CommandDedupeSet extends Object
The ids of commands the UltiCloud gateway delivered recently, so a redelivered command is acknowledged but not run twice (D-16, D-17). An id is remembered until either bound is reached first: the most recent DEFAULT_DEDUPE_MAX ids, or DEFAULT_DEDUPE_TTL_MS after it was first seen. An id older than the TTL (strictly more than the TTL ago) or pushed out by newer ids is first-seen again.

Thread safety. Every method is synchronized on this set and every eviction runs inside the mutation that caused it; the monitor is a leaf lock. Dirty tracking matches CloudOutbox: a mutation sets the flag and increments a modification count, and CloudStateStore clears the flag only if nothing changed since its snapshot.

Receipts (plan 22-21, S-59, S-65). An id may carry one receipt: the result payload the framework answered a player.link.completed with, so a redelivery is answered again from the record without asking the module. A receipt lives and dies with its id -- it is evicted by the same count bound and expires with the same TTL -- and is at most MAX_RECEIPT_BYTES of compact JSON; a larger one is not recorded.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    (package private) static final class 
    One remembered command id, when it was first seen and its receipt, if any.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private final LongSupplier
     
    (package private) static final int
    The default number of remembered ids; welcome.limits.commandDedupeMax overrides it (D-13, 22-08).
    (package private) static final long
    The default memory of one id, 24 h; welcome.limits.commandDedupeTtlSeconds overrides it (D-13).
    private boolean
     
    (package private) static final int
    The largest receipt recorded, 4 KiB of compact UTF-8 JSON (S-65).
    private int
     
    private long
     
    private final Map<String,com.google.gson.JsonObject>
    Id to its receipt; every key is also a key of seen.
    private final LinkedHashMap<String,Long>
    Id to first-seen time, in the order the ids were first seen.
    private long
     
  • Constructor Summary

    Constructors
    Constructor
    Description
    CommandDedupeSet(int maxEntries, long ttlMillis, LongSupplier clock)
     
  • Method Summary

    Modifier and Type
    Method
    Description
    private static void
    checkLimits(int maxEntries, long ttlMillis)
     
    (package private) void
    Forgets every id.
    (package private) void
    Clears the dirty flag unconditionally.
    (package private) void
    clearDirtyIfUnchanged(long expectedModCount)
    Clears the dirty flag only if nothing changed since expectedModCount was read.
    private boolean
     
    private boolean
    expire(long now)
     
    (package private) boolean
    Records id and reports whether it is new.
    (package private) static boolean
    fitsReceipt(com.google.gson.JsonObject receipt)
     
    (package private) boolean
     
    (package private) long
     
    (package private) com.google.gson.JsonObject
     
    (package private) boolean
    recordReceipt(String id, com.google.gson.JsonObject receipt)
    Records receipt for id, remembering the id as first seen now when it is not remembered yet (a completion is recorded only once it is answered).
    (package private) void
    Replaces the content with ids loaded from the state file, applying both bounds.
    (package private) void
    setLimits(int newMaxEntries, long newTtlMillis)
    Changes both bounds and applies them at once.
    (package private) List<CommandDedupeSet.Seen>
     
    private void
     

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • DEFAULT_DEDUPE_MAX

      static final int DEFAULT_DEDUPE_MAX
      The default number of remembered ids; welcome.limits.commandDedupeMax overrides it (D-13, 22-08).
      See Also:
    • DEFAULT_DEDUPE_TTL_MS

      static final long DEFAULT_DEDUPE_TTL_MS
      The default memory of one id, 24 h; welcome.limits.commandDedupeTtlSeconds overrides it (D-13).
      See Also:
    • MAX_RECEIPT_BYTES

      static final int MAX_RECEIPT_BYTES
      The largest receipt recorded, 4 KiB of compact UTF-8 JSON (S-65).
      See Also:
    • clock

      private final LongSupplier clock
    • seen

      private final LinkedHashMap<String,Long> seen
      Id to first-seen time, in the order the ids were first seen.
    • receipts

      private final Map<String,com.google.gson.JsonObject> receipts
      Id to its receipt; every key is also a key of seen.
    • maxEntries

      private int maxEntries
    • ttlMillis

      private long ttlMillis
    • dirty

      private boolean dirty
    • modCount

      private long modCount
  • Constructor Details

    • CommandDedupeSet

      CommandDedupeSet(int maxEntries, long ttlMillis, LongSupplier clock)
      Parameters:
      maxEntries - the most ids remembered, at least 1
      ttlMillis - how long one id is remembered, at least 1 ms
      clock - milliseconds since the Unix epoch
  • Method Details

    • checkLimits

      private static void checkLimits(int maxEntries, long ttlMillis)
    • firstSeen

      boolean firstSeen(String id)
      Records id and reports whether it is new.
      Parameters:
      id - a command's envelope id
      Returns:
      true the first time id is seen within both bounds, false for a repeat
    • setLimits

      void setLimits(int newMaxEntries, long newTtlMillis)
      Changes both bounds and applies them at once.
      Parameters:
      newMaxEntries - the most ids remembered, at least 1
      newTtlMillis - how long one id is remembered, at least 1 ms
    • snapshot

      Returns:
      the remembered ids with their first-seen times, oldest first
    • restore

      void restore(List<CommandDedupeSet.Seen> loaded)
      Replaces the content with ids loaded from the state file, applying both bounds. Marked dirty only when the bounds removed something, so a load alone does not cause a rewrite.
      Parameters:
      loaded - the ids, oldest first; a repeated id keeps its first occurrence
    • clear

      void clear()
      Forgets every id.
    • recordReceipt

      boolean recordReceipt(String id, com.google.gson.JsonObject receipt)
      Records receipt for id, remembering the id as first seen now when it is not remembered yet (a completion is recorded only once it is answered). A receipt over MAX_RECEIPT_BYTES is not recorded and the set is left unchanged.
      Parameters:
      id - the envelope id the receipt answers
      receipt - the result payload; not copied, so the caller must not mutate it afterwards
      Returns:
      true when recorded, false when the receipt is too large
    • receiptFor

      com.google.gson.JsonObject receiptFor(String id)
      Parameters:
      id - an envelope id
      Returns:
      its receipt, or null when the id has none or is no longer remembered (expired or evicted); the caller must not mutate it
    • fitsReceipt

      static boolean fitsReceipt(com.google.gson.JsonObject receipt)
      Parameters:
      receipt - a candidate receipt
      Returns:
      whether its compact UTF-8 JSON is at most MAX_RECEIPT_BYTES
    • isDirty

      boolean isDirty()
      Returns:
      whether anything changed since the last successful write
    • clearDirty

      void clearDirty()
      Clears the dirty flag unconditionally.
    • modCount

      long modCount()
      Returns:
      the modification count, for clearDirtyIfUnchanged(long)
    • clearDirtyIfUnchanged

      void clearDirtyIfUnchanged(long expectedModCount)
      Clears the dirty flag only if nothing changed since expectedModCount was read.
      Parameters:
      expectedModCount - the modCount() captured with the persisted snapshot
    • expire

      private boolean expire(long now)
    • evictToMax

      private boolean evictToMax()
    • touch

      private void touch()