Releases: Hyshmily/Zeta
Release list
1.1.55
Rebranding & Configuration
- Package rename —
io.github.hyshmily.hotkey→io.github.hyshmily.zeta, all class names and imports updated accordingly. - Configuration prefix —
hotkey.*→zeta.*(including local, sync, worker, report, spring‑cache, etc.). - Artifact IDs —
hotkey-parent→zeta-parent,hotkey→zeta,hotkey-worker→zeta-worker. - Exchange/queue names — Default exchange names (
zeta.report.exchange,zeta.heartbeat.exchange), queue prefixes, and Redis key prefixes all switched tozeta:*.
Architecture & Core Refactoring
- Interface/Impl split — Most concrete classes (e.g.,
SingleFlight,CircuitBreaker,ExpireManager,RingManager,HealthView,SreRateLimiter,VersionController,SystemLoadMonitor) are now public interfaces with package‑private implementation classes inimpl/sub‑packages. - CentralDispatcher — Replaces scattered direct calls to
CacheSyncPublisherandHotKeyReporter; provides unifiedrecordAccess()andsend()coordination. - CacheCompressor abstraction — Value‑level LZ4 compression/decompression via
Lz4CacheCompressor(falls back to no‑op if LZ4 unavailable). - Memory‑weighted eviction — New
CacheConfigwithmax‑weight(bytes) andDefaultWeigherto enforce memory‑based limits (overrides entry‑count when set). @Internalmarker — Annotates classes not intended for public API consumption.
New Capabilities
- Atomic
computeIfAbsent— Uses Caffeine'scompute()within stripe lock, eliminating TOCTOU race between lookup and load. - CAS operations —
compareAndSet()andcompareAndInvalidate()for lock‑free optimistic updates. - Batch
get—get(Iterable<String>, Function, ...)for bulk read‑through with per‑key TTL/report control. - Timed refresh scheduler —
registerRefresh(),updateRefresh(),unregisterRefresh()with a lazy 2‑thread pool for background periodic refresh. - Per‑key ordered dispatcher —
PerKeyOrderedDispatcherensures FIFO execution of tasks for the same key in bothCacheSyncListenerandWorkerListener. - Concurrent‑threads interception — New
InterceptTrigger.CONCURRENT_THREADSwithconcurrentThreads()attribute to guard per‑key thread count. - Broadcast control — All
invalidate(),putThrough(),invalidateAfterPut()methods now accept anisBroadcastByThisTimeparameter;invalidate(keys, false)replacesevictLocal(). - Multi‑key mutate‑invalidate — Batch
invalidateAfterPut(Map)overload for atomic invalidations.
Fixes & Improvements
- Worker broadcast rollback —
WorkerBroadcaster.broadcastHot/Cool()returnsboolean;ReportConsumercallsrollbackToPreviousState()when broadcast fails, preventing state drift. - INVALIDATE vs REFRESH race —
CacheSyncListenertracksrecentInvalidatedhigh‑water mark per key, skipping stale REFRESH after an INVALIDATE. - Stale‑return on circuit‑breaker open —
HotKeyCache.mapEmpty()now returns the staleCacheEntry(if available) when circuit‑breaker is open, instead ofOptional.empty(). - ExpireManager centralization — All
CacheEntryconstruction flows throughExpireManager.createBuilder(),applyTtl(),replaceEntryValue(), unifying TTL logic and compression.
Dependency & Build Changes
- New dependencies —
org.lz4:lz4‑java:1.8.0(compression),org.apache.lucene:lucene‑core:10.5.0(root POM) and9.12.0(common POM – version mismatch, likely intentional for compatibility). - JaCoCo excludes — Updated package paths from
io/github/hyshmily/hotkey/*toio/github/hyshmily/zeta/*. - Main class —
WorkerApplicationmoved toio.github.hyshmily.zeta.worker.
Removed / Deprecated
evictLocal()— Replaced byinvalidate(key, false).putBeforeInvalidate()— Replaced byinvalidateAfterPut()with broadcast control.- Batch
computeIfAbsent(Collection, Function, ...)— Removed; use single‑key atomics plus new batchget()instead. HotKey*classes — All renamed toZeta*(e.g.,HotKeyCacheStats→ZetaCacheStats,HotKeyDecision→ZetaDecision).- Old endpoint paths —
/actuator/hotkey→/actuator/zeta,/actuator/hotkey/worker/state→/actuator/zeta/worker/state.
v1.1.54
Highlights
- Circuit breaker — New
HotKeyCircuitBreakerprotects remote cache-load suppliers;SingleFlightchecksallowRequest()before queuing; returns stale Caffeine entry on open breaker. - Null-value caching —
NullValue.INSTANCEcached with configurable TTL (default 10s) when reader returns empty. - HeavyKeeper rewrite — Sliding-window ring buffer (
windows[]+slotSums[]) replaces flatlong[];ConcurrentHashMap<String, Node>+ReentrantLockfor TopK membership (hot path lock-free);LongAccumulatorfor striped count write;locCachememoizes hash+fingerprint+bucket indices;volatile long minPqCountfor O(1) fast-rejection; 65536-entry decay LUT; adaptive stripe count; paralleladdDirect()batch feed. - BufferedCounter replaces Caffeine for reporter counters; configurable buffer/flush/eagerSwap params;
HotKeyThreadFactorynaming. - Parallel ReportConsumer —
parallelStream()chunked (1000 keys); batchaddDirect()TopK feed; pre-allocatednextDecisionVersion(); serial drain avoids AMQP channel contention; fire-and-forget broadcast. - State machine cool 15s→10min —
coolDurationMs15000→600000,preCoolGraceMs5000→60000. - SlidingWindowDetector memory —
AtomicLong[]→AtomicLongArray; ~94% reduction (80MB→5MB/100k keys). - Cluster health simplified — Removed
degradedflag, manual ring override;minAliveWorkersquorum. Heartbeat defaults: timeout 30s→10s, verify 1500ms→5000ms.
Breaking / API
- CacheLoader SPI —
Function<String, Object>→CacheLoaderinterface. - RingManager auto-mode only;
addNode()/removeNode()/resetToAuto()/isManualMode()removed. - HotKeyEndpoint — Actuator
@Endpoint→ Spring MVC@RequestMapping. ttlJitterEnabledremoved — jitter always on; ratio 0.1→0.05.- ClusterHealthView constructor signature changed (no
degradedparam).
Infrastructure
- TimeSource — Caches
System.currentTimeMillis()on daemon thread (5ms tick); all hot-path expiry migrated. - HotKeyThreadFactory — Daemon, named,
NORM_PRIORITY; replaces all ad-hocThreadcreation. - Dedicated
hotKeySyncScheduler— Isolated from reporter flush scheduler. @Scheduledremoved — Tasks useScheduledExecutorService.scheduleWithFixedDelay()directly.
v1.1.53
Full-Link Optimization & Bug Fixes
-
isLocalHotKey() HeavyKeeper fallback — When L1 cache entry is a
CacheEntrywithKeyState.NORMAL, added a fallback check tohotKeyDetector.contains()to prevent local detection from always returning false before Worker broadcast arrives. -
Reporter flush lost counters on empty route — Changed
sumThenReset()to first callsum()to read, then confirm route, thensumThenReset()to prevent burst counters from being silently discarded when Worker is not ready. -
BBR maxInFlight floor fix —
maxInFlight()now returnsMath.max(1, floor(...))to prevent BBR deadlock whenminRTis too small (resulting in 0). -
ThresholdLearner dynamic threshold only rises — Lower bound changed from
Math.max(10, ...)toMath.max(hotThreshold, ...)to prevent lowering a fixed threshold of 1000 to 20 under low QPS. -
Worker heartbeat robustness with DependsOn("rabbitAdmin") —
WorkerHeartbeatProducernow waits forRabbitAdminto complete exchange declaration before starting heartbeat sending, eliminating the firstNOT_FOUNDerror. -
initialDelay=pingIntervalMs — First heartbeat delay changed from 0 to
pingIntervalMs(default 1s), working with@DependsOnto allow Worker to start independently. -
StateMachine independent time slicing — StateMachine now has its own
sm-duration-ms(500) andsm-slices(10), independent of the sliding-window detector.confirmCount = ceil(100 / 50) = 2. -
heartbeat.timeout-ms default — Changed from 3000 to 30000 (30 seconds) for production stability.
-
sliding-window.duration-ms default — Changed from 500 to 1000 (1 second) to cover intermittent burst hotspots.
-
warmup-jitter-ms (sync) default — Changed from 100 to 50 (50 milliseconds).
-
warmup-jitter-ms (worker-listener) default — Changed from 100 to 50 (50 milliseconds).
-
confirm-duration-ms default — Changed from 150 to 100 (2-window confirmation).
v1.1.51
Distributed Lock Support
- LockProvider — New
LockProviderinterface definingtryLock(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 PXwith UUID-based safe release. Exponential backoff retry, configurable lock/inquiry/unlock counts. Inner classRedisLockHandleimplementsAutoReleaseLock. - HotKeyLockAutoConfiguration — Auto-wires
LockProviderbean whenStringRedisTemplateis available. Registered inAutoConfiguration.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). - HotKeyProperties — 3 new config properties:
tryLockLockCount(default 2),tryLockInquiryCount(default 1),tryLockUnlockCount(default 2). - HotKeyConstants — New
REDIS_LOCK_KEY_PREFIX = "hotkey:lock:".
Deployment Mode Enforcement
- HotKeyModeException — New
RuntimeExceptionwithoperation,currentMode,requiredModefields. Thrown when a HotKey API is called in an unsupported deployment mode. - requireAppCache / requireAppDetector — Former single
requireCache()throwingUnsupportedOperationExceptionreplaced by two methods, both throwingHotKeyModeException:requireAppCache(operation)guards L1 cache operations;requireAppDetector(operation)guards local TopK detector operations. Operation name strings provide precise error messages. - Mode query methods — New public methods:
isApp()(cache available),isWorker()(Worker TopK available),isAppOnly()(pure App mode),isWorkerOnly()(pure Worker mode). - currentModeLabel() — New private helper generating human-readable deployment mode labels (
"App-only mode","Worker-only mode","Coexistence mode","Uninitialized mode"). - Stricter read method guards —
get(),getWithSoftExpire(), etc. now call bothrequireAppCache()andrequireAppDetector()(reference only checked cache). - notifyLocalDetector guards — All 5
notifyLocalDetector*()methods now callrequireAppDetector()instead of NPE-ing in Worker-only mode as in the reference. - computeIfAbsent simplification —
computeIfAbsent()andcomputeIfAbsentWithSoftExpire()convenience overloads no longer callrequireAppCache()directly — they delegate to innerget()/getWithSoftExpire()which perform their own guard checks.
invalidateAll Rename
- invalidateAll → invalidateAllLocal — The no-arg
invalidateAll()method renamed toinvalidateAllLocal(), emphasizing local-only clear semantics (no broadcast).invalidateAll(String...)andinvalidateAll(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(), andpeek()now wrap their logic in try-catch:HotKeyBlockedExceptionre-thrown as-is; all otherRuntimeExceptionlogged and returnOptional.empty(). Prevents internal failures from propagating to callers. - Writer exception handling —
putThrough()now wrapswriter.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 callsVersionGuard.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
executedfield initializedAtomicBoolean(false).execute()checkscompareAndSet(false, true); throwsIllegalStateException("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
syncSchedulerare 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)withincomingNodeIdandincomingEpochparameters. Logic:- Incoming epoch > existing epoch → accept (Worker restart — ADR-0010)
- Incoming epoch < existing epoch → skip (stale)
- 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 withnullnodeId and0epoch.
CacheEntry Epoch Tracking
- New field decisionNodeId —
private final String decisionNodeId. Tracks which Worker node produced the decisionVersion. Used byVersionGuard.shouldSkipForWorkerfor per-Worker version space partitioning. - New field decisionEpoch —
private final long decisionEpoch. Tracks Worker restart counter epoch. Used byVersionGuard.shouldSkipForWorkerfor restart detection (ADR-0010).
VersionController Lua Script Optimization
- Pre-compiled script — Lua script
INCR_SCRIPTnow defined as a static final field instead of creating a newDefaultRedisScriptobject on everynextVersion()call. Avoids re-creating script object on every version allocation. - Simplified script content — Always calls EXPIRE after INCR now (conditional
if tonumber(ARGV[1]) > 0check 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 throughNullValuesentinel insideCacheEntrywithout a separate keyset. Removesimport java.util.Setandimport java.util.concurrent.ConcurrentHashMap.
ClusterHealthView Dynamic Worker Count
- knownWorkerCount made mutable —
knownWorkerCountchanged fromfinaltovolatilewith setter.@RequiredArgsConstructorremoved; explicit constructor added. - isClusterHealthy() fallback — When
knownWorkerCount <= 0,isClusterHealthy()now returnsaliveCount > 0(falls back to observed alive Workers) instead of hardcodedfalseas in reference. Enables backward compatibility. - Getter added —
@Getteradded onknownWorkerCount.
Package Reorganization
- HeavyKeeper typo fix — Package renamed from
hotkeydetector/heavykepper/(double p) tohotkeydetector/heavykeeper/. All imports updated. - Sync package split — Flat
sync/package split intosync/local/(SyncMessage, CacheSyncPublisher, CacheSyncListener, CacheSyncProperties),sync/worker/(WorkerMessage, WorkerListener, WorkerListenerProperties, WorkerHeartbeatVerifier, WorkerHeartbeatMessage), andsync/distributedlock/(LockProvider, AutoReleaseLock, impl/RedisLockProvider). All imports updated. - Version classes moved —
VersionController.javaandVersionGuard.javamoved fromsync/toutil/version/. - ClusterHealthView moved —
ClusterHealthView.javamoved fromsync/tosharding/.
Transaction Support Documentation
- Rollback behavior —
TransactionSupportJavadoc adds<p><b>Rollback behavior:</b>section documenting thatrunNowOrAfterCommitactions are NOT executed on transaction rollback.
1.1.5-SNAPSHOT
Broadcast Suppression Annotation
- Broadcast — New
@Target(METHOD)annotation withboolean value() default true. When set tofalse, suppresses cross-instance cache sync messages. Supported on@Cacheable,@CachePut, and@CacheEvict. - HotKeyCacheContext — New
skipBroadcastfield andisSkipBroadcast()getter. Context stores flag when any companion annotation is non-default. Applied byHotKeyCacheExtensionAspectbefore method execution. - HotKeyCacheExtensionAspect — New
@Around("@annotation(cachePut)")and@Around("@annotation(cacheEvict)")pointcuts. Reads@Broadcastand setsskipBroadcastin context. ExistingaroundCacheable()also reads@Broadcast. - HotKeySpringCache —
put(),evict(), and null-caching path inget(key, loader)checkisSkipBroadcast(). When true, callsputLocal()/evictLocal()instead ofputThrough()/invalidate(), avoiding broadcast, version bump, and hot-key detection. - HotKeyCacheTTL —
@Targetexpanded to includeElementType.TYPE, allowing class-level TTL defaults.
1.1.5.Beta
Fluent Cache Read/Write API
- HotKeyReadQuery — Chain-style multi-level reads with
withPrimary,thenExecute,allowNull,allowBroadcast, andorElseThisTime. Preserves L1 caching, hot-key detection, and SingleFlight via HotKey core API. - HotKeyWriteCommand — Fluent write operations (
putThrough,putBeforeInvalidate,invalidate) with TTL overrides. All commands automatically enforce blacklist rules.
Local-Only Writes
- putLocal — Added to
HotKeyCacheandHotKeyfacade. Writes to local Caffeine without version bump, broadcast, or hot-key detection. Preserves existing entry metadata.
Broadcast Control
- allowBroadcast / notAllowBroadcast — Callers control whether fallback results are broadcast via
putThrough. Default isfalse(no broadcast), keeping reads silent by default.
Blacklist & Null Handling Fixes
- Blacklist Consistency —
putThroughandputBeforeInvalidatenow throwHotKeyBlockedExceptionon blocked keys, matching the behavior ofget/peek. - Null Sentinel Unification — All internal null caching uses
NullValue.INSTANCEinputThroughand fallback caching, avoiding Caffeine null rejection.
Documentation Clarifications
- No Broadcast on Reads —
getandgetWithSoftExpirenever broadcast; onlyputThrough/invalidateand explicitlyallowBroadcast-enabled fallbacks trigger REFRESH.
1.1.4-SNAPSHOT
Dual-Buffer Counter
- Lock-Free Batching Layer — Adds a dual-buffer counter before HeavyKeeper. Per-
get()add(key, 1)calls are merged into periodic (100-500ms)add(key, N)batch submissions. Reduces lock contention from O(N) to O(1) under ultra-high concurrency (millions QPS). - Transparent to Callers — No API changes required. The batching layer operates silently, preserving existing
get()semantics while dramatically reducing HeavyKeeper’s synchronization overhead. - Inspired by HotCaffeine — Adopts the proven dual-buffer design from HotCaffeine’s
DefaultKeyCounter, adapted for HotKey’sHeavyKeeperintegration.
Reporter Self-Protection (BBR + CPU Fusion)
- BBR + CPU Fusion — Integrated BBR congestion control with CPU EMA into
Reporterflush.BbrRateLimiter.tryAcquire()decides to admit or drop each batch based on concurrency budget and CPU load. - Two‑Zone Decision — Below 80% CPU: permissive (admit if concurrency within budget OR not in cooldown). Above 80%: strict (admit only if concurrency within budget). Prevents overload amplification.
- CPU EMA — Smoothed via
cpuDecay=0.95, 500ms interval, avoids thrashing on spikes. - BBR Sliding Window — 10s window, 100 buckets, tracks max pass rate & min RTT to compute safe concurrency limit.
- Backpressure — Drops logged at
DEBUG, metrichotkey.reporter.bbr.dropped. Self‑throttles before RabbitMQ/Worker overload. - Default Enabled —
hotkey.local.reporter.enabled=true. Zero overhead when healthy.
1.1.4
Version & Platform
- JDK 17 Downgrade — Minimum Java version lowered from 25 to 17. Replaced JDK 21+ APIs (
Thread.startVirtualThread(), unnamed_lambda parameters) with JDK 17-compatible equivalents. - Version Bump — All POMs, READMEs, and metadata updated to 1.1.4.
Consistent Hash Ring (ADR 0005, 0006, 0007)
- Consistent Hash Ring — New sharding layer using Guava
murmur3_32_fixed()withTreeMap+ virtual nodes (default 500/node). Lock-free reads via volatile ring snapshot. Collision probability <3% with 500 vnodes × 100 Workers. RingManager— Orchestrates ring with auto (heartbeat-discovered) and manual (operator override) modes. Heartbeat timeout: 5s default.WorkerHealthMonitorEliminated — Replaced byRingManager. Node identity migrated fromint shardIndextoString nodeIdacross all modules (WorkerMessage, report queues, heartbeats).- Key-Based Worker Routing —
routeNode(key)maps cache keys to responsible Worker nodes via consistent hash ring with liveness predicate.
Decentralized Config Gossip (ADR 0003)
- Decentralized Config Gossip —
WorkerConfigNegotiatorlistens for heartbeat-borne state-machine config from peers. Applies when remote timestamp > local. Falls back toWorkerPropertiesdefaults if no heartbeat within 3s. - Heartbeat Enriched with Config —
broadcastHeartbeat()now carriesconfirmCount,coolCount,preCoolGraceCount, and monotonicconfigTimestampin AMQP headers. No dedicated config store needed. configTimestampCounter— SharedAtomicLongfor config-change ordering across the Worker cluster.
Graceful Degradation (ADR 0001, 0009)
- Local Promotion with Worker-Aware Fallback —
promoteLocalHotkeyIfNeeded()upgradesNORMALalways;COOLonly when all Workers dead. Local TopK takes over during Worker outage; self-heals viadecisionVersionon Worker recovery. - Graceful Degradation Path — When
isAnyWorkerAlive() == false:COOLentries become promotable,Reporterdrops batches silently, Worker recovery auto-reasserts authority. - Null-Safe
evaluateRule()— ReturnsALLOWinstead of throwing whenhotKeyCacheis null (Worker-only mode).
Dual Version Space (ADR 0008)
- Two-Version Architecture —
dataVersion(RedisINCR, may degrade to node-local counter) for cross-instance sync;decisionVersion(WorkerAtomicLong, never degrades) forHOT/COOLordering. Orthogonal. VersionGuardRefactored — Overloaded methods acceptingCacheEntrydirectly for use insidecompute()blocks, eliminating redundantgetIfPresentcalls.- Degraded Comparison — 4-case comparison prevents degraded
dataVersionfrom overwriting normal ones across instances.
Performance & Concurrency
- Atomic Cache Operations — All writes use
caffeineCache.asMap().compute()instead of blindput(). Worker-managed entries (HOT/COOL) preserved viaisWorkerManagedEntry()guard. SingleFlightException-Only Invalidate (ADR 0002) — RemovedwhenCompleteinvalidate; only catch-block invalidates. Normal completions stay cached via CaffeineexpireAfterWrite.- Batch Invalidation Chunking — Large key sets split into 1000-key AMQP messages to prevent oversized message rejection.
- SpEL Expression Caching — Parsed
Expressionobjects cached inConcurrentHashMapacross invocations. - Fallback Method Caching — Resolved methods cached in
ConcurrentHashMapto avoid repeated reflection. CacheExpireManagerJitter — Hard/soft expire timestamps apply ±10% jitter viaThreadLocalRandomto prevent thundering herd.- Graceful Executor Shutdown —
ThreadPoolTaskExecutorwaits for in-flight tasks (60s timeout). - Prefetch Count — Both listener containers set
prefetchCount=5for backpressure control.
Composable Annotation System
HotKeyCacheTTL— Per-method hard/soft TTL override. ReplacesHotKey.hardTtlMs()/.softTtlMs().Intercept— READ marker: hot key → skip original method, returnFallbackorpeek().Fallback— Declares fallback value/method for READ. ReplacesHotKey.fallbackEnabled()/.fallback(). Triggered on blacklist block,Intercepthot key, or cache loader exception.condition()SpEL — When false, cache bypassed entirely.unless()SpEL — Evaluated after cache load;#resultavailable. Result accepted as-is, cache still used.HotKeyAspectRewrite — 281→391 lines. 7-step priority chain: blacklist → condition → unless → notifyLocalDetector → intercept → TTL resolution → cache lookup.- Blacklist Integration —
RuleAction.BLOCKsupport withHotKeyBlockedException.
Micrometer & Actuator
HotKeyMicrometerAutoConfiguration— 17+ custom gauges:hotkey.topk.*,hotkey.reporter.*(queue depth/dropped/expired/pending),hotkey.singleflight.*,hotkey.version.degraded,hotkey.sync.dedup,hotkey.worker.alive,hotkey.worker.tracked, plus Caffeine L1 metrics.RingEndpoint—/actuator/hotkeyringwith full CRUD (GET, POST addNode, DELETE removeNode, POST rebuild).StateMachineEndpoint—/actuator/hotkey/worker/statewith GET (config) and POST (updateconfirmCount/coolCount/preCoolGraceCount). Changes propagate via heartbeat gossip.
Expanded Public API
getLocalCache()— Exposes raw Caffeine Cache for introspection.notifyLocalDetector()— Feeds local TopK without Worker report.isWorkerHotKey()— O(K) list iteration → O(1)contains().
Configuration Changes
confirm-duration-msDefault — 2000 → 300. HOT promotion within 300ms (was 2s).evict-interval-ms— New (default 30000) for dedicated eviction.shard-count/shard-indexRemoved — Replaced by auto-generatednodeId.- Consistent Hashing Properties — New
enabled(default true) andvirtual-nodes(default 500). ConditionalOnPropertyPrefix —hotkey→hotkey.annotation(functionally equivalent).
AMQP Improvements
- JSON Message Converter —
Jackson2JsonMessageConverterfor report messages (was Java serialization). AmqpExceptionHandling — New try-catch inCacheSyncPublisherandWorkerListener.- Ack-Before-Update (ADR 0004) — Ack before cache write for at-most-once delivery; self-healing via periodic broadcast.
- Shared
ObjectMapper—static finalinstance instead of per-call allocation.
Documentation
- 9 ADRs —
docs/adr/0001–0009covering all architecture trade-offs. ANNOTATION.md— Composable annotation system docs.CONTEXT.md— Domain glossary with canonical term definitions.- Full Javadoc — Every public class, method, and field documented.
Test Coverage
- 3 New Suites —
MicrometerAutoConfigurationTest,ConsistentHashRingTest,RingManagerTest. HotKeyEndpointTest— 2→9 tests.- Total — 379 passing (332 common + 47 worker).
1.1.3
- Version Cohesion — Unified version management: Eliminates version comparison bugs across Redis and local fallback.
- Increase Worker Heartbeat Mechanism — Added heartbeat mechanism for Worker nodes to monitor liveness and detect failures.
- Improve Endpoint Calls — Enhanced Actuator endpoints with additional metrics: report queue depth, capacity, expired count, and queue full count. Provides better observability for backpressure and system health.
1.1.2
- Report Backpressure Mechanism —
HotKeyReporter.flush()changed to enqueue viaLinkedBlockingQueue.offer(timeout), no longer direct publish. When queue is full, after 100ms timeout it discards with WARN, backpressure naturally flows to Caffeine 30s eviction for rate limiting. - Dedicated Consumer Thread Pool —
HotKeyReporterstarts N daemon consumer threads (max(1, shardCount/2), configurableconsumerCount) consuming from bounded queue and publishing to RabbitMQ. Each consumer checks 5s expiration when dequeuing, silently discarding expired entries. - New Configuration Properties —
HotKeyPropertiesaddedqueueCapacity(default 10000),queueOfferTimeoutMs(default 100),consumerCount(default 0 = auto), prefixhotkey.local.*. - Actuator Queue Metrics —
HotKeyEndpointadded 4 monitoring fields:reportQueueDepth,reportQueueCapacity,reportExpiredCount,reportQueueFullCount, injected viaObjectProvider<HotKeyReporter>intoHotKeyActuatorAutoConfiguration. - Reporter Lifecycle Management —
HotKeyReportAutoConfigurationusesinitMethod = "start"anddestroyMethod = "stop"to manageHotKeyReporterdispatcher threads. - Negative Shard Routing Fix —
HotKeyReportershard routing changed fromMath.abs(key.hashCode()) % shardCounttoMath.floorMod(key.hashCode(), shardCount). EliminatesInteger.MIN_VALUEedge case whereMath.abs(Integer.MIN_VALUE)returns negative causing array index out of bounds. - Checked Exception Propagation Fix —
HotKeyAspectreplacedthrow new RuntimeException(e)with Lombok-freesneakyThrow(e), preserving original checked exception types throughSupplier/Runnablelambdas inhandleReadandhandleWrite, so callers don't need to catchRuntimeExceptionwrapper. - Fixed known bugs