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

Hyshmily / hotkey / 28159619024

25 Jun 2026 09:10AM UTC coverage: 92.667% (-0.02%) from 92.685%
28159619024

push

github

Hyshmily
release: v1.1.52

1199 of 1351 branches covered (88.75%)

Branch coverage included in aggregate %.

3426 of 3640 relevant lines covered (94.12%)

4.25 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.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.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
@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
   * @return a new Redis-backed HotKeyCache instance
115
   */
116
  @Bean
117
  @ConditionalOnMissingBean
118
  @ConditionalOnBean({ RedisTemplate.class, HotKeyDetector.class })
119
  @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
120
  public HotKeyCache hotKeyCache(
121
    @Qualifier("hotKeyDetector") HotKeyDetector hotKeyDetector,
122
    Cache<String, Object> hotLocalCache,
123
    SingleFlight singleFlight,
124
    CacheExpireManager expireManager,
125
    Optional<CacheSyncPublisher> syncPublisher,
126
    Optional<HotKeyReporter> hotKeyReporter,
127
    @Qualifier("hotKeyExecutor") Executor hotKeyExecutor,
128
    ObjectProvider<StringRedisTemplate> redisTemplateProvider,
129
    HotKeyProperties properties,
130
    RuleMatcher ruleMatcher,
131
    ObjectProvider<ClusterHealthView> healthViewProvider
132
  ) {
133
    return new HotKeyCache(
14✔
134
      hotKeyDetector,
135
      hotLocalCache,
136
      singleFlight,
137
      expireManager,
138
      hotKeyExecutor,
139
      syncPublisher,
140
      hotKeyReporter,
141
      ruleMatcher,
142
      new VersionController(
143
        Optional.ofNullable(redisTemplateProvider.getIfAvailable()),
4✔
144
        properties.getVersionKeyTtlMinutes()
5✔
145
      ),
146
      healthViewProvider.getIfAvailable(() ->
3✔
147
        new ClusterHealthView(
×
148
          properties.getExpectedWorkerCount(),
×
149
          properties.getHeartbeat().getTimeoutMs(),
×
150
          properties.getHeartbeat().getDegradeAfterFailures()
×
151
        )
152
      )
153
    );
154
  }
155

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