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

Hyshmily / hotkey / 28637103998

03 Jul 2026 03:48AM UTC coverage: 90.399% (-0.2%) from 90.63%
28637103998

push

github

Hyshmily
fix: add packaging to gitignore, fix flaky jitter test, update docs references

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

1264 of 1461 branches covered (86.52%)

Branch coverage included in aggregate %.

3604 of 3924 relevant lines covered (91.85%)

4.19 hits per line

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

69.23
/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.Internal;
21
import io.github.hyshmily.hotkey.cache.HotKeyCache;
22
import io.github.hyshmily.hotkey.cache.cachesupport.CacheExpireManager;
23
import io.github.hyshmily.hotkey.cache.cachesupport.SingleFlight;
24
import io.github.hyshmily.hotkey.hotkeydetector.HotKeyDetector;
25
import io.github.hyshmily.hotkey.reporting.HotKeyReporter;
26
import io.github.hyshmily.hotkey.rule.RuleMatcher;
27
import io.github.hyshmily.hotkey.sharding.ClusterHealthView;
28
import io.github.hyshmily.hotkey.sync.local.CacheSyncPublisher;
29
import io.github.hyshmily.hotkey.util.version.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
@Internal
64
@AutoConfiguration(after = { HotKeyAutoConfiguration.class, RedisAutoConfiguration.class })
65
@ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
66
@EnableConfigurationProperties(HotKeyProperties.class)
67
public class HotKeyRedisAutoConfiguration {
3✔
68

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

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

158
  /**
159
   * Fallback {@link HotKey} facade bean for the Redis-enhanced path.
160
   *
161
   * <p>Only active when a {@link HotKeyCache} bean exists but no {@link HotKey}
162
   * has been defined yet. This covers the case where the primary
163
   * {@link HotKeyFacadeAutoConfiguration} is excluded and the {@link HotKeyCache}
164
   * originates from this configuration. The returned facade provides the full
165
   * public API including read/write/invalidate operations, TopK introspection,
166
   * and rule management.
167
   *
168
   * @param hotKeyCache    the Redis-backed HotKeyCache instance (never {@code null})
169
   * @param hotKeyDetector the app-side TopK detector (never {@code null})
170
   * @return a new HotKey facade instance
171
   */
172
  @Bean
173
  @ConditionalOnBean(HotKeyCache.class)
174
  @ConditionalOnMissingBean
175
  public HotKey hotKey(HotKeyCache hotKeyCache, @Qualifier("hotKeyDetector") HotKeyDetector hotKeyDetector) {
176
    return new HotKey(hotKeyCache, hotKeyDetector, null);
7✔
177
  }
178
}
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