You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
LockProvider — New LockProvider interface defining tryLock(key, expire, unit) and overloads with explicit retry counts. AutoReleaseLock (@FunctionalInterface extends AutoCloseable) for try-with-resources usage.
RedisLockProvider — Redis-backed implementation using SET NX PX with UUID-based safe release. Exponential backoff retry, configurable lock/inquiry/unlock counts. Inner class RedisLockHandle implements AutoReleaseLock.
HotKeyLockAutoConfiguration — Auto-wires LockProvider bean when StringRedisTemplate is available. Registered in AutoConfiguration.imports. Graceful degradation via @ConditionalOnClass (no-op when Redis absent).
HotKey Facade — New 4th constructor parameter LockProvider (nullable). New public methods: tryLock(key, expire, unit), tryLock(key, expire, unit, lockCount, inquiryCount, unlockCount), tryLockAndRun(key, expire, unit, action), tryLockAndRun(key, expire, unit, action, lockCount, inquiryCount, unlockCount).
HotKeyConstants — New REDIS_LOCK_KEY_PREFIX = "hotkey:lock:".
Deployment Mode Enforcement
HotKeyModeException — New RuntimeException with operation, currentMode, requiredMode fields. Thrown when a HotKey API is called in an unsupported deployment mode.
requireAppCache / requireAppDetector — Former single requireCache() throwing UnsupportedOperationException replaced by two methods, both throwing HotKeyModeException: requireAppCache(operation) guards L1 cache operations; requireAppDetector(operation) guards local TopK detector operations. Operation name strings provide precise error messages.
Stricter read method guards — get(), getWithSoftExpire(), etc. now call both requireAppCache() and requireAppDetector() (reference only checked cache).
notifyLocalDetector guards — All 5 notifyLocalDetector*() methods now call requireAppDetector() instead of NPE-ing in Worker-only mode as in the reference.
computeIfAbsent simplification — computeIfAbsent() and computeIfAbsentWithSoftExpire() convenience overloads no longer call requireAppCache() directly — they delegate to inner get()/getWithSoftExpire() which perform their own guard checks.
invalidateAll Rename
invalidateAll → invalidateAllLocal — The no-arg invalidateAll() method renamed to invalidateAllLocal(), emphasizing local-only clear semantics (no broadcast). invalidateAll(String...) and invalidateAll(Collection) remain unchanged (varargs overloads still exist — they broadcast batch invalidations).
putLocal silent no-op — All three putLocal() overloads now silently return in Worker-only mode (if (hotKeyCache == null) return;) instead of throwing. Design choice — local writes should always be safe.
HotKeyCache Resilience and Correctness
Read path try-catch — get(), getWithSoftExpire(), and peek() now wrap their logic in try-catch: HotKeyBlockedException re-thrown as-is; all other RuntimeException logged and return Optional.empty(). Prevents internal failures from propagating to callers.
Writer exception handling — putThrough() now wraps writer.run() in try-catch; logs error and skips cache update when writer throws. Fixes partial-write issue where cache was updated despite data source failure.
buildPutThroughEntry version guard — buildPutThroughEntry() now calls VersionGuard.shouldSkipForSync(). If existing entry's dataVersion is already >= the new version, the existing entry is preserved unchanged. Prevents stale writes from overwriting newer data.
Conditional TOCTOU re-read — Re-reads from cache only when side effects actually occurred (real promotion or report not skipped), instead of always re-reading. Minor performance optimization.
HotKeyReadQuery Single-Execution Guard
AtomicBoolean executed — New executed field initialized AtomicBoolean(false). execute() checks compareAndSet(false, true); throws IllegalStateException("HotKeyReadQuery can only be executed once") if already invoked on the same builder. Prevents accidental double execution.
CacheSyncListener Async Error Handling
try-catch wrapping async task — Runnable tasks scheduled on syncScheduler are now wrapped in try-catch with logging of type/key/version. Catches uncaught exceptions that would otherwise be swallowed.
VersionGuard Worker Epoch + NodeId
shouldSkipForWorker 5-arg signature — New shouldSkipForWorker(CacheEntry, long, String, long) with incomingNodeId and incomingEpoch parameters. Logic:
Same epoch, same nodeId → normal ordering (existing >= incoming → skip)
Different nodeId, same epoch → accept (cross-Worker ownership transfer)
shouldSkipForWorker Cache-level overload — New 5-arg shouldSkipForWorker(Cache, String, long, String, long) overload. 3-arg version now delegates to 5-arg with null nodeId and 0 epoch.
CacheEntry Epoch Tracking
New field decisionNodeId — private final String decisionNodeId. Tracks which Worker node produced the decisionVersion. Used by VersionGuard.shouldSkipForWorker for per-Worker version space partitioning.
New field decisionEpoch — private final long decisionEpoch. Tracks Worker restart counter epoch. Used by VersionGuard.shouldSkipForWorker for restart detection (ADR-0010).
VersionController Lua Script Optimization
Pre-compiled script — Lua script INCR_SCRIPT now defined as a static final field instead of creating a new DefaultRedisScript object on every nextVersion() call. Avoids re-creating script object on every version allocation.
Simplified script content — Always calls EXPIRE after INCR now (conditional if tonumber(ARGV[1]) > 0 check removed).
HotKeySpringCache nullCachedKeys Removal
nullCachedKeys set removed — Entire nullCachedKeys (ConcurrentHashMap.newKeySet()) removed. lookup() no longer checks it, put()/evict()/clear() no longer manage it, get(Object, Callable) no longer populates it. Null caching behavior now handled entirely through NullValue sentinel inside CacheEntry without a separate keyset. Removes import java.util.Set and import java.util.concurrent.ConcurrentHashMap.
ClusterHealthView Dynamic Worker Count
knownWorkerCount made mutable — knownWorkerCount changed from final to volatile with setter. @RequiredArgsConstructor removed; explicit constructor added.
isClusterHealthy() fallback — When knownWorkerCount <= 0, isClusterHealthy() now returns aliveCount > 0 (falls back to observed alive Workers) instead of hardcoded false as in reference. Enables backward compatibility.
Getter added — @Getter added on knownWorkerCount.
Package Reorganization
HeavyKeeper typo fix — Package renamed from hotkeydetector/heavykepper/ (double p) to hotkeydetector/heavykeeper/. All imports updated.
Sync package split — Flat sync/ package split into sync/local/ (SyncMessage, CacheSyncPublisher, CacheSyncListener, CacheSyncProperties), sync/worker/ (WorkerMessage, WorkerListener, WorkerListenerProperties, WorkerHeartbeatVerifier, WorkerHeartbeatMessage), and sync/distributedlock/ (LockProvider, AutoReleaseLock, impl/RedisLockProvider). All imports updated.
Version classes moved — VersionController.java and VersionGuard.java moved from sync/ to util/version/.
ClusterHealthView moved — ClusterHealthView.java moved from sync/ to sharding/.
Transaction Support Documentation
Rollback behavior — TransactionSupport Javadoc adds <p><b>Rollback behavior:</b> section documenting that runNowOrAfterCommit actions are NOT executed on transaction rollback.