Merge branch 'stable-3.3' * stable-3.3: Use wrapper to store cache key Change-Id: I46cd2d89b141eefa09b452e9c6229751d3775eab
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/CacheSerializers.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/CacheSerializers.java new file mode 100644 index 0000000..8546981 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/CacheSerializers.java
@@ -0,0 +1,58 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.googlesource.gerrit.modules.cache.chroniclemap; + +import com.google.gerrit.server.cache.PersistentCacheDef; +import com.google.gerrit.server.cache.serialize.CacheSerializer; +import com.google.inject.Singleton; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Singleton +public class CacheSerializers { + private static final Map<String, CacheSerializer<?>> keySerializers = new ConcurrentHashMap<>(); + private static final Map<String, CacheSerializer<?>> valueSerializers = new ConcurrentHashMap<>(); + + static <K, V> void registerCacheDef(PersistentCacheDef<K, V> def) { + String cacheName = def.name(); + registerCacheKeySerializer(cacheName, def.keySerializer()); + registerCacheValueSerializer(cacheName, def.valueSerializer()); + } + + static <K, V> void registerCacheKeySerializer( + String cacheName, CacheSerializer<K> keySerializer) { + keySerializers.computeIfAbsent(cacheName, (name) -> keySerializer); + } + + static <K, V> void registerCacheValueSerializer( + String cacheName, CacheSerializer<V> valueSerializer) { + valueSerializers.computeIfAbsent(cacheName, (name) -> valueSerializer); + } + + @SuppressWarnings("unchecked") + public static <K> CacheSerializer<K> getKeySerializer(String name) { + if (keySerializers.containsKey(name)) { + return (CacheSerializer<K>) keySerializers.get(name); + } + throw new IllegalStateException("Could not find key serializer for " + name); + } + + @SuppressWarnings("unchecked") + public static <V> CacheSerializer<V> getValueSerializer(String name) { + if (valueSerializers.containsKey(name)) { + return (CacheSerializer<V>) valueSerializers.get(name); + } + throw new IllegalStateException("Could not find value serializer for " + name); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheImpl.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheImpl.java index 311445b..bde4fd1 100644 --- a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheImpl.java +++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheImpl.java
@@ -39,7 +39,7 @@ private final ChronicleMapCacheConfig config; private final CacheLoader<K, V> loader; - private final ChronicleMap<K, TimedValue<V>> store; + private final ChronicleMap<KeyWrapper<K>, TimedValue<V>> store; private final LongAdder hitCount = new LongAdder(); private final LongAdder missCount = new LongAdder(); private final LongAdder loadSuccessCount = new LongAdder(); @@ -55,6 +55,8 @@ CacheLoader<K, V> loader, MetricMaker metricMaker) throws IOException { + CacheSerializers.registerCacheDef(def); + this.config = config; this.loader = loader; this.hotEntries = @@ -63,11 +65,11 @@ ChronicleMapStorageMetrics metrics = new ChronicleMapStorageMetrics(metricMaker); - final Class<K> keyClass = (Class<K>) def.keyType().getRawType(); + final Class<KeyWrapper<K>> keyWrapperClass = (Class<KeyWrapper<K>>) (Class) KeyWrapper.class; final Class<TimedValue<V>> valueWrapperClass = (Class<TimedValue<V>>) (Class) TimedValue.class; - final ChronicleMapBuilder<K, TimedValue<V>> mapBuilder = - ChronicleMap.of(keyClass, valueWrapperClass).name(def.name()); + final ChronicleMapBuilder<KeyWrapper<K>, TimedValue<V>> mapBuilder = + ChronicleMap.of(keyWrapperClass, valueWrapperClass).name(def.name()); // Chronicle-map does not allow to custom-serialize boxed primitives // such as Boolean, Integer, for which size is statically determined. @@ -75,11 +77,11 @@ // it cannot be used. if (!mapBuilder.constantlySizedKeys()) { mapBuilder.averageKeySize(config.getAverageKeySize()); - mapBuilder.keyMarshaller(new ChronicleMapMarshallerAdapter<>(def.keySerializer())); + mapBuilder.keyMarshaller(new KeyWrapperMarshaller<>(def.name())); } mapBuilder.averageValueSize(config.getAverageValueSize()); - mapBuilder.valueMarshaller(new TimedValueMarshaller<>(def.valueSerializer())); + mapBuilder.valueMarshaller(new TimedValueMarshaller<>(def.name())); mapBuilder.entries(config.getMaxEntries()); @@ -117,7 +119,7 @@ } <K, V> void registerCallBackMetrics( - String name, ChronicleMap<K, TimedValue<V>> store, InMemoryLRU<K> hotEntries) { + String name, ChronicleMap<KeyWrapper<K>, TimedValue<V>> store, InMemoryLRU<K> hotEntries) { String sanitizedName = metricMaker.sanitizeMetricName(name); String PERCENTAGE_FREE_SPACE_METRIC = "cache/chroniclemap/percentage_free_space_" + sanitizedName; @@ -162,10 +164,12 @@ return config; } + @SuppressWarnings("unchecked") @Override public V getIfPresent(Object objKey) { - if (store.containsKey(objKey)) { - TimedValue<V> vTimedValue = store.get(objKey); + KeyWrapper<K> keyWrapper = (KeyWrapper<K>) new KeyWrapper<>(objKey); + if (store.containsKey(keyWrapper)) { + TimedValue<V> vTimedValue = store.get(keyWrapper); if (!expired(vTimedValue.getCreated())) { hitCount.increment(); hotEntries.add((K) objKey); @@ -180,8 +184,9 @@ @Override public V get(K key) throws ExecutionException { - if (store.containsKey(key)) { - TimedValue<V> vTimedValue = store.get(key); + KeyWrapper<K> keyWrapper = new KeyWrapper<>(key); + if (store.containsKey(keyWrapper)) { + TimedValue<V> vTimedValue = store.get(keyWrapper); if (!needsRefresh(vTimedValue.getCreated())) { hitCount.increment(); hotEntries.add(key); @@ -211,8 +216,9 @@ @Override public V get(K key, Callable<? extends V> valueLoader) throws ExecutionException { - if (store.containsKey(key)) { - TimedValue<V> vTimedValue = store.get(key); + KeyWrapper<K> keyWrapper = new KeyWrapper<>(key); + if (store.containsKey(keyWrapper)) { + TimedValue<V> vTimedValue = store.get(keyWrapper); if (!needsRefresh(vTimedValue.getCreated())) { hitCount.increment(); return vTimedValue.getValue(); @@ -235,14 +241,16 @@ @SuppressWarnings("unchecked") public void putUnchecked(Object key, Object value, Timestamp created) { - TimedValue<?> wrapped = new TimedValue<>(value, created.toInstant().toEpochMilli()); - store.put((K) key, (TimedValue<V>) wrapped); + TimedValue<?> wrappedValue = new TimedValue<>(value, created.toInstant().toEpochMilli()); + KeyWrapper<?> wrappedKey = new KeyWrapper<>(key); + store.put((KeyWrapper<K>) wrappedKey, (TimedValue<V>) wrappedValue); } @Override public void put(K key, V val) { - TimedValue<V> wrapped = new TimedValue<>(val); - store.put(key, wrapped); + KeyWrapper<K> wrappedKey = new KeyWrapper<>(key); + TimedValue<V> wrappedValue = new TimedValue<>(val); + store.put(wrappedKey, wrappedValue); hotEntries.add(key); } @@ -251,7 +259,7 @@ store.forEachEntry( c -> { if (expired(c.value().get().getCreated())) { - hotEntries.remove(c.key().get()); + hotEntries.remove(c.key().get().getValue()); c.context().remove(c); } }); @@ -282,17 +290,19 @@ private void evictColdEntries() { store.forEachEntryWhile( e -> { - if (!hotEntries.contains(e.key().get())) { + if (!hotEntries.contains(e.key().get().getValue())) { e.doRemove(); } return runningOutOfFreeSpace(); }); } + @SuppressWarnings("unchecked") @Override public void invalidate(Object key) { - store.remove(key); - hotEntries.remove((K) key); + KeyWrapper<K> wrappedKey = (KeyWrapper<K>) new KeyWrapper<>(key); + store.remove(wrappedKey); + hotEntries.remove(wrappedKey.getValue()); } @Override
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapMarshallerAdapter.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapMarshallerAdapter.java deleted file mode 100644 index 0c60dca..0000000 --- a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapMarshallerAdapter.java +++ /dev/null
@@ -1,51 +0,0 @@ -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package com.googlesource.gerrit.modules.cache.chroniclemap; - -import com.google.gerrit.server.cache.serialize.CacheSerializer; -import net.openhft.chronicle.bytes.Bytes; -import net.openhft.chronicle.core.util.ReadResolvable; -import net.openhft.chronicle.hash.serialization.BytesReader; -import net.openhft.chronicle.hash.serialization.BytesWriter; - -public class ChronicleMapMarshallerAdapter<T> - implements BytesWriter<T>, BytesReader<T>, ReadResolvable<ChronicleMapMarshallerAdapter<T>> { - - private final CacheSerializer<T> cacheSerializer; - - ChronicleMapMarshallerAdapter(CacheSerializer<T> cacheSerializer) { - this.cacheSerializer = cacheSerializer; - } - - @Override - public ChronicleMapMarshallerAdapter<T> readResolve() { - return new ChronicleMapMarshallerAdapter<>(cacheSerializer); - } - - @Override - public T read(Bytes in, T using) { - int serializedLength = (int) in.readUnsignedInt(); - byte[] serialized = new byte[serializedLength]; - in.read(serialized, 0, serializedLength); - using = cacheSerializer.deserialize(serialized); - return using; - } - - @Override - public void write(Bytes out, T toWrite) { - final byte[] serialized = cacheSerializer.serialize(toWrite); - out.writeUnsignedInt(serialized.length); - out.write(serialized); - } -}
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/KeyWrapper.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/KeyWrapper.java new file mode 100644 index 0000000..cfab341 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/KeyWrapper.java
@@ -0,0 +1,42 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.googlesource.gerrit.modules.cache.chroniclemap; + +import com.google.common.base.Objects; + +public class KeyWrapper<V> { + + private final V value; + + KeyWrapper(V value) { + this.value = value; + } + + public V getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof KeyWrapper)) return false; + KeyWrapper<?> that = (KeyWrapper<?>) o; + return Objects.equal(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/KeyWrapperMarshaller.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/KeyWrapperMarshaller.java new file mode 100644 index 0000000..7c7801c --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/KeyWrapperMarshaller.java
@@ -0,0 +1,55 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.googlesource.gerrit.modules.cache.chroniclemap; + +import net.openhft.chronicle.bytes.Bytes; +import net.openhft.chronicle.core.util.ReadResolvable; +import net.openhft.chronicle.hash.serialization.BytesReader; +import net.openhft.chronicle.hash.serialization.BytesWriter; + +public class KeyWrapperMarshaller<V> + implements BytesWriter<KeyWrapper<V>>, + BytesReader<KeyWrapper<V>>, + ReadResolvable<KeyWrapperMarshaller<V>> { + + private final String name; + + KeyWrapperMarshaller(String name) { + this.name = name; + } + + @Override + public KeyWrapperMarshaller<V> readResolve() { + return new KeyWrapperMarshaller<>(name); + } + + @SuppressWarnings("unchecked") + @Override + public KeyWrapper<V> read(Bytes in, KeyWrapper<V> using) { + int serializedLength = (int) in.readUnsignedInt(); + byte[] serialized = new byte[serializedLength]; + in.read(serialized, 0, serializedLength); + V v = (V) CacheSerializers.getKeySerializer(name).deserialize(serialized); + using = new KeyWrapper<>(v); + + return using; + } + + @Override + public void write(Bytes out, KeyWrapper<V> toWrite) { + final byte[] serialized = CacheSerializers.getKeySerializer(name).serialize(toWrite.getValue()); + out.writeUnsignedInt(serialized.length); + out.write(serialized); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/TimedValueMarshaller.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/TimedValueMarshaller.java index 60897bf..ec30043 100644 --- a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/TimedValueMarshaller.java +++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/TimedValueMarshaller.java
@@ -13,7 +13,6 @@ // limitations under the License. package com.googlesource.gerrit.modules.cache.chroniclemap; -import com.google.gerrit.server.cache.serialize.CacheSerializer; import java.nio.ByteBuffer; import net.openhft.chronicle.bytes.Bytes; import net.openhft.chronicle.core.util.ReadResolvable; @@ -25,17 +24,18 @@ BytesReader<TimedValue<V>>, ReadResolvable<TimedValueMarshaller<V>> { - private final CacheSerializer<V> serializer; + private final String name; - TimedValueMarshaller(CacheSerializer<V> serializer) { - this.serializer = serializer; + TimedValueMarshaller(String name) { + this.name = name; } @Override public TimedValueMarshaller<V> readResolve() { - return new TimedValueMarshaller<>(serializer); + return new TimedValueMarshaller<>(name); } + @SuppressWarnings("unchecked") @Override public TimedValue<V> read(Bytes in, TimedValue<V> using) { long initialPosition = in.readPosition(); @@ -57,7 +57,7 @@ // Deserialize object V (remaining bytes) byte[] serializedV = new byte[vLength]; in.read(serializedV, 0, vLength); - V v = serializer.deserialize(serializedV); + V v = (V) CacheSerializers.getValueSerializer(name).deserialize(serializedV); using = new TimedValue<>(v, created); @@ -66,7 +66,7 @@ @Override public void write(Bytes out, TimedValue<V> toWrite) { - byte[] serialized = serializer.serialize(toWrite.getValue()); + byte[] serialized = CacheSerializers.getValueSerializer(name).serialize(toWrite.getValue()); // Serialize as follows: // created | length of serialized V | serialized value V
diff --git a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheIT.java b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheIT.java index df6fc38..36ce53c 100644 --- a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheIT.java +++ b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheIT.java
@@ -48,7 +48,7 @@ final int negativeDiskLimit = -1; final Cache<String, String> cache = persistentCacheFactory.build( - new TestPersistentCacheDef("foo", negativeDiskLimit), CacheBackend.CAFFEINE); + new TestPersistentCacheDef("foo", null, negativeDiskLimit), CacheBackend.CAFFEINE); assertThat(cache.getClass().getSimpleName()).isEqualTo("CaffeinatedGuavaCache"); } @@ -58,7 +58,7 @@ final int positiveDiskLimit = 1024; assertThat( persistentCacheFactory.build( - new TestPersistentCacheDef("foo", positiveDiskLimit), CacheBackend.CAFFEINE)) + new TestPersistentCacheDef("foo", null, positiveDiskLimit), CacheBackend.CAFFEINE)) .isInstanceOf(ChronicleMapCacheImpl.class); }
diff --git a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheTest.java b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheTest.java index e8c1f4a..5d380ff 100644 --- a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheTest.java +++ b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheTest.java
@@ -25,6 +25,7 @@ import com.google.gerrit.metrics.DisabledMetricMaker; import com.google.gerrit.metrics.MetricMaker; import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker; +import com.google.gerrit.server.cache.serialize.CacheSerializer; import com.google.gerrit.server.cache.serialize.StringCacheSerializer; import com.google.gerrit.server.config.SitePaths; import com.google.inject.Guice; @@ -47,6 +48,7 @@ import org.junit.rules.TemporaryFolder; public class ChronicleMapCacheTest { + private static final String TEST_CACHE_NAME = "test-cache-name"; @Inject MetricMaker metricMaker; @Inject MetricRegistry metricRegistry; @@ -58,6 +60,8 @@ @Before public void setUp() throws Exception { + CacheSerializers.registerCacheKeySerializer(TEST_CACHE_NAME, StringCacheSerializer.INSTANCE); + CacheSerializers.registerCacheValueSerializer(TEST_CACHE_NAME, StringCacheSerializer.INSTANCE); sitePaths = new SitePaths(temporaryFolder.newFolder().toPath()); Files.createDirectories(sitePaths.etc_dir); @@ -82,7 +86,7 @@ } @Test - public void getIfPresentShouldReturnNullWhenThereisNoCachedValue() throws Exception { + public void getIfPresentShouldReturnNullWhenThereIsNoCachedValue() throws Exception { assertThat(newCacheWithLoader(null).getIfPresent("foo")).isNull(); } @@ -237,7 +241,7 @@ @Test public void getIfPresentShouldReturnNullWhenValueIsExpired() throws Exception { ChronicleMapCacheImpl<String, String> cache = - newCache(true, null, Duration.ofSeconds(1), null, 1); + newCache(true, TEST_CACHE_NAME, null, Duration.ofSeconds(1), null, 1); cache.put("foo", "some-stale-value"); Thread.sleep(1010); // Allow cache entry to expire assertThat(cache.getIfPresent("foo")).isNull(); @@ -247,7 +251,7 @@ public void getShouldRefreshValueWhenExpired() throws Exception { String newCachedValue = UUID.randomUUID().toString(); ChronicleMapCacheImpl<String, String> cache = - newCache(true, newCachedValue, null, Duration.ofSeconds(1), 1); + newCache(true, TEST_CACHE_NAME, newCachedValue, null, Duration.ofSeconds(1), 1); cache.put("foo", "some-stale-value"); Thread.sleep(1010); // Allow cache to be flagged as needing refresh assertThat(cache.get("foo")).isEqualTo(newCachedValue); @@ -256,7 +260,7 @@ @Test public void shouldPruneExpiredValues() throws Exception { ChronicleMapCacheImpl<String, String> cache = - newCache(true, null, Duration.ofSeconds(1), null, 1); + newCache(true, TEST_CACHE_NAME, null, Duration.ofSeconds(1), null, 1); cache.put("foo1", "some-stale-value1"); cache.put("foo2", "some-stale-value1"); Thread.sleep(1010); // Allow cache entries to expire @@ -293,10 +297,10 @@ public void shouldEvictOldestElementInCacheWhenIsNeverAccessed() throws Exception { final String fooValue = "foo"; - gerritConfig.setInt("cache", "foo", "maxEntries", 2); - gerritConfig.setInt("cache", "foo", "percentageHotKeys", 10); - gerritConfig.setInt("cache", "foo", "avgKeySize", "foo1".getBytes().length); - gerritConfig.setInt("cache", "foo", "avgValueSize", valueSize(fooValue)); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "maxEntries", 2); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "percentageHotKeys", 10); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "avgKeySize", "foo1".getBytes().length); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "avgValueSize", valueSize(fooValue)); gerritConfig.save(); ChronicleMapCacheImpl<String, String> cache = newCacheWithLoader(fooValue); @@ -313,10 +317,10 @@ public void shouldEvictRecentlyInsertedElementInCacheWhenOldestElementIsAccessed() throws Exception { final String fooValue = "foo"; - gerritConfig.setInt("cache", "foo", "maxEntries", 2); - gerritConfig.setInt("cache", "foo", "percentageHotKeys", 10); - gerritConfig.setInt("cache", "foo", "avgKeySize", "foo1".getBytes().length); - gerritConfig.setInt("cache", "foo", "avgValueSize", valueSize(fooValue)); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "maxEntries", 2); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "percentageHotKeys", 10); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "avgKeySize", "foo1".getBytes().length); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "avgValueSize", valueSize(fooValue)); gerritConfig.save(); ChronicleMapCacheImpl<String, String> cache = newCacheWithLoader(fooValue); @@ -354,13 +358,13 @@ @Test public void shouldTriggerPercentageFreeMetric() throws Exception { String cachedValue = UUID.randomUUID().toString(); - String freeSpaceMetricName = "cache/chroniclemap/percentage_free_space_" + cachedValue; - gerritConfig.setInt("cache", cachedValue, "maxEntries", 2); - gerritConfig.setInt("cache", cachedValue, "avgKeySize", cachedValue.getBytes().length); - gerritConfig.setInt("cache", cachedValue, "avgValueSize", valueSize(cachedValue)); + String freeSpaceMetricName = "cache/chroniclemap/percentage_free_space_" + TEST_CACHE_NAME; + gerritConfig.setInt("cache", TEST_CACHE_NAME, "maxEntries", 2); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "avgKeySize", cachedValue.getBytes().length); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "avgValueSize", valueSize(cachedValue)); gerritConfig.save(); - ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(cachedValue); + ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(TEST_CACHE_NAME, cachedValue); assertThat(getMetric(freeSpaceMetricName).getValue()).isEqualTo(100); @@ -373,13 +377,13 @@ @Test public void shouldTriggerRemainingAutoResizeMetric() throws Exception { String cachedValue = UUID.randomUUID().toString(); - String autoResizeMetricName = "cache/chroniclemap/remaining_autoresizes_" + cachedValue; - gerritConfig.setInt("cache", cachedValue, "maxEntries", 2); - gerritConfig.setInt("cache", cachedValue, "avgKeySize", cachedValue.getBytes().length); - gerritConfig.setInt("cache", cachedValue, "avgValueSize", valueSize(cachedValue)); + String autoResizeMetricName = "cache/chroniclemap/remaining_autoresizes_" + TEST_CACHE_NAME; + gerritConfig.setInt("cache", TEST_CACHE_NAME, "maxEntries", 2); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "avgKeySize", cachedValue.getBytes().length); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "avgValueSize", valueSize(cachedValue)); gerritConfig.save(); - ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(cachedValue); + ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(TEST_CACHE_NAME, cachedValue); assertThat(getMetric(autoResizeMetricName).getValue()).isEqualTo(1); @@ -397,12 +401,12 @@ int percentageHotKeys = 60; int maxEntries = 10; int expectedCapacity = 6; - String hotKeysCapacityMetricName = "cache/chroniclemap/hot_keys_capacity_" + cachedValue; - gerritConfig.setInt("cache", cachedValue, "maxEntries", maxEntries); - gerritConfig.setInt("cache", cachedValue, "percentageHotKeys", percentageHotKeys); + String hotKeysCapacityMetricName = "cache/chroniclemap/hot_keys_capacity_" + TEST_CACHE_NAME; + gerritConfig.setInt("cache", TEST_CACHE_NAME, "maxEntries", maxEntries); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "percentageHotKeys", percentageHotKeys); gerritConfig.save(); - ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(cachedValue); + ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(TEST_CACHE_NAME, cachedValue); assertThat(getMetric(hotKeysCapacityMetricName).getValue()).isEqualTo(expectedCapacity); } @@ -414,12 +418,12 @@ int maxEntries = 10; int maxHotKeyCapacity = 3; final Duration METRIC_TRIGGER_TIMEOUT = Duration.ofSeconds(2); - String hotKeysSizeMetricName = "cache/chroniclemap/hot_keys_size_" + cachedValue; - gerritConfig.setInt("cache", cachedValue, "maxEntries", maxEntries); - gerritConfig.setInt("cache", cachedValue, "percentageHotKeys", percentageHotKeys); + String hotKeysSizeMetricName = "cache/chroniclemap/hot_keys_size_" + TEST_CACHE_NAME; + gerritConfig.setInt("cache", TEST_CACHE_NAME, "maxEntries", maxEntries); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "percentageHotKeys", percentageHotKeys); gerritConfig.save(); - ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(cachedValue); + ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(TEST_CACHE_NAME, cachedValue); assertThat(getMetric(hotKeysSizeMetricName).getValue()).isEqualTo(0); @@ -448,12 +452,12 @@ int maxEntries = 10; int maxHotKeyCapacity = 3; final Duration METRIC_TRIGGER_TIMEOUT = Duration.ofSeconds(2); - String hotKeysSizeMetricName = "cache/chroniclemap/hot_keys_size_" + cachedValue; - gerritConfig.setInt("cache", cachedValue, "maxEntries", maxEntries); - gerritConfig.setInt("cache", cachedValue, "percentageHotKeys", percentageHotKeys); + String hotKeysSizeMetricName = "cache/chroniclemap/hot_keys_size_" + TEST_CACHE_NAME; + gerritConfig.setInt("cache", TEST_CACHE_NAME, "maxEntries", maxEntries); + gerritConfig.setInt("cache", TEST_CACHE_NAME, "percentageHotKeys", percentageHotKeys); gerritConfig.save(); - ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(cachedValue); + ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(TEST_CACHE_NAME, cachedValue); for (int i = 0; i < maxHotKeyCapacity; i++) { cache.put(cachedValue + i, cachedValue); @@ -478,7 +482,7 @@ String autoResizeMetricName = "cache/chroniclemap/remaining_autoresizes_" + sanitized; String hotKeyCapacityMetricName = "cache/chroniclemap/hot_keys_capacity_" + sanitized; - newCacheWithMetrics(cacheName); + newCacheWithMetrics(cacheName, null); getMetric(hotKeySizeMetricName); getMetric(percentageFreeMetricName); @@ -487,44 +491,61 @@ } private int valueSize(String value) { - final TimedValueMarshaller<String> marshaller = - new TimedValueMarshaller<>(StringCacheSerializer.INSTANCE); + final TimedValueMarshaller<String> marshaller = new TimedValueMarshaller<>(TEST_CACHE_NAME); Bytes<ByteBuffer> out = Bytes.elasticByteBuffer(); marshaller.write(out, new TimedValue<>(value)); return out.toByteArray().length; } - private ChronicleMapCacheImpl<String, String> newCacheWithMetrics(String cachedValue) + private ChronicleMapCacheImpl<String, String> newCacheWithMetrics( + String cacheName, @Nullable String cachedValue) throws IOException { + return newCache(true, cacheName, cachedValue, null, null, null, null, 1, metricMaker); + } + + private ChronicleMapCacheImpl<String, String> newCacheWithMetrics( + String cacheName, + String cachedValue, + CacheSerializer<String> keySerializer, + CacheSerializer<String> valueSerializer) throws IOException { - return newCache(true, cachedValue, null, null, 1, metricMaker); + return newCache( + true, cacheName, cachedValue, null, null, null, null, 1, new DisabledMetricMaker()); } private ChronicleMapCacheImpl<String, String> newCache( Boolean withLoader, - @Nullable String cachedValue, + String cacheName, + @Nullable String loadedValue, @Nullable Duration expireAfterWrite, @Nullable Duration refreshAfterWrite, Integer version) throws IOException { return newCache( withLoader, - cachedValue, + cacheName, + loadedValue, expireAfterWrite, refreshAfterWrite, + null, + null, version, new DisabledMetricMaker()); } private ChronicleMapCacheImpl<String, String> newCache( Boolean withLoader, + String cacheName, @Nullable String cachedValue, @Nullable Duration expireAfterWrite, @Nullable Duration refreshAfterWrite, + @Nullable CacheSerializer<String> keySerializer, + @Nullable CacheSerializer<String> valueSerializer, Integer version, MetricMaker metricMaker) throws IOException { - TestPersistentCacheDef cacheDef = new TestPersistentCacheDef(cachedValue); + TestPersistentCacheDef cacheDef = + new TestPersistentCacheDef(cacheName, cachedValue, keySerializer, valueSerializer); File persistentFile = ChronicleMapCacheFactory.fileName( @@ -542,21 +563,21 @@ cacheDef, config, withLoader ? cacheDef.loader() : null, metricMaker); } - private ChronicleMapCacheImpl<String, String> newCacheWithLoader(@Nullable String cachedValue) + private ChronicleMapCacheImpl<String, String> newCacheWithLoader(@Nullable String loadedValue) throws IOException { - return newCache(true, cachedValue, null, null, 1); + return newCache(true, TEST_CACHE_NAME, loadedValue, null, null, 1); } private ChronicleMapCacheImpl<String, String> newCacheWithLoader() throws IOException { - return newCache(true, null, null, null, 1); + return newCache(true, TEST_CACHE_NAME, null, null, null, 1); } private ChronicleMapCacheImpl<String, String> newCacheVersion(int version) throws IOException { - return newCache(true, null, null, null, version); + return newCache(true, TEST_CACHE_NAME, null, null, null, version); } private ChronicleMapCacheImpl<String, String> newCacheWithoutLoader() throws IOException { - return newCache(false, null, null, null, 1); + return newCache(false, TEST_CACHE_NAME, null, null, null, 1); } private <V> Gauge<V> getMetric(String name) {
diff --git a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapMarshallerAdapterTest.java b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapMarshallerAdapterTest.java deleted file mode 100644 index 09f7f17..0000000 --- a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapMarshallerAdapterTest.java +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// limitations under the License. -// See the License for the specific language governing permissions and -package com.googlesource.gerrit.modules.cache.chroniclemap; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.gerrit.server.cache.serialize.ObjectIdCacheSerializer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import net.openhft.chronicle.bytes.Bytes; -import org.eclipse.jgit.lib.ObjectId; -import org.junit.Test; - -public class ChronicleMapMarshallerAdapterTest { - - @Test - public void shouldDeserializeToTheSameObject() { - ObjectId id = ObjectId.fromString("aabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); - byte[] serializedId = ObjectIdCacheSerializer.INSTANCE.serialize(id); - ByteBuffer serializedIdWithLen = ByteBuffer.allocate(serializedId.length + 4); - serializedIdWithLen.order(ByteOrder.LITTLE_ENDIAN); - serializedIdWithLen.putInt(serializedId.length); - serializedIdWithLen.put(serializedId); - - ChronicleMapMarshallerAdapter<ObjectId> marshaller = - new ChronicleMapMarshallerAdapter<>(ObjectIdCacheSerializer.INSTANCE); - ObjectId out = marshaller.read(Bytes.allocateDirect(serializedIdWithLen.array()), null); - - assertThat(id).isEqualTo(out); - } - - @Test - public void shouldSerializeToTheSameObject() { - ObjectId id = ObjectId.fromString("aabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); - ChronicleMapMarshallerAdapter<ObjectId> marshaller = - new ChronicleMapMarshallerAdapter<>(ObjectIdCacheSerializer.INSTANCE); - - Bytes<ByteBuffer> out = Bytes.elasticByteBuffer(); - marshaller.write(out, id); - assertThat(marshaller.read(out, null)).isEqualTo(id); - } -}
diff --git a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/KeyWrapperMarshallerTest.java b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/KeyWrapperMarshallerTest.java new file mode 100644 index 0000000..44a5dd6 --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/KeyWrapperMarshallerTest.java
@@ -0,0 +1,45 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.googlesource.gerrit.modules.cache.chroniclemap; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.server.cache.serialize.ObjectIdCacheSerializer; +import java.nio.ByteBuffer; +import net.openhft.chronicle.bytes.Bytes; +import org.eclipse.jgit.lib.ObjectId; +import org.junit.Before; +import org.junit.Test; + +public class KeyWrapperMarshallerTest { + private static final String TEST_CACHE_NAME = "key-wrapper-test"; + + @Before + public void setup() { + CacheSerializers.registerCacheKeySerializer(TEST_CACHE_NAME, ObjectIdCacheSerializer.INSTANCE); + } + + @Test + public void shouldSerializeAndDeserializeBack() { + ObjectId id = ObjectId.fromString("1234567890123456789012345678901234567890"); + KeyWrapperMarshaller<ObjectId> marshaller = new KeyWrapperMarshaller<>(TEST_CACHE_NAME); + + final KeyWrapper<ObjectId> wrapped = new KeyWrapper<>(id); + + Bytes<ByteBuffer> out = Bytes.elasticByteBuffer(); + marshaller.write(out, wrapped); + final KeyWrapper<ObjectId> actual = marshaller.read(out, null); + assertThat(actual).isEqualTo(wrapped); + } +}
diff --git a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/TestPersistentCacheDef.java b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/TestPersistentCacheDef.java index b81baec..0e52f1d 100644 --- a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/TestPersistentCacheDef.java +++ b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/TestPersistentCacheDef.java
@@ -21,42 +21,61 @@ import com.google.gerrit.server.cache.serialize.StringCacheSerializer; import com.google.inject.TypeLiteral; import java.time.Duration; +import java.util.Optional; import java.util.UUID; public class TestPersistentCacheDef implements PersistentCacheDef<String, String> { private static final Integer DEFAULT_DISK_LIMIT = 1024; + private final String name; private final String loadedValue; private final Duration expireAfterWrite; private final Duration refreshAfterWrite; private final Integer diskLimit; + private final CacheSerializer<String> keySerializer; + private final CacheSerializer<String> valueSerializer; public TestPersistentCacheDef( - String loadedValue, + String name, + @Nullable String loadedValue, @Nullable Duration expireAfterWrite, @Nullable Duration refreshAfterWrite) { + this.name = name; this.loadedValue = loadedValue; this.expireAfterWrite = expireAfterWrite; this.refreshAfterWrite = refreshAfterWrite; this.diskLimit = DEFAULT_DISK_LIMIT; + this.keySerializer = StringCacheSerializer.INSTANCE; + this.valueSerializer = StringCacheSerializer.INSTANCE; } - public TestPersistentCacheDef(String loadedValue, Integer diskLimit) { + public TestPersistentCacheDef(String name, @Nullable String loadedValue, Integer diskLimit) { + this.name = name; this.loadedValue = loadedValue; this.expireAfterWrite = null; this.refreshAfterWrite = null; this.diskLimit = diskLimit; + this.keySerializer = StringCacheSerializer.INSTANCE; + this.valueSerializer = StringCacheSerializer.INSTANCE; } - public TestPersistentCacheDef(String loadedValue) { + public TestPersistentCacheDef( + String name, + @Nullable String loadedValue, + @Nullable CacheSerializer<String> keySerializer, + @Nullable CacheSerializer<String> valueSerializer) { + this.name = name; this.loadedValue = loadedValue; this.expireAfterWrite = Duration.ZERO; this.refreshAfterWrite = Duration.ZERO; this.diskLimit = DEFAULT_DISK_LIMIT; + this.keySerializer = Optional.ofNullable(keySerializer).orElse(StringCacheSerializer.INSTANCE); + this.valueSerializer = + Optional.ofNullable(valueSerializer).orElse(StringCacheSerializer.INSTANCE); } @Override @@ -71,17 +90,17 @@ @Override public CacheSerializer<String> keySerializer() { - return StringCacheSerializer.INSTANCE; + return keySerializer; } @Override public CacheSerializer<String> valueSerializer() { - return StringCacheSerializer.INSTANCE; + return valueSerializer; } @Override public String name() { - return loadedValue; + return name; } @Override
diff --git a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/TimedValueMarshallerTest.java b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/TimedValueMarshallerTest.java index 62c1d44..b630e36 100644 --- a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/TimedValueMarshallerTest.java +++ b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/TimedValueMarshallerTest.java
@@ -19,16 +19,23 @@ import java.nio.ByteBuffer; import net.openhft.chronicle.bytes.Bytes; import org.eclipse.jgit.lib.ObjectId; +import org.junit.Before; import org.junit.Test; public class TimedValueMarshallerTest { + private static final String TEST_CACHE_NAME = "timed-value-cache"; + + @Before + public void setup() { + CacheSerializers.registerCacheValueSerializer( + TEST_CACHE_NAME, ObjectIdCacheSerializer.INSTANCE); + } @Test public void shouldSerializeAndDeserializeBack() { ObjectId id = ObjectId.fromString("1234567890123456789012345678901234567890"); long timestamp = 1600329018L; - TimedValueMarshaller<ObjectId> marshaller = - new TimedValueMarshaller<>(ObjectIdCacheSerializer.INSTANCE); + TimedValueMarshaller<ObjectId> marshaller = new TimedValueMarshaller<>(TEST_CACHE_NAME); final TimedValue<ObjectId> wrapped = new TimedValue<>(id, timestamp);