EternalCore
EternalCore copied to clipboard
GH-1153 Rework delay system with per-entry Instant TTL
Description
This PR refactors the delay handling to eliminate cache-level TTL conflicts.
- Introduced GuavaDelay with shared logic on top of Guava Cache
- Removed
expireAfterWriteto ensure TTL is controlled per entry via Instant - Added separate interfaces: DefaultDelay (with predefined duration) and ExplicitDelay (explicit duration required)
- Implemented GuavaDefaultDelay and GuavaExplicitDelay
- Added Delay factory for convenient instance creation
- Clarified contract and documentation
Result: cleaner API, safer usage, no premature cache evictions. Fixes #1153
Type of change
- [X] Bug fix (non-breaking change which fixes an issue)
- [X] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [X] This change requires a documentation update
How Has This Been Tested?
- [X] Localhost server
/gemini review
// kontrakt
public interface Delay<TYPE> {
void mark(TYPE key, Function<TYPE, Duration> durationProviderOverride);
void unmark(TYPE key);
boolean isActive(TYPE key);
Duration getRemaining(TYPE key);
Instant getExpireAt(TYPE key);
void extend(TYPE key, Duration extra);
static <TYPE> Delay<TYPE> ofDefault(Duration defaultDuration, long maximumSize) {
return new KeyedDelay<>(key -> defaultDuration, maximumSize);
}
static <TYPE> Delay<TYPE> ofDynamic(Function<TYPE, Duration> durationProvider, long maximumSize) {
return new KeyedDelay<>(durationProvider, maximumSize);
}
}
final class KeyedDelay<TYPE> implements Delay<TYPE> {
private final Function<TYPE, Duration> durationProvider;
private final Cache<TYPE, Instant> cache;
KeyedDelay(Function<TYPE, Duration> durationProvider, long maximumSize) {
this.durationProvider = durationProvider;
this.cache = Caffeine.newBuilder()
.maximumSize(maximumSize)
.build();
}
@Override
public void mark(TYPE key, Function<TYPE, Duration> durationProviderOverride) {
Function<TYPE, Duration> functor = durationProviderOverride != null ? durationProviderOverride : durationProvider;
Duration duration = functor.apply(key);
cache.put(key, Instant.now().plus(duration));
}
@Override
public void unmark(TYPE key) {
cache.invalidate(key);
}
@Override
public boolean isActive(TYPE key) {
Instant expire = cache.getIfPresent(key);
return expire != null && Instant.now().isBefore(expire);
}
@Override
public Duration getRemaining(TYPE key) {
Instant expire = cache.getIfPresent(key);
if (expire == null) {
return Duration.ZERO;
}
Duration remaining = Duration.between(Instant.now(), expire);
return remaining.isNegative() ? Duration.ZERO : remaining;
}
@Override
public Instant getExpireAt(TYPE key) {
return cache.getIfPresent(key);
}
@Override
public void extend(TYPE key, Duration extra) {
Instant expire = cache.getIfPresent(key);
Instant newExpire = (expire != null ? expire : Instant.now()).plus(extra);
cache.put(key, newExpire);
}
}
uzycie:
final class DelayExample {
void main(String[] args) throws InterruptedException {
// Tworzymy Delay z domyślnym czasem np. 5 sekund
Delay<String> delay = Delay.ofDefault(Duration.ofSeconds(5), 100);
String key = "hujsonpool";
// Sprawdzamy, czy mamy delay
System.out.println("Czy jest delay? " + delay.has(key)); // :: false
// Oznaczamy delay
delay.mark(key, null);
System.out.println("Ustawiono delay dla " + key);
// Pozostały czas
System.out.println("Pozostały czas: " + delay.getRemaining(key).toSeconds() + " sekund");
// Przykry przykład Czekamy 3 sekundy
Thread.sleep(3000);
System.out.println("Czy jest delay po 3 sekundach? " + delay.has(key));
System.out.println("Pozostały czas: " + delay.getRemaining(key).toSeconds() + " sekund");
// Przedłużamy o 2 sekundy
delay.extend(key, Duration.ofSeconds(2));
System.out.println("Przedłużono delay. Pozostały czas: " + delay.getRemaining(key).toSeconds() + " sekund");
// Czekamy kolejne 5 sekund
Thread.sleep(5000);
System.out.println("Czy jest delay po 8 sekundach? " + delay.has(key)); // :: false
System.out.println("Pozostały czas: " + delay.getRemaining(key).toSeconds() + " sekund");
// Usuwamy delay
delay.unmark(key);
System.out.println("Delay usunięty" + delay.has(key));
}
}