Class RedisCoordinator
- All Implemented Interfaces:
CrossServerCoordinator
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.
-
Field Summary
Fields inherited from interface com.enhancedechest.crossserver.CrossServerCoordinator
NOOP -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidacquireOwner(UUID owner) Blocks until this server holdsowner's lock.voidbeginRelease(UUID owner) Marksowner's lock as no longer held locally.voidclose()Releases every held lock (after the caller has flushed the cache — see theonDisableordering) and stops the subscriber, heartbeat and pool.voidfinishRelease(UUID owner) Completes aCrossServerCoordinator.beginRelease(java.util.UUID): deletes the lock key and notifies waiting servers.voidinit()Verifies the connection (PING), then starts the pub/sub subscriber thread and the heartbeat.booleanWhether this server currently holdsowner's lock (cheap, local — no network I/O).booleanisHeldElsewhere(UUID owner) Whetherowner's lock is currently held by a different server (network read).serverId()voidsetReleaseRequestHandler(Consumer<UUID> handler) Wires the handler invoked when another server asks for an owner this server holds.
-
Constructor Details
-
RedisCoordinator
-
-
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
Wires the handler invoked when another server asks for an owner this server holds. -
serverId
-
acquireOwner
Description copied from interface:CrossServerCoordinatorBlocks until this server holdsowner'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:
acquireOwnerin interfaceCrossServerCoordinator
-
isHeld
Description copied from interface:CrossServerCoordinatorWhether this server currently holdsowner'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:
isHeldin interfaceCrossServerCoordinator
-
isHeldElsewhere
Description copied from interface:CrossServerCoordinatorWhetherowner'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:
isHeldElsewherein interfaceCrossServerCoordinator
-
beginRelease
Description copied from interface:CrossServerCoordinatorMarksowner'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:
beginReleasein interfaceCrossServerCoordinator
-
finishRelease
Description copied from interface:CrossServerCoordinatorCompletes aCrossServerCoordinator.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:
finishReleasein interfaceCrossServerCoordinator
-
close
public void close()Releases every held lock (after the caller has flushed the cache — see theonDisableordering) and stops the subscriber, heartbeat and pool.
-