Class CloudOutbox

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

final class CloudOutbox extends Object
The bounded buffer of reliable events not yet acknowledged by the UltiCloud gateway (D-11, D-14, D-15). An event gets its envelope id (a ULID) when it is enqueued, stays until an ack { ids } names it, and is resent after every reconnect.

Bounded. The buffer never holds more than its capacity (default DEFAULT_CAPACITY). Enqueueing into a full buffer, lowering the capacity, or loading more entries than fit drops the oldest entries and adds them to droppedSinceLastReport(), which the session reports once as outbox.overflow { dropped }. The count is reduced by the number actually reported (markOverflowReported(long)), so drops that happen while a report is in flight are kept for the next report.

Thread safety. Every method is synchronized on this buffer, and every eviction runs inside the same synchronized mutation that caused it. The monitor is a leaf lock: no other lock is taken while it is held.

Dirty tracking. Every mutation sets a dirty flag and increments a modification count; CloudStateStore clears the flag after a successful write only if nothing changed since its snapshot (clearDirtyIfUnchanged(long)), so a mutation that races a flush is never lost.

  • Field Details

    • DEFAULT_CAPACITY

      static final int DEFAULT_CAPACITY
      The default capacity; welcome.limits.outboxMax overrides it per connection (D-13, plan 22-08).
      See Also:
    • clock

      private final LongSupplier clock
    • entries

      private final LinkedHashMap<String,CloudOutbox.Entry> entries
    • capacity

      private int capacity
    • dropped

      private long dropped
    • dirty

      private boolean dirty
    • modCount

      private long modCount
  • Constructor Details

    • CloudOutbox

      CloudOutbox(int capacity, LongSupplier clock)
      Parameters:
      capacity - the maximum number of entries, at least 1
      clock - milliseconds since the Unix epoch, used for each event's ts
  • Method Details

    • requirePositive

      private static int requirePositive(int capacity)
    • enqueue

      CloudOutbox.Entry enqueue(String type, com.google.gson.JsonObject payload)
      Adds an event with a new ULID id and the current time, dropping the oldest entry when full.
      Parameters:
      type - the envelope type, for example player.join
      payload - the event payload; copied, so later changes by the caller do not reach the buffer
      Returns:
      the stored entry
    • unacked

      Returns:
      a snapshot of every unacknowledged entry, oldest first; later mutations do not change it
    • ack

      int ack(Collection<String> ids)
      Removes the entries the gateway acknowledged.
      Parameters:
      ids - the acknowledged envelope ids; unknown ids are ignored
      Returns:
      the number of entries removed; the dirty flag changes only if it is positive
    • droppedSinceLastReport

      long droppedSinceLastReport()
      Returns:
      the number of entries dropped for lack of space since the last reported overflow
    • markOverflowReported

      void markOverflowReported(long reported)
      Records that an outbox.overflow carrying reported was sent. Only that many are subtracted, so drops counted after the report was built remain for the next one.
      Parameters:
      reported - the dropped value that was sent
    • setCapacity

      void setCapacity(int newCapacity)
      Changes the capacity; a smaller capacity drops the oldest entries at once and counts them.
      Parameters:
      newCapacity - the new maximum, at least 1
    • clear

      void clear()
      Removes every entry and resets the dropped count.
    • restore

      void restore(List<CloudOutbox.Entry> loaded, long loadedDropped)
      Replaces the content with entries loaded from the state file. Entries beyond the capacity keep the newest and add the rest to the dropped count. The buffer is marked dirty only when the loaded content had to be trimmed and clean otherwise, so a load alone does not cause a rewrite.
      Parameters:
      loaded - the entries, oldest first
      loadedDropped - the persisted dropped count
    • 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
    • snapshot

      Returns:
      the entries, dropped count and modification count, read together under this buffer's monitor
    • evictToCapacity

      private boolean evictToCapacity()
    • touch

      private void touch()