Refactor structure of caching metrics and expose auth cache metrics via JMX

patch by Aleksei Zotov; reviewed by Stefan Miklosovic, Maxim Muzafarov for CASSANDRA-17062

Co-authored-by: Sam Tunnicliffe <samt@apache.org>
Co-authored-by: Stefan Miklosovic <smiklosovic@apache.org>
This commit is contained in:
Aleksei Zotov 2024-01-25 17:27:08 +01:00 committed by Stefan Miklosovic
parent 097c1231e2
commit 64e2a4e9a3
No known key found for this signature in database
GPG Key ID: 32F35CB2F546D93E
10 changed files with 491 additions and 84 deletions

View File

@ -1,4 +1,5 @@
5.1
* Refactor structure of caching metrics and expose auth cache metrics via JMX (CASSANDRA-17062)
* Allow CQL client certificate authentication to work without sending an AUTHENTICATE request (CASSANDRA-18857)
* Extend nodetool tpstats and system_views.thread_pools with detailed pool parameters (CASSANDRA-19289)
* Remove dependency on Sigar in favor of OSHI (CASSANDRA-16565)

View File

@ -528,9 +528,16 @@ ViewReplicasSuccess.
base table and when CL.ONE is achieved on view.
|===
== Cache Metrics
== Weighted Cache Metrics
Cassandra caches have metrics to track the effectivness of the caches.
[NOTE]
====
The non-auth caches are weighted (they do care about weight of a single entry) whereas the auth caches are
non-weighted. Particularly it affects Capacity and Size attributes of the MBeans.
For weighted caches they represent size of entries in bytes, for non-weighted caches they represent number of entries.
====
Cassandra caches have metrics to track the effectivness of the weighted caches.
Though the `Table Metrics` might be more useful.
Reported name format:
@ -538,7 +545,7 @@ Reported name format:
*Metric Name*::
`org.apache.cassandra.metrics.Cache.<MetricName>.<CacheName>`
*JMX MBean*::
`org.apache.cassandra.metrics:type=Cache scope=<CacheName> name=<MetricName>`
`org.apache.cassandra.metrics:type=Cache,scope=<CacheName>,name=<MetricName>`
[cols=",,",options="header",]
|===
@ -569,8 +576,57 @@ The following caches are covered:
[NOTE]
====
Misses and MissLatency are only defined for the ChunkCache
* Misses and MissLatency are only defined for the ChunkCache
* ChunkCache MBean is only available if the cache is enabled
====
== Unweighted Cache Metrics
Cassandra caches have metrics to track the effectivness of the unweighted caches.
Reported name format:
*Metric Name*::
`org.apache.cassandra.metrics.UnweightedCache.<MetricName>.<CacheName>`
*JMX MBean*::
`org.apache.cassandra.metrics:type=UnweightedCache,scope=<CacheName>,name=<MetricName>`
[cols=",,",options="header",]
|===
|Name |Type |Description
|MaxEntries |Gauge<Integer> |Maximum number of entries.
|Entries |Gauge<Integer> |Total number of cache entries.
|FifteenMinuteCacheHitRate |Gauge<Double> |15m cache hit rate.
|FiveMinuteCacheHitRate |Gauge<Double> |5m cache hit rate.
|OneMinuteCacheHitRate |Gauge<Double> |1m cache hit rate.
|HitRate |Gauge<Double> |All time cache hit rate.
|Hits |Meter |Total number of cache hits.
|Misses |Meter |Total number of cache misses.
|MissLatency |Timer |Latency of misses.
|Requests |Gauge<Long> |Total number of cache requests.
|===
The following caches are covered:
[cols=",",options="header",]
|===
|Name |Description
|CredentialsCache |Auth cache for credentials.
|JmxPermissionsCache |Auth cache for JMX permissions.
|CIDRPermissionsCache | Auth cache for CIDR permissions.
|IdentityCache| Auth cache for `MutualTlsAuthenticator`.
|NetworkPermissionsCache |Auth cache for network permissions.
|PermissionsCache |Auth cache for permissions.
|RolesCache |Auth cache for roles.
|===
[NOTE]
.Note
====
AuthCache metrics MBeans are only available if corresponding authorizers and authenticators are enabled.
====
== CQL Metrics
Metrics specific to CQL prepared statement caching.

View File

@ -33,6 +33,7 @@ import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.Uninterruptibles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -40,9 +41,14 @@ import org.slf4j.LoggerFactory;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Policy;
import com.github.benmanes.caffeine.cache.RemovalCause;
import org.apache.cassandra.cache.UnweightedCacheSize;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import com.github.benmanes.caffeine.cache.stats.StatsCounter;
import org.apache.cassandra.concurrent.ExecutorPlus;
import org.apache.cassandra.concurrent.ScheduledExecutors;
import org.apache.cassandra.concurrent.Shutdownable;
import org.apache.cassandra.metrics.UnweightedCacheMetrics;
import org.apache.cassandra.utils.ExecutorUtils;
import org.apache.cassandra.utils.MBeanWrapper;
@ -52,13 +58,13 @@ import static org.apache.cassandra.config.CassandraRelevantProperties.AUTH_CACHE
import static org.apache.cassandra.config.CassandraRelevantProperties.AUTH_CACHE_WARMING_RETRY_INTERVAL_MS;
import static org.apache.cassandra.config.CassandraRelevantProperties.DISABLE_AUTH_CACHES_REMOTE_CONFIGURATION;
public class AuthCache<K, V> implements AuthCacheMBean, Shutdownable
public class AuthCache<K, V> implements AuthCacheMBean, UnweightedCacheSize, Shutdownable
{
private static final Logger logger = LoggerFactory.getLogger(AuthCache.class);
public static final String MBEAN_NAME_BASE = "org.apache.cassandra.auth:type=";
private volatile ScheduledFuture cacheRefresher = null;
private volatile ScheduledFuture<?> cacheRefresher = null;
// Keep a handle on created instances so their executors can be terminated cleanly
private static final Set<Shutdownable> REGISTRY = new HashSet<>(4);
@ -93,6 +99,8 @@ public class AuthCache<K, V> implements AuthCacheMBean, Shutdownable
// values until the natural expiry time.
private final BiPredicate<K, V> invalidateCondition;
private final UnweightedCacheMetrics metrics;
/**
* @param name Used for MBean
* @param setValidityDelegate Used to set cache validity period. See {@link Policy#expireAfterWrite()}
@ -176,6 +184,7 @@ public class AuthCache<K, V> implements AuthCacheMBean, Shutdownable
this.bulkLoadFunction = checkNotNull(bulkLoadFunction);
this.enableCache = checkNotNull(cacheEnabledDelegate);
this.invalidateCondition = checkNotNull(invalidationCondition);
this.metrics = new UnweightedCacheMetrics(name, this);
init();
}
@ -215,7 +224,7 @@ public class AuthCache<K, V> implements AuthCacheMBean, Shutdownable
/**
* Retrieve a value from the cache. Will call {@link LoadingCache#get(Object)} which will
* "load" the value if it's not present, thus populating the key.
* @param k
* @param k key
* @return The current value of {@code K} if cached or loaded.
*
* See {@link LoadingCache#get(Object)} for possible exceptions.
@ -284,7 +293,7 @@ public class AuthCache<K, V> implements AuthCacheMBean, Shutdownable
/**
* Set maximum number of entries in the cache.
* @param maxEntries
* @param maxEntries max number of entries
*/
public synchronized void setMaxEntries(int maxEntries)
{
@ -319,6 +328,11 @@ public class AuthCache<K, V> implements AuthCacheMBean, Shutdownable
return cache == null ? 0L : cache.estimatedSize();
}
public UnweightedCacheMetrics getMetrics()
{
return metrics;
}
/**
* (Re-)initialise the underlying cache. Will update validity, max entries, and update interval if
* any have changed. The underlying {@link LoadingCache} will be initiated based on the provided {@code loadFunction}.
@ -345,6 +359,7 @@ public class AuthCache<K, V> implements AuthCacheMBean, Shutdownable
.expireAfterWrite(getValidity(), TimeUnit.MILLISECONDS)
.maximumSize(getMaxEntries())
.executor(cacheRefreshExecutor)
.recordStats(MetricsUpdater::new)
.build(loadFunction::apply);
}
else
@ -439,4 +454,48 @@ public class AuthCache<K, V> implements AuthCacheMBean, Shutdownable
return Collections::emptyMap;
}
}
@Override
public int maxEntries()
{
return getMaxEntries();
}
@Override
public int entries()
{
return Ints.checkedCast(getEstimatedSize());
}
private class MetricsUpdater implements StatsCounter
{
@Override
public void recordHits(int i)
{
metrics.requests.mark(i);
metrics.hits.mark(i);
}
@Override
public void recordMisses(int i)
{
metrics.requests.mark(i);
metrics.misses.mark(i);
}
@Override
public void recordLoadSuccess(long l) {}
@Override
public void recordLoadFailure(long l) {}
@Override
public void recordEviction(int i, RemovalCause removalCause) {}
@Override
public CacheStats snapshot()
{
return CacheStats.of(metrics.hits.getCount(), metrics.misses.getCount(), 0, 0L, 0, 0L, 0L);
}
}
}

View File

@ -20,15 +20,31 @@
*/
package org.apache.cassandra.cache;
/**
* Represents a weighted cache size.
*
* The non-auth caches are weighted (they do care about weight of a single entry) whereas the auth caches are
* unweighted.
*/
public interface CacheSize
{
/**
* Returns the maximum total weighted size of this cache.
*/
long capacity();
/**
* Specifies the maximum total weighted size of this cache.
*/
void setCapacity(long capacity);
/**
* Returns the approximate number of entries in this cache.
*/
int size();
/**
* Returns the approximate accumulated weight of entries in this cache.
*/
long weightedSize();
}

View File

@ -43,8 +43,7 @@ import org.apache.cassandra.metrics.ChunkCacheMetrics;
import org.apache.cassandra.utils.memory.BufferPool;
import org.apache.cassandra.utils.memory.BufferPools;
public class ChunkCache
implements CacheLoader<ChunkCache.Key, ChunkCache.Buffer>, RemovalListener<ChunkCache.Key, ChunkCache.Buffer>, CacheSize
public class ChunkCache implements CacheLoader<ChunkCache.Key, ChunkCache.Buffer>, RemovalListener<ChunkCache.Key, ChunkCache.Buffer>, CacheSize
{
public static final int RESERVED_POOL_SPACE_IN_MiB = 32;
public static final long cacheSize = 1024L * 1024L * Math.max(0, DatabaseDescriptor.getFileCacheSizeInMiB() - RESERVED_POOL_SPACE_IN_MiB);
@ -172,7 +171,7 @@ public class ChunkCache
buffer.release();
}
public void close()
public void clear()
{
cache.invalidateAll();
}

View File

@ -0,0 +1,45 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cassandra.cache;
/**
* Represents an unweighted cache size.
*
* The non-auth caches are weighted (they do care about weight of a single entry) whereas the auth caches are
* unweighted.
*/
public interface UnweightedCacheSize
{
/**
* Returns the maximum number of entries in this cache.
*/
int maxEntries();
/**
* Specifies the maximum number of entries in this cache.
*/
void setMaxEntries(int maxEntries);
/**
* Returns the approximate number of entries in this cache.
*/
int entries();
}

View File

@ -0,0 +1,113 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cassandra.metrics;
import java.util.function.DoubleSupplier;
import com.google.common.annotations.VisibleForTesting;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter;
import com.codahale.metrics.RatioGauge;
import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics;
/**
* Base class for metrics for weighted or unweighted caches.
*/
public abstract class AbstractCacheMetrics
{
/**
* Type of the cache, basically its name
*/
public final String type;
/**
* Total number of cache hits
*/
public final Meter hits;
/**
* Total number of cache misses
*/
public final Meter misses;
/**
* Total number of cache requests
*/
public final Meter requests;
/**
* all time cache hit rate
*/
public final Gauge<Double> hitRate;
/**
* 1m hit rate
*/
public final Gauge<Double> oneMinuteHitRate;
/**
* 5m hit rate
*/
public final Gauge<Double> fiveMinuteHitRate;
/**
* 15m hit rate
*/
public final Gauge<Double> fifteenMinuteHitRate;
protected final MetricNameFactory factory;
protected AbstractCacheMetrics(MetricNameFactory metricNameFactory, String type)
{
this.type = type;
factory = metricNameFactory;
hits = Metrics.meter(factory.createMetricName("Hits"));
misses = Metrics.meter(factory.createMetricName("Misses"));
requests = Metrics.meter(factory.createMetricName("Requests"));
hitRate = Metrics.register(factory.createMetricName("HitRate"),
ratioGauge(hits::getCount, requests::getCount));
oneMinuteHitRate = Metrics.register(factory.createMetricName("OneMinuteHitRate"),
ratioGauge(hits::getOneMinuteRate, requests::getOneMinuteRate));
fiveMinuteHitRate = Metrics.register(factory.createMetricName("FiveMinuteHitRate"),
ratioGauge(hits::getFiveMinuteRate, requests::getFiveMinuteRate));
fifteenMinuteHitRate = Metrics.register(factory.createMetricName("FifteenMinuteHitRate"),
ratioGauge(hits::getFifteenMinuteRate, requests::getFifteenMinuteRate));
}
@VisibleForTesting
public void reset()
{
// No actual reset happens. The Meter counter is put to zero but will not reset the moving averages
// It rather injects a weird value into them.
// This method is being only used by CacheMetricsTest and CachingBench so fixing this issue was acknowledged
// but not considered mandatory to be fixed now (CASSANDRA-16228)
hits.mark(-hits.getCount());
misses.mark(-misses.getCount());
requests.mark(-requests.getCount());
}
private static RatioGauge ratioGauge(DoubleSupplier numeratorSupplier, DoubleSupplier denominatorSupplier)
{
return new RatioGauge()
{
@Override
public Ratio getRatio()
{
return Ratio.of(numeratorSupplier.getAsDouble(), denominatorSupplier.getAsDouble());
}
};
}
}

View File

@ -17,11 +17,7 @@
*/
package org.apache.cassandra.metrics;
import java.util.function.DoubleSupplier;
import com.google.common.annotations.VisibleForTesting;
import com.codahale.metrics.*;
import com.codahale.metrics.Gauge;
import org.apache.cassandra.cache.CacheSize;
import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics;
@ -29,86 +25,28 @@ import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics;
/**
* Metrics for {@code ICache}.
*/
public class CacheMetrics
public class CacheMetrics extends AbstractCacheMetrics
{
/** Cache capacity in bytes */
public final Gauge<Long> capacity;
/** Total size of cache, in bytes */
public final Gauge<Long> size;
/** Total number of cache entries */
public final Gauge<Integer> entries;
/** Total number of cache hits */
public final Meter hits;
/** Total number of cache misses */
public final Meter misses;
/** Total number of cache requests */
public final Meter requests;
/** all time cache hit rate */
public final Gauge<Double> hitRate;
/** 1m hit rate */
public final Gauge<Double> oneMinuteHitRate;
/** 5m hit rate */
public final Gauge<Double> fiveMinuteHitRate;
/** 15m hit rate */
public final Gauge<Double> fifteenMinuteHitRate;
protected final MetricNameFactory factory;
/**
* Create metrics for given cache.
*
* @param type Type of Cache to identify metrics.
* @param cache Cache to measure metrics
* @param type Type of Cache to identify metrics
* @param cache Weighted Cache to measure metrics
*/
public CacheMetrics(String type, CacheSize cache)
{
factory = new DefaultNameFactory("Cache", type);
super(new DefaultNameFactory("Cache", type), type);
capacity = Metrics.register(factory.createMetricName("Capacity"), cache::capacity);
size = Metrics.register(factory.createMetricName("Size"), cache::weightedSize);
entries = Metrics.register(factory.createMetricName("Entries"), cache::size);
hits = Metrics.meter(factory.createMetricName("Hits"));
misses = Metrics.meter(factory.createMetricName("Misses"));
requests = Metrics.meter(factory.createMetricName("Requests"));
hitRate =
Metrics.register(factory.createMetricName("HitRate"),
ratioGauge(hits::getCount, requests::getCount));
oneMinuteHitRate =
Metrics.register(factory.createMetricName("OneMinuteHitRate"),
ratioGauge(hits::getOneMinuteRate, requests::getOneMinuteRate));
fiveMinuteHitRate =
Metrics.register(factory.createMetricName("FiveMinuteHitRate"),
ratioGauge(hits::getFiveMinuteRate, requests::getFiveMinuteRate));
fifteenMinuteHitRate =
Metrics.register(factory.createMetricName("FifteenMinuteHitRate"),
ratioGauge(hits::getFifteenMinuteRate, requests::getFifteenMinuteRate));
}
@VisibleForTesting
public void reset()
{
// No actual reset happens. The Meter counter is put to zero but will not reset the moving averages
// It rather injects a weird value into them.
// This method is being only used by CacheMetricsTest and CachingBench so fixing this issue was acknowledged
// but not considered mandatory to be fixed now (CASSANDRA-16228)
hits.mark(-hits.getCount());
misses.mark(-misses.getCount());
requests.mark(-requests.getCount());
}
private static RatioGauge ratioGauge(DoubleSupplier numeratorSupplier, DoubleSupplier denominatorSupplier)
{
return new RatioGauge()
{
@Override
public Ratio getRatio()
{
return Ratio.of(numeratorSupplier.getAsDouble(), denominatorSupplier.getAsDouble());
}
};
}
}

View File

@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cassandra.metrics;
import com.codahale.metrics.Gauge;
import org.apache.cassandra.cache.UnweightedCacheSize;
import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics;
/**
* Metrics for an unweighted cache like {@code AuthCache}.
*/
public class UnweightedCacheMetrics extends AbstractCacheMetrics
{
/**
* Cache capacity (maximum number of entries)
*/
public final Gauge<Integer> maxEntries;
/**
* Total number of cache entries
*/
public final Gauge<Integer> entries;
/**
* Create metrics for given cache.
*
* @param type Type of Cache to identify metrics
* @param cache Unweighted Cache to measure metrics
*/
public UnweightedCacheMetrics(String type, UnweightedCacheSize cache)
{
super(new DefaultNameFactory("UnweightedCache", type), type);
maxEntries = Metrics.register(factory.createMetricName("MaxEntries"), cache::maxEntries);
entries = Metrics.register(factory.createMetricName("Entries"), cache::entries);
}
}

View File

@ -20,6 +20,7 @@ package org.apache.cassandra.auth;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BooleanSupplier;
@ -28,6 +29,8 @@ import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.apache.cassandra.db.ConsistencyLevel;
@ -50,6 +53,12 @@ public class AuthCacheTest
private final int MAX_ENTRIES = 10;
@After
public void afterTest() throws Throwable
{
AuthCache.shutdownAllAndWait(1, TimeUnit.MINUTES);
}
@Test
public void testCacheLoaderIsCalledOnFirst()
{
@ -322,6 +331,107 @@ public class AuthCacheTest
assertEquals(1, loadCounter);
}
@Test
public void testMetricsOnCacheEnabled()
{
TestCache authCache = new TestCache(this::countingLoader, this::emptyBulkLoader, this::setValidity, () -> validity, () -> isCacheEnabled);
Assert.assertEquals(10, authCache.getMaxEntries());
Assert.assertEquals(0, authCache.entries());
authCache.get("10");
authCache.get("11");
assertThat(authCache.getMetrics().requests.getCount()).isEqualTo(2L);
assertThat(authCache.getMetrics().hits.getCount()).isEqualTo(0L);
assertThat(authCache.getMetrics().misses.getCount()).isEqualTo(2L);
assertEquals(2, loadCounter);
Assert.assertEquals(10, authCache.getMaxEntries());
Assert.assertEquals(2, authCache.entries());
authCache.get("10");
authCache.get("11");
Assert.assertEquals(10, authCache.getMaxEntries());
Assert.assertEquals(2, authCache.entries());
assertThat(authCache.getMetrics().requests.getCount()).isEqualTo(4L);
assertThat(authCache.getMetrics().hits.getCount()).isEqualTo(2L);
assertThat(authCache.getMetrics().misses.getCount()).isEqualTo(2L);
assertEquals(2, loadCounter);
Assert.assertEquals(10, authCache.getMaxEntries());
Assert.assertEquals(2, authCache.entries());
authCache.getAll();
assertThat(authCache.getMetrics().requests.getCount()).isEqualTo(4L);
assertThat(authCache.getMetrics().hits.getCount()).isEqualTo(2L);
assertThat(authCache.getMetrics().misses.getCount()).isEqualTo(2L);
assertEquals(2, loadCounter);
}
@Test
public void testMetricsOnCacheDisabled()
{
isCacheEnabled = false;
TestCache authCache = new TestCache(this::countingLoader, this::emptyBulkLoader, this::setValidity, () -> validity, () -> isCacheEnabled);
authCache.get("10");
authCache.get("11");
assertThat(authCache.getMetrics().requests.getCount()).isEqualTo(0L);
assertThat(authCache.getMetrics().hits.getCount()).isEqualTo(0L);
assertThat(authCache.getMetrics().misses.getCount()).isEqualTo(0L);
assertEquals(2, loadCounter);
Assert.assertEquals(10, authCache.getMaxEntries());
Assert.assertEquals(0, authCache.entries());
authCache.getAll();
assertThat(authCache.getMetrics().requests.getCount()).isEqualTo(0L);
assertThat(authCache.getMetrics().hits.getCount()).isEqualTo(0L);
assertThat(authCache.getMetrics().misses.getCount()).isEqualTo(0L);
assertEquals(2, loadCounter);
Assert.assertEquals(10, authCache.getMaxEntries());
Assert.assertEquals(0, authCache.entries());
}
@Test
public void testSettingMaxEntries()
{
AtomicInteger maxEntries = new AtomicInteger(10);
TestCache authCache = new TestCache(this::countingLoader,
this::emptyBulkLoader,
this::setValidity,
() -> validity,
() -> isCacheEnabled,
maxEntries::set,
maxEntries::get);
Assert.assertEquals(10, authCache.getMaxEntries());
Assert.assertEquals(0, authCache.entries());
authCache.get("10");
authCache.get("11");
Assert.assertEquals(10, authCache.getMaxEntries());
Assert.assertEquals(2, authCache.entries());
authCache.setMaxEntries(20);
Assert.assertEquals(20, authCache.getMaxEntries());
Assert.assertEquals(2, authCache.entries());
authCache.invalidate();
authCache.cleanup();
Assert.assertEquals(20, authCache.getMaxEntries());
Assert.assertEquals(0, authCache.entries());
}
private void setValidity(int validity)
{
this.validity = validity;
@ -384,14 +494,31 @@ public class AuthCacheTest
IntConsumer setValidityDelegate,
IntSupplier getValidityDelegate,
BooleanSupplier cacheEnabledDelegate)
{
this(loadFunction,
bulkLoadFunction,
setValidityDelegate,
getValidityDelegate,
cacheEnabledDelegate,
(MAX_ENTRIES) -> {},
() -> 10);
}
TestCache(Function<String, Integer> loadFunction,
Supplier<Map<String, Integer>> bulkLoadFunction,
IntConsumer setValidityDelegate,
IntSupplier getValidityDelegate,
BooleanSupplier cacheEnabledDelegate,
IntConsumer maxEntriesSetter,
IntSupplier maxEntriesGetter)
{
super("TestCache" + nameCounter++,
setValidityDelegate,
getValidityDelegate,
(updateInterval) -> {}, // set update interval
() -> 1000, // get update interval
(MAX_ENTRIES) -> {}, // set max entries
() -> 10, // get max entries
maxEntriesSetter, // set max entries
maxEntriesGetter, // get max entries
(updateActiveUpdate) -> {}, // set active update enabled
() -> false, // get active update enabled
loadFunction,