mirror of https://github.com/apache/cassandra
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:
parent
097c1231e2
commit
64e2a4e9a3
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue