Skip to content

rls: Refactor estimatedSizeBytes updates #12145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 12, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 14 additions & 25 deletions rls/src/main/java/io/grpc/rls/LinkedHashLruCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ abstract class LinkedHashLruCache<K, V> implements LruCache<K, V> {

private final LinkedHashMap<K, SizedValue> delegate;
private final Ticker ticker;
private final EvictionListener<K, SizedValue> evictionListener;
@Nullable
private final EvictionListener<K, V> evictionListener;
private long estimatedSizeBytes;
private long estimatedMaxSizeBytes;

Expand All @@ -53,7 +54,7 @@ abstract class LinkedHashLruCache<K, V> implements LruCache<K, V> {
final Ticker ticker) {
checkState(estimatedMaxSizeBytes > 0, "max estimated cache size should be positive");
this.estimatedMaxSizeBytes = estimatedMaxSizeBytes;
this.evictionListener = new SizeHandlingEvictionListener(evictionListener);
this.evictionListener = evictionListener;
this.ticker = checkNotNull(ticker, "ticker");
delegate = new LinkedHashMap<K, SizedValue>(
// rough estimate or minimum hashmap default
Expand Down Expand Up @@ -135,7 +136,7 @@ public final V cache(K key, V value) {
estimatedSizeBytes += size;
existing = delegate.put(key, new SizedValue(size, value));
if (existing != null) {
evictionListener.onEviction(key, existing, EvictionType.REPLACED);
fireOnEviction(key, existing, EvictionType.REPLACED);
}
return existing == null ? null : existing.value;
}
Expand Down Expand Up @@ -174,7 +175,7 @@ private V invalidate(K key, EvictionType cause) {
checkNotNull(cause, "cause");
SizedValue existing = delegate.remove(key);
if (existing != null) {
evictionListener.onEviction(key, existing, cause);
fireOnEviction(key, existing, cause);
}
return existing == null ? null : existing.value;
}
Expand All @@ -185,7 +186,7 @@ public final void invalidateAll() {
while (iterator.hasNext()) {
Map.Entry<K, SizedValue> entry = iterator.next();
if (entry.getValue() != null) {
evictionListener.onEviction(entry.getKey(), entry.getValue(), EvictionType.EXPLICIT);
fireOnEviction(entry.getKey(), entry.getValue(), EvictionType.EXPLICIT);
}
iterator.remove();
}
Expand Down Expand Up @@ -215,23 +216,22 @@ public final List<V> values() {
protected final boolean fitToLimit() {
boolean removedAnyUnexpired = false;
if (estimatedSizeBytes <= estimatedMaxSizeBytes) {
// new size is larger no need to do cleanup
return false;
}
// cleanup expired entries
long now = ticker.read();
cleanupExpiredEntries(now);

// cleanup eldest entry until new size limit
// cleanup eldest entry until the size of all entries fits within the limit
Iterator<Map.Entry<K, SizedValue>> lruIter = delegate.entrySet().iterator();
while (lruIter.hasNext() && estimatedMaxSizeBytes < this.estimatedSizeBytes) {
Map.Entry<K, SizedValue> entry = lruIter.next();
if (!shouldInvalidateEldestEntry(entry.getKey(), entry.getValue().value, now)) {
break; // Violates some constraint like minimum age so stop our cleanup
}
lruIter.remove();
// eviction listener will update the estimatedSizeBytes
evictionListener.onEviction(entry.getKey(), entry.getValue(), EvictionType.SIZE);
// fireOnEviction will update the estimatedSizeBytes
fireOnEviction(entry.getKey(), entry.getValue(), EvictionType.SIZE);
removedAnyUnexpired = true;
}
return removedAnyUnexpired;
Expand Down Expand Up @@ -270,7 +270,7 @@ private boolean cleanupExpiredEntries(int maxExpiredEntries, long now) {
Map.Entry<K, SizedValue> entry = lruIter.next();
if (isExpired(entry.getKey(), entry.getValue().value, now)) {
lruIter.remove();
evictionListener.onEviction(entry.getKey(), entry.getValue(), EvictionType.EXPIRED);
fireOnEviction(entry.getKey(), entry.getValue(), EvictionType.EXPIRED);
removedAny = true;
maxExpiredEntries--;
}
Expand All @@ -283,21 +283,10 @@ public final void close() {
invalidateAll();
}

/** A {@link EvictionListener} keeps track of size. */
private final class SizeHandlingEvictionListener implements EvictionListener<K, SizedValue> {

private final EvictionListener<K, V> delegate;

SizeHandlingEvictionListener(@Nullable EvictionListener<K, V> delegate) {
this.delegate = delegate;
}

@Override
public void onEviction(K key, SizedValue value, EvictionType cause) {
estimatedSizeBytes -= value.size;
if (delegate != null) {
delegate.onEviction(key, value.value, cause);
}
private void fireOnEviction(K key, SizedValue value, EvictionType cause) {
estimatedSizeBytes -= value.size;
if (evictionListener != null) {
evictionListener.onEviction(key, value.value, cause);
}
}

Expand Down