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

Hyshmily / hotkey / 27925388821

22 Jun 2026 02:14AM UTC coverage: 92.403% (-1.2%) from 93.615%
27925388821

push

github

Hyshmily
fix:fix known bugs and promote performance

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

1143 of 1289 branches covered (88.67%)

Branch coverage included in aggregate %.

184 of 236 new or added lines in 19 files covered. (77.97%)

6 existing lines in 3 files now uncovered.

3321 of 3542 relevant lines covered (93.76%)

4.22 hits per line

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

85.71
/common/src/main/java/io/github/hyshmily/hotkey/cache/SingleFlight.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.cache;
17

18
import com.github.benmanes.caffeine.cache.Cache;
19
import com.github.benmanes.caffeine.cache.Caffeine;
20
import lombok.extern.slf4j.Slf4j;
21

22
import java.util.Optional;
23
import java.util.concurrent.CompletableFuture;
24
import java.util.concurrent.CompletionException;
25
import java.util.concurrent.Executor;
26
import java.util.concurrent.TimeUnit;
27
import java.util.function.Supplier;
28

29

30
/**
31
 * Deduplicates concurrent in-flight loads for the same key.
32
 * <p>
33
 * Only the first caller executes the supplier; subsequent callers wait for
34
 * the same {@link CompletableFuture}. On normal completion, the future
35
 * remains cached (TTL-based expiry via {@code expireAfterWrite}) so
36
 * late-arriving callers reuse the result without re-execution. On timeout or
37
 * exception, the entry is evicted immediately to allow a subsequent retry.
38
 * <p>
39
 * The internal dedup cache is bounded by {@code maxSize} (LRU eviction) and
40
 * entries expire after the configured {@code ttlSec} seconds from write.
41
 * This class is thread-safe.
42
 */
43
@Slf4j
4✔
44
public class SingleFlight {
45

46
  /** Caffeine cache tracking currently in-flight loads (key -> CompletableFuture). */
47
  private final Cache<String, CompletableFuture<Object>> inflightLoads;
48
  /** Async executor for running the supplier. */
49
  private final Executor executor;
50
  /** Timeout in seconds before a supplier future is completed exceptionally. */
51
  private final int timeoutSeconds;
52
  /** Maximum number of in-flight keys tracked simultaneously. */
53
  private final int inflightMaxSize;
54

55
  /**
56
   * Creates a SingleFlight deduplicator that prevents concurrent in-flight loads
57
   * for the same key.
58
   *
59
   * @param maxSize        maximum number of concurrent in-flight keys tracked
60
   * @param ttlSec         time-to-live for dedup entries after write
61
   * @param timeoutSeconds per-supplier timeout before the future is completed exceptionally
62
   * @param executor       async executor for supplier execution
63
   */
64
  public SingleFlight(int maxSize, int ttlSec, int timeoutSeconds, Executor executor) {
2✔
65
    this.inflightLoads = Caffeine.newBuilder().maximumSize(maxSize).expireAfterWrite(ttlSec, TimeUnit.SECONDS).build();
11✔
66
    this.executor = executor;
3✔
67
    this.timeoutSeconds = timeoutSeconds;
3✔
68
    this.inflightMaxSize = maxSize;
3✔
69
  }
1✔
70

71
  /**
72
   * Approximate number of keys currently tracked for dedup.
73
   * Useful for monitoring and diagnostics.
74
   *
75
   * @return the estimated number of in-flight keys
76
   */
77
  public long estimatedInflightSize() {
78
    return inflightLoads.estimatedSize();
4✔
79
  }
80

81
  /**
82
   * Load a value via the supplier, deduplicating concurrent requests for the same key.
83
   * Thread-safe: concurrent calls for the same key share a single future.
84
   *
85
   * @param cacheKey the key to load
86
   * @param reader   the value supplier (should not return {@code null})
87
   * @param <T>      the value type
88
   * @return the loaded value, or empty if the load failed or timed out
89
   */
90
  @SuppressWarnings("unchecked")
91
  public <T> Optional<T> load(String cacheKey, Supplier<T> reader) {
92
    if (estimatedInflightSize() > inflightMaxSize * 0.8) {
10✔
93
      log.warn("SingleFlight inflight queue is high: {}/{}", estimatedInflightSize(), inflightMaxSize);
9✔
94
    }
95

96
    CompletableFuture<Object> future = inflightLoads
2✔
97
      .asMap()
5✔
98
      .computeIfAbsent(cacheKey, k ->
3✔
99
        CompletableFuture.supplyAsync(() -> (Object) reader.get(), executor).orTimeout(timeoutSeconds, TimeUnit.SECONDS)
14✔
100
      );
101

102
    try {
103
      return Optional.ofNullable((T) future.join());
4✔
104
    } catch (CompletionException e) {
1✔
105
      log.warn("singleflight join failed: key={}", cacheKey, e);
5✔
106
      inflightLoads.invalidate(cacheKey);
4✔
107

108
      Throwable cause = e.getCause();
3✔
109

110
      if (cause instanceof InterruptedException) {
3!
NEW
111
        Thread.currentThread().interrupt();
×
NEW
112
        throw new RuntimeException("Thread interrupted during load for key: " + cacheKey, cause);
×
113
      }
114

115
      if (cause instanceof RuntimeException re) {
6✔
116
        throw re;
2✔
117
      }
118

119
      if (cause instanceof Error err) {
3!
NEW
120
        throw err;
×
121
      }
122

123
      throw new RuntimeException("Loader failed for key: " + cacheKey, cause);
7✔
124
    }
125
  }
126
}
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