Releases: Hyshmily/hotkey
Release list
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
1.1.1
- Fallback Version Negative Space —
fallbackVersion()migrated toLong.MIN_VALUE + counter(negative space). This eliminates the bug where degraded versions suppressed normal broadcasts insendDedupednumeric comparison. Normal versions (positive) always dominate degraded versions (negative). - Invalidate Guard —
handleInvalidatenow includes ashouldSkipForSyncguard, preventing degradedINVALIDATEbroadcasts from incorrectly clearing normal L1 entries. - Worker HOT Promotion on Redis Unavailable — When Redis returns
nullduringhandleHot, the method promotes an existing degraded L1 entry (if present) to HOT, preventing permanent loss of Worker HOT decisions. - SyncMessage Default Fix —
SyncMessage.from()defaultisVersionDegradedchanged fromtruetofalse, preventing silent data sync failures when the header is missing. - Multi-Module Split —
hotkey:1.1.0refactored intohotkey-parent(pom) withcommon/andworker/modules. Thecommonartifact published to Maven Central;workeris a standalone Spring Boot app (maven.deploy.skip=true). Worker-only classes moved out of the shared artifact, reducing App-only footprint. - Coexistence Mode Removal — Removed
exclusive-modeandhotkey.worker.mode(COEXISTENCE/EMBEDDED/STANDALONE). Deployment simplified to two modes: App-only / Worker-only. Replaced condition annotation with@ConditionalOnProperty(prefix="hotkey.worker", name="enabled", havingValue="false", matchIfMissing=true). - Annotation Support — Added
annotation/package and@HotCacheannotation (renamed to avoid conflict withHotKeyfacade). Supports SpEL key(), three operation types (READ/WRITE/INVALIDATE), and per-annotation TTL/soft-expire overrides.HotKeyAspectinjectsHotKeyfacade; requiresspring-boot-starter-aop(<optional>true</optional>).HotKeyAnnotationAutoConfigurationregistered as the 9th auto-configuration class. - Worker Configuration Restructure —
WorkerProperties.javareorganized into 9 nested inner classes (Routing,Messaging,SlidingWindow,Threshold,StateMachine,DynamicThreshold,TopKValidation,HeavyKeeper) plus a flatenabledfield. Auto-configuration property access paths updated to nested getters; fixed@ScheduledSpEL; retained backward-compatible delegate methods. - Degraded Version Upgrade — Replaced
System.nanoTime()fallback with(nodeId << 32) | counter. UsesInstanceIdGenerator(UUID file + hostname fallback) to derive node ID and a sharedAtomicLong. Guarantees cross-JVM ordering without clock drift. - Full-Path Reporting —
get()/getWithSoftExpire()now callhotKeyReporter.record()on both L1 hit and L2 miss paths (previously only hot-key misses). Worker receives complete access patterns rather than only the hot-key subset. - Dependency Additions — Added
spring-amqpandspring-boot-starter-aopas<optional>true</optional>to the common module. Bumped Guava version to32.1.3-jrein<dependencyManagement>. AutoConfiguration.importsUpdate — RemovedWorkerAutoConfigurationfrom the shared imports file (now scanned by@SpringBootApplicationin worker module). AddedHotKeyAnnotationAutoConfiguration. Total 9 entries maintained.- Documentation & Readability — README deployment tables collapsed to two modes; Worker config reorganized with group header rows and nested YAML examples. Removed coexistence references from
CLAUDE.md/AGENTS.md.application.ymlpartitioned with# ── Section ──markers. Retained Apache 2.0 license headers.