Class RedisCoordinator

java.lang.Object
com.enhancedechest.crossserver.RedisCoordinator
All Implemented Interfaces:
CrossServerCoordinator

public final class RedisCoordinator extends Object implements CrossServerCoordinator
Redis implementation of CrossServerCoordinator: one lock key per owner (<prefix>lock:<uuid> holding this server's id, TTL-protected and heartbeat-extended) plus one pub/sub channel (<prefix>events) used to hand owners over quickly.

Lock lifecycle. acquireOwner(java.util.UUID) loops on SET NX PX; while another server holds the key it publishes a req message each round and waits for the matching rel (with a poll fallback), giving up with CrossServerLockException after the acquire timeout. The lock is never stolen from a live holder — a holder that crashed simply stops heartbeating and its keys expire after LOCK_TTL_MS. The heartbeat re-extends (or restores, after a Redis outage) every held key and logs loudly if a key turns out to be owned by someone else (split-brain signal, should never happen).

Handover. An incoming req for an owner this server holds is passed to the release-request handler (wired in the plugin bootstrap: flush + evict + release, provided the player is not online here and no chest session of theirs is still open or saving). Requesters re-publish every poll round, so a request that arrives too early — the player is still mid-disconnect here — is simply honored a round later; no state is kept for it.

All blocking work (acquire loops, Redis I/O) runs on the async storage executor or this class's own daemon threads — never on a tick thread.

  • Constructor Details

    • RedisCoordinator

      public RedisCoordinator(String host, int port, String password, boolean ssl, int database, String keyPrefix, String serverId, Scheduler scheduler, org.slf4j.Logger logger, Telemetry telemetry)
  • Method Details

    • init

      public void init()
      Verifies the connection (PING), then starts the pub/sub subscriber thread and the heartbeat. Throws on an unreachable/misconfigured Redis so the plugin can refuse to start in cross-server mode rather than run unsynchronized against a shared database.
    • setReleaseRequestHandler

      public void setReleaseRequestHandler(Consumer<UUID> handler)
      Wires the handler invoked when another server asks for an owner this server holds.
    • serverId

      public String serverId()
    • acquireOwner

      public void acquireOwner(UUID owner)
      Description copied from interface: CrossServerCoordinator
      Blocks until this server holds owner's lock. Called on the async storage executor by the cache's load-on-miss path, never on a tick thread. If another server holds the lock, that server is asked (pub/sub) to flush + hand the owner over, which it does as soon as the player has fully quit there; on a crashed holder the lock's TTL expires instead.
      Specified by:
      acquireOwner in interface CrossServerCoordinator
    • isHeld

      public boolean isHeld(UUID owner)
      Description copied from interface: CrossServerCoordinator
      Whether this server currently holds owner's lock (cheap, local — no network I/O). The cache re-checks this under its state lock right before flipping an owner resident, closing the race where an eviction releases the lock between a load's acquire and its apply.
      Specified by:
      isHeld in interface CrossServerCoordinator
    • isHeldElsewhere

      public boolean isHeldElsewhere(UUID owner)
      Description copied from interface: CrossServerCoordinator
      Whether owner's lock is currently held by a different server (network read). Used by the expiry sweep to skip candidates whose owner is online on another server — that server's own sweep handles them — instead of blocking a full acquire timeout on each.
      Specified by:
      isHeldElsewhere in interface CrossServerCoordinator
    • beginRelease

      public void beginRelease(UUID owner)
      Description copied from interface: CrossServerCoordinator
      Marks owner's lock as no longer held locally. Must be called inside the cache's state lock, in the same critical section that evicts the owner's rows. Pure local bookkeeping.
      Specified by:
      beginRelease in interface CrossServerCoordinator
    • finishRelease

      public void finishRelease(UUID owner)
      Description copied from interface: CrossServerCoordinator
      Completes a CrossServerCoordinator.beginRelease(java.util.UUID): deletes the lock key and notifies waiting servers. Called outside the cache's state lock (network I/O). Failures are logged, never thrown — a lost release degrades to the lock's TTL expiry.
      Specified by:
      finishRelease in interface CrossServerCoordinator
    • close

      public void close()
      Releases every held lock (after the caller has flushed the cache — see the onDisable ordering) and stops the subscriber, heartbeat and pool.