diff --git a/communication/src/main/java/datadog/communication/serialization/GenerationalUtf8Cache.java b/communication/src/main/java/datadog/communication/serialization/GenerationalUtf8Cache.java index 036abfe6e79..33b775b197d 100644 --- a/communication/src/main/java/datadog/communication/serialization/GenerationalUtf8Cache.java +++ b/communication/src/main/java/datadog/communication/serialization/GenerationalUtf8Cache.java @@ -85,10 +85,10 @@ public final class GenerationalUtf8Cache implements EncodingCache { static final int MAX_ENTRY_LEN = 256; - private final CacheEntry[] edenEntries; + final CacheEntry[] edenEntries; private final int[] edenMarkers; - private final CacheEntry[] tenuredEntries; + final CacheEntry[] tenuredEntries; private long accessTimeMs; private double promotionThreshold = INITIAL_PROMOTION_THRESHOLD; @@ -120,7 +120,7 @@ public GenerationalUtf8Cache(int capacity) { public GenerationalUtf8Cache(int edenCapacity, int tenuredCapacity) { this.accessTimeMs = System.currentTimeMillis(); - int edenSize = Caching.cacheSizeFor(Math.min(tenuredCapacity, MAX_EDEN_CAPACITY)); + int edenSize = Caching.cacheSizeFor(Math.min(edenCapacity, MAX_EDEN_CAPACITY)); this.edenEntries = new CacheEntry[edenSize]; this.edenMarkers = new int[edenSize]; @@ -143,7 +143,7 @@ public void updateAccessTime(long accessTimeMs) { } /** Updates access time to the @link {@link System#currentTimeMillis()} */ - public void refreshAcessTime() { + public void refreshAccessTime() { this.updateAccessTime(System.currentTimeMillis()); } @@ -215,14 +215,13 @@ public final byte[] getUtf8(String value, long accessTimeMs) { if (value.length() > MAX_ENTRY_LEN) return CacheEntry.utf8(value); int adjHash = Caching.adjHash(value); - long lookupTimeMs = this.accessTimeMs; CacheEntry[] tenuredEntries = this.tenuredEntries; int matchingTenuredIndex = lookupEntryIndex(tenuredEntries, MAX_TENURED_PROBES, adjHash, value); if (matchingTenuredIndex != -1) { CacheEntry tenuredEntry = tenuredEntries[matchingTenuredIndex]; - tenuredEntry.hit(lookupTimeMs); + tenuredEntry.hit(accessTimeMs); this.tenuredHits += 1; return tenuredEntry.utf8(); @@ -233,7 +232,7 @@ public final byte[] getUtf8(String value, long accessTimeMs) { if (matchingEdenIndex != -1) { CacheEntry edenEntry = edenEntries[matchingEdenIndex]; - double hits = edenEntry.hit(lookupTimeMs); + double hits = edenEntry.hit(accessTimeMs); if (hits > this.promotionThreshold) { // mark promoted first - to avoid racy insertions this.promotions += 1; @@ -256,8 +255,8 @@ public final byte[] getUtf8(String value, long accessTimeMs) { CacheEntry newEntry = new CacheEntry(adjHash, value); // First request was swallowed by marking, so double hit - newEntry.hit(lookupTimeMs); - newEntry.hit(lookupTimeMs); + newEntry.hit(accessTimeMs); + newEntry.hit(accessTimeMs); // search for empty slot or failing that the MFU entry int edenMfuIndex = findFirstAvailableOrMfuIndex(edenEntries, MAX_EDEN_PROBES, adjHash); @@ -346,7 +345,7 @@ static final boolean lfuInsert(CacheEntry[] entries, int numProbes, CacheEntry n } } - // If we get here, then we're evicted the LRU + // If we get here, then we're evicting the LFU entries[lfuIndex] = newEntry; return true; } diff --git a/communication/src/main/java/datadog/communication/serialization/SimpleUtf8Cache.java b/communication/src/main/java/datadog/communication/serialization/SimpleUtf8Cache.java index bb2dcf11f5d..93e6c92bb9b 100644 --- a/communication/src/main/java/datadog/communication/serialization/SimpleUtf8Cache.java +++ b/communication/src/main/java/datadog/communication/serialization/SimpleUtf8Cache.java @@ -160,7 +160,7 @@ static final boolean lfuInsert(CacheEntry[] entries, CacheEntry newEntry) { } } - // If we get here, then we're evicting the LRU + // If we get here, then we're evicting the LFU entries[lfuIndex] = newEntry; return true; } diff --git a/communication/src/test/java/datadog/communication/serialization/GenerationalUtf8CacheTest.java b/communication/src/test/java/datadog/communication/serialization/GenerationalUtf8CacheTest.java index b8fb3fde316..86876b3d29f 100644 --- a/communication/src/test/java/datadog/communication/serialization/GenerationalUtf8CacheTest.java +++ b/communication/src/test/java/datadog/communication/serialization/GenerationalUtf8CacheTest.java @@ -36,6 +36,13 @@ public void capacity() { assertEquals(128, cache.tenuredCapacity()); } + @Test + public void capacity_twoArg() { + GenerationalUtf8Cache cache = new GenerationalUtf8Cache(64, 256); + assertEquals(64, cache.edenCapacity()); + assertEquals(256, cache.tenuredCapacity()); + } + @Test public void maxCapacity() { GenerationalUtf8Cache cache = @@ -82,6 +89,29 @@ public void caching() { assertNotEquals(0, cache.edenHits); } + @Test + public void getUtf8_perCallAccessTime_overridesField() { + GenerationalUtf8Cache cache = create(); + // The field value should not leak into the entry when an explicit time is supplied. + cache.updateAccessTime(0L); + + String value = "bar"; + long callTime = 12345L; + + // First call only marks; the second call creates the entry. + cache.getUtf8(value, callTime); + cache.getUtf8(value, callTime); + + assertEquals(callTime, lookupEdenLastUsedMs(cache, value)); + + // Drive enough hits to promote into tenured. + while (cache.promotions == 0) { + cache.getUtf8(value, callTime); + } + + assertEquals(callTime, lookupTenuredLastUsedMs(cache, value)); + } + @Test public void promotion() { GenerationalUtf8Cache cache = create(); @@ -205,6 +235,24 @@ static final String nextValue() { return baseString + valueSuffix; } + static long lookupEdenLastUsedMs(GenerationalUtf8Cache cache, String value) { + return lookupLastUsedMs(cache.edenEntries, "edenEntries", value); + } + + static long lookupTenuredLastUsedMs(GenerationalUtf8Cache cache, String value) { + return lookupLastUsedMs(cache.tenuredEntries, "tenuredEntries", value); + } + + private static long lookupLastUsedMs( + GenerationalUtf8Cache.CacheEntry[] entries, String arrayName, String value) { + for (GenerationalUtf8Cache.CacheEntry entry : entries) { + if (entry != null && value.equals(entry.value)) { + return entry.lastUsedMs(); + } + } + throw new AssertionError("entry for value '" + value + "' not found in " + arrayName); + } + static final void printStats(GenerationalUtf8Cache cache) { System.out.printf( "eden hits: %5d\tpromotion hits: %5d\tpromotions: %5d\tearly: %5d\tlocal evictions: %5d\tglobal evictions: %5d%n",