• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Hyshmily / hotkey / 27890525626

21 Jun 2026 02:07AM UTC coverage: 93.615% (-0.1%) from 93.742%
27890525626

push

github

Hyshmily
feat: cross-Worker decisionVersion partitioning with nodeId/epoch + expectedWorkerCount

Core changes:
- VersionGuard: 4-param/5-param overloads with nodeId/epoch for per-Worker
  version space isolation
- WorkerMessage: add nodeId/epoch fields, broadcast as AMQP headers
- WorkerListener: propagate nodeId/epoch through DCL into CacheEntry
- CacheEntry: add decisionNodeId/decisionEpoch fields
- WorkerBroadcaster: stamp every HOT/COOL broadcast with nodeId + epoch
- WorkerAutoConfiguration: @Bean workerNodeId(), workerEpochCounter()
- HotKeyConstants: AMQP_HEADER_EPOCH constant

Cluster health:
- HotKeyProperties: expectedWorkerCount (default 0=dynamic)
- ClusterHealthView: majority-quorum (> expectedWorkerCount / 2) health check
- Wire through HotKeyAutoConfiguration, HotKeyAmqpAutoConfiguration,
  HotKeyRedisAutoConfiguration

Production hardening:
- CacheExpireManager: CompletableFuture.orTimeout() with
  RejectedExecutionException catch to prevent stuck refresh markers
- HotKeyStateMachine: updated state transitions
- RingManager: consistent-hash ring refinements
- ReportConsumer: ingest path improvements

Signed-off-by: Hyshmily <cxm8607@outlook.com>

1133 of 1265 branches covered (89.57%)

Branch coverage included in aggregate %.

149 of 160 new or added lines in 15 files covered. (93.13%)

1 existing line in 1 file now uncovered.

3280 of 3449 relevant lines covered (95.1%)

4.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.43
/common/src/main/java/io/github/hyshmily/hotkey/autoconfigure/HotKeyRedisAutoConfiguration.java
1
/*
2
 * Copyright 2026 Hyshmily. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package io.github.hyshmily.hotkey.autoconfigure;
17

18
import com.github.benmanes.caffeine.cache.Cache;
19
import io.github.hyshmily.hotkey.HotKey;
20
import io.github.hyshmily.hotkey.cache.CacheExpireManager;
21
import io.github.hyshmily.hotkey.cache.HotKeyCache;
22
import io.github.hyshmily.hotkey.cache.SingleFlight;
23
import io.github.hyshmily.hotkey.hotkeydetector.HotKeyDetector;
24
import io.github.hyshmily.hotkey.reporting.HotKeyReporter;
25
import io.github.hyshmily.hotkey.rule.RuleMatcher;
26
import io.github.hyshmily.hotkey.sharding.RingManager;
27
import io.github.hyshmily.hotkey.sync.CacheSyncPublisher;
28
import io.github.hyshmily.hotkey.sync.ClusterHealthView;
29
import io.github.hyshmily.hotkey.sync.VersionController;
30
import java.util.Optional;
31
import java.util.concurrent.Executor;
32
import org.springframework.beans.factory.ObjectProvider;
33
import org.springframework.beans.factory.annotation.Qualifier;
34
import org.springframework.boot.autoconfigure.AutoConfiguration;
35
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
36
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
37
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
38
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
39
import org.springframework.boot.context.properties.EnableConfigurationProperties;
40
import org.springframework.context.annotation.Bean;
41
import org.springframework.data.redis.core.RedisTemplate;
42
import org.springframework.data.redis.core.StringRedisTemplate;
43

44
/**
45
 * Redis-enhanced auto-configuration that overrides the default
46
 * {@link HotKeyCache} with one that supports version-based stale detection.
47
 *
48
 * <p>Activates when a {@link RedisTemplate} bean is available.
49
 * {@link StringRedisTemplate} is injected as optional — if absent,
50
 * version tracking falls back to a node-local counter ({@link
51
 * io.github.hyshmily.hotkey.util.InstanceIdGenerator#getNodeId()} + {@link
52
 * java.util.concurrent.atomic.AtomicLong}) with a {@code Long.MIN_VALUE}
53
 * base, ensuring degraded versions from different JVMs occupy distinct ranges.
54
 *
55
 * <p>Runs <em>after</em> {@link HotKeyAutoConfiguration} so its
56
 * {@code @ConditionalOnMissingBean} on {@code hotKeyCache} wins over the
57
 * non-Redis variant, and after {@link RedisAutoConfiguration} so the Redis
58
 * infrastructure is ready.
59
 *
60
 * <p>Also creates a {@link RuleMatcher} with optional Redis persistence,
61
 * enabling rule CRUD operations to survive app restarts.
62
 */
63
@AutoConfiguration(after = { HotKeyAutoConfiguration.class, RedisAutoConfiguration.class })
64
@ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
65
@EnableConfigurationProperties(HotKeyProperties.class)
66
public class HotKeyRedisAutoConfiguration {
3✔
67

68
  /**
69
   * Create the {@link RuleMatcher} with optional Redis persistence and broadcast support.
70
   *
71
   * <p>When a {@link StringRedisTemplate} is available, rules (blacklist/whitelist)
72
   * are persisted in Redis under the key {@code hotkey:rules}, surviving app restarts.
73
   * When a {@link CacheSyncPublisher} is available, rule changes are broadcast to
74
   * peer instances for cluster-wide consistency.
75
   *
76
   * @param redisTemplateProvider optional provider for StringRedisTemplate (Redis persistence);
77
   *                              may be absent
78
   * @param publisherProvider     optional provider for the cache sync publisher (cross-instance
79
   *                              broadcast); may be absent
80
   * @return a new RuleMatcher instance with optional Redis-backed persistence
81
   */
82
  @Bean
83
  @ConditionalOnMissingBean
84
  public RuleMatcher ruleMatcher(
85
    ObjectProvider<StringRedisTemplate> redisTemplateProvider,
86
    ObjectProvider<CacheSyncPublisher> publisherProvider
87
  ) {
88
    return new RuleMatcher(
4✔
89
      Optional.ofNullable(redisTemplateProvider.getIfAvailable()),
4✔
90
      Optional.ofNullable(publisherProvider.getIfAvailable())
4✔
91
    );
92
  }
93

94
  /**
95
   * Create the Redis-enhanced {@link HotKeyCache} with version-based stale detection.
96
   *
97
   * <p>The {@link StringRedisTemplate} is optional — if absent, version tracking falls
98
   * back to a node-local counter ({@code Long.MIN_VALUE + nodeId + counter}), ensuring
99
   * graceful degradation when Redis is temporarily unavailable. The full HotKeyCache
100
   * pipeline includes: TopK detection, L1 Caffeine, SingleFlight dedup, soft/hard
101
   * expiry, cross-instance sync, reporting, rule matching, and consistent-hash routing.
102
   *
103
   * @param hotKeyDetector     the app-side TopK detector for hot-key frequency tracking
104
   * @param hotLocalCache      the L1 Caffeine cache (shared across all keys)
105
   * @param singleFlight       the deduplication layer for concurrent cache-load requests
106
   * @param expireManager      the soft/hard expiration manager for TTL control
107
   * @param syncPublisher      optional cache sync publisher for cross-instance broadcast
108
   * @param hotKeyReporter     optional hot key reporter for app-to-Worker data flow
109
   * @param hotKeyExecutor     the dedicated HotKey async executor
110
   * @param redisTemplateProvider optional provider for StringRedisTemplate (version tracking);
111
   *                              may be absent
112
   * @param properties         the HotKey configuration properties
113
   * @param ruleMatcher        the rule matcher for blacklist/whitelist evaluation
114
   * @param ringManagerProvider  provider for the consistent-hash ring manager (creates default if absent)
115
   * @return a new Redis-backed HotKeyCache instance
116
   */
117
  @Bean
118
  @ConditionalOnMissingBean
119
  @ConditionalOnBean(RedisTemplate.class)
120
  @ConditionalOnClass(HotKeyDetector.class)
121
  @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
122
  public HotKeyCache hotKeyCache(
123
    @Qualifier("hotKeyDetector") HotKeyDetector hotKeyDetector,
124
    Cache<String, Object> hotLocalCache,
125
    SingleFlight singleFlight,
126
    CacheExpireManager expireManager,
127
    Optional<CacheSyncPublisher> syncPublisher,
128
    Optional<HotKeyReporter> hotKeyReporter,
129
    @Qualifier("hotKeyExecutor") Executor hotKeyExecutor,
130
    ObjectProvider<StringRedisTemplate> redisTemplateProvider,
131
    HotKeyProperties properties,
132
    RuleMatcher ruleMatcher,
133
    ObjectProvider<RingManager> ringManagerProvider,
134
    ObjectProvider<ClusterHealthView> healthViewProvider
135
  ) {
136
    return new HotKeyCache(
14✔
137
      hotKeyDetector,
138
      hotLocalCache,
139
      singleFlight,
140
      expireManager,
141
      hotKeyExecutor,
142
      syncPublisher,
143
      hotKeyReporter,
144
      ruleMatcher,
145
      new VersionController(
146
        Optional.ofNullable(redisTemplateProvider.getIfAvailable()),
4✔
147
        properties.getVersionKeyTtlMinutes()
5✔
148
      ),
149
      ringManagerProvider.getIfAvailable(() -> new RingManager(properties.getConsistentHashing().getVirtualNodes())),
5✔
150
      healthViewProvider.getIfAvailable(() ->
3✔
NEW
151
        new ClusterHealthView(
×
NEW
152
          properties.getExpectedWorkerCount(),
×
NEW
153
          properties.getHeartbeat().getTimeoutMs(),
×
NEW
154
          properties.getHeartbeat().getDegradeAfterFailures()
×
155
        )
156
      )
157
    );
158
  }
159

160
  /**
161
   * Fallback {@link HotKey} facade bean for the Redis-enhanced path.
162
   *
163
   * <p>Only active when a {@link HotKeyCache} bean exists but no {@link HotKey}
164
   * has been defined yet. This covers the case where the primary
165
   * {@link HotKeyFacadeAutoConfiguration} is excluded and the {@link HotKeyCache}
166
   * originates from this configuration. The returned facade provides the full
167
   * public API including read/write/invalidate operations, TopK introspection,
168
   * and rule management.
169
   *
170
   * @param hotKeyCache    the Redis-backed HotKeyCache instance (never {@code null})
171
   * @param hotKeyDetector the app-side TopK detector (never {@code null})
172
   * @return a new HotKey facade instance
173
   */
174
  @Bean
175
  @ConditionalOnBean(HotKeyCache.class)
176
  @ConditionalOnMissingBean
177
  public HotKey hotKey(HotKeyCache hotKeyCache, @Qualifier("hotKeyDetector") HotKeyDetector hotKeyDetector) {
178
    return new HotKey(hotKeyCache, hotKeyDetector, null);
7✔
179
  }
180
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc