diff --git a/server/src/main/java/org/opensearch/index/cache/request/RequestCacheStats.java b/server/src/main/java/org/opensearch/index/cache/request/RequestCacheStats.java index 5aa9f57b64032..5480dd5b01240 100644 --- a/server/src/main/java/org/opensearch/index/cache/request/RequestCacheStats.java +++ b/server/src/main/java/org/opensearch/index/cache/request/RequestCacheStats.java @@ -38,8 +38,11 @@ import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.core.xcontent.ToXContentFragment; import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.indices.TierType; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; /** * Request for the query cache statistics @@ -48,79 +51,117 @@ */ public class RequestCacheStats implements Writeable, ToXContentFragment { - private long memorySize; - private long evictions; - private long hitCount; - private long missCount; - private long entries; // number of entries in the cache - - public RequestCacheStats() {} + private Map map; + public RequestCacheStats() { + this.map = new HashMap<>(); + for (TierType tierType : TierType.values()) { + map.put(tierType.getStringValue(), new StatsHolder()); + // Every possible tier type must have counters, even if they are disabled. Then the counters report 0 + } + } public RequestCacheStats(StreamInput in) throws IOException { - memorySize = in.readVLong(); - evictions = in.readVLong(); - hitCount = in.readVLong(); - missCount = in.readVLong(); - entries = in.readVLong(); + // Any RequestCacheStats written to a stream should already have a counter for each possible tier type + this.map = in.readMap(StreamInput::readString, StatsHolder::new); // does it know to use the right constructor? does it rly need to be registered? + } + + public RequestCacheStats(TierType tierType, StatsHolder statsHolder) { + // Create a RequestCacheStats object with only one tier's statistics populated + this(); + map.put(tierType.getStringValue(), statsHolder); } - public RequestCacheStats(long memorySize, long evictions, long hitCount, long missCount, long entries) { // - this.memorySize = memorySize; - this.evictions = evictions; - this.hitCount = hitCount; - this.missCount = missCount; - this.entries = entries; + public RequestCacheStats(Map inputMap) { + // Create a RequestCacheStats with multiple tiers' statistics + this(); + for (TierType tierType : inputMap.keySet()) { + map.put(tierType.getStringValue(), inputMap.get(tierType)); + } } + // can prob eliminate some of these constructors + public void add(RequestCacheStats stats) { - this.memorySize += stats.memorySize; - this.evictions += stats.evictions; - this.hitCount += stats.hitCount; - this.missCount += stats.missCount; - this.entries += stats.entries; + for (String tier : stats.map.keySet()) { + map.get(tier).add(stats.map.get(tier)); + } + } + + private StatsHolder getTierStats(TierType tierType) { + return map.get(tierType.getStringValue()); + } + + // should these take in strings bc thats whats done in the xcontent builder? seems wasteful + public long getMemorySizeInBytes(TierType tierType) { + return getTierStats(tierType).totalMetric.count(); + } + + public ByteSizeValue getMemorySize(TierType tierType) { + return new ByteSizeValue(getMemorySizeInBytes(tierType)); } + public long getEvictions(TierType tierType) { + return getTierStats(tierType).evictionsMetric.count(); + } + + public long getHitCount(TierType tierType) { + return getTierStats(tierType).hitCount.count(); + } + + public long getMissCount(TierType tierType) { + return getTierStats(tierType).missCount.count(); + } + + public long getEntries(TierType tierType) { + return getTierStats(tierType).entries.count(); + } + + // By default, return on-heap stats if no tier is specified + public long getMemorySizeInBytes() { - return this.memorySize; + return getMemorySizeInBytes(TierType.ON_HEAP); } public ByteSizeValue getMemorySize() { - return new ByteSizeValue(memorySize); + return getMemorySize(TierType.ON_HEAP); } public long getEvictions() { - return this.evictions; + return getEvictions(TierType.ON_HEAP); } public long getHitCount() { - return this.hitCount; + return getHitCount(TierType.ON_HEAP); } public long getMissCount() { - return this.missCount; + return getMissCount(TierType.ON_HEAP); } public long getEntries() { - return this.entries; + return getEntries(TierType.ON_HEAP); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVLong(memorySize); - out.writeVLong(evictions); - out.writeVLong(hitCount); - out.writeVLong(missCount); - out.writeVLong(entries); + out.writeMap(this.map, StreamOutput::writeString, (o, v) -> v.writeTo(o)); // ? } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(Fields.REQUEST_CACHE_STATS); - builder.humanReadableField(Fields.MEMORY_SIZE_IN_BYTES, Fields.MEMORY_SIZE, getMemorySize()); - builder.field(Fields.EVICTIONS, getEvictions()); - builder.field(Fields.HIT_COUNT, getHitCount()); - builder.field(Fields.MISS_COUNT, getMissCount()); - builder.field(Fields.ENTRIES, getEntries()); + // write on-heap stats outside of tiers object + getTierStats(TierType.ON_HEAP).toXContent(builder, params); + builder.startObject(Fields.TIERS); + for (TierType tierType : TierType.values()) { // fixed order + if (tierType != TierType.ON_HEAP) { + String tier = tierType.getStringValue(); + builder.startObject(tier); + map.get(tier).toXContent(builder, params); + builder.endObject(); + } + } + builder.endObject(); builder.endObject(); return builder; } @@ -132,6 +173,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws */ static final class Fields { static final String REQUEST_CACHE_STATS = "request_cache"; + static final String TIERS = "tiers"; static final String MEMORY_SIZE = "memory_size"; static final String MEMORY_SIZE_IN_BYTES = "memory_size_in_bytes"; static final String EVICTIONS = "evictions"; diff --git a/server/src/main/java/org/opensearch/index/cache/request/ShardRequestCache.java b/server/src/main/java/org/opensearch/index/cache/request/ShardRequestCache.java index d43418d63a6a6..f8d77a4c0dbaf 100644 --- a/server/src/main/java/org/opensearch/index/cache/request/ShardRequestCache.java +++ b/server/src/main/java/org/opensearch/index/cache/request/ShardRequestCache.java @@ -33,11 +33,9 @@ package org.opensearch.index.cache.request; import org.apache.lucene.util.Accountable; -import org.opensearch.common.metrics.CounterMetric; import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.indices.TierType; -import java.io.Serializable; import java.util.EnumMap; /** @@ -57,40 +55,11 @@ public ShardRequestCache() { public RequestCacheStats stats() { // TODO: Change RequestCacheStats to support disk tier stats. - return stats(TierType.ON_HEAP); + // Changing this function to return a RequestCacheStats with stats from all tiers. + //return stats(TierType.ON_HEAP); + return new RequestCacheStats(statsHolder); } - public RequestCacheStats stats(TierType tierType) { - return new RequestCacheStats( - statsHolder.get(tierType).totalMetric.count(), - statsHolder.get(tierType).evictionsMetric.count(), - statsHolder.get(tierType).hitCount.count(), - statsHolder.get(tierType).missCount.count(), - statsHolder.get(tierType).entries.count() - ); - } - - public RequestCacheStats overallStats() { - long totalSize = 0; - long totalEvictions = 0; - long totalHits = 0; - long totalMisses = 0; - long totalEntries = 0; - for (TierType tierType : TierType.values()) { - totalSize += statsHolder.get(tierType).totalMetric.count(); - totalEvictions += statsHolder.get(tierType).evictionsMetric.count(); - totalHits += statsHolder.get(tierType).hitCount.count(); - totalMisses += statsHolder.get(tierType).missCount.count(); - totalEntries += statsHolder.get(tierType).entries.count(); - } - return new RequestCacheStats( - totalSize, - totalEvictions, - totalHits, - totalMisses, - totalEntries - ); - } public void onHit(TierType tierType) { statsHolder.get(tierType).hitCount.inc(); @@ -119,13 +88,4 @@ public void onRemoval(Accountable key, BytesReference value, boolean evicted, Ti statsHolder.get(tierType).totalMetric.dec(dec); statsHolder.get(tierType).entries.dec(); } - - static class StatsHolder implements Serializable { - - final CounterMetric evictionsMetric = new CounterMetric(); - final CounterMetric totalMetric = new CounterMetric(); - final CounterMetric hitCount = new CounterMetric(); - final CounterMetric missCount = new CounterMetric(); - final CounterMetric entries = new CounterMetric(); - } } diff --git a/server/src/main/java/org/opensearch/index/cache/request/StatsHolder.java b/server/src/main/java/org/opensearch/index/cache/request/StatsHolder.java new file mode 100644 index 0000000000000..86f99871fea26 --- /dev/null +++ b/server/src/main/java/org/opensearch/index/cache/request/StatsHolder.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.index.cache.request; + +import org.opensearch.common.metrics.CounterMetric; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; +import java.io.Serializable; + +public class StatsHolder implements Serializable, Writeable, ToXContentFragment { + final CounterMetric evictionsMetric; + final CounterMetric totalMetric; + final CounterMetric hitCount; + final CounterMetric missCount; + final CounterMetric entries; + + + public StatsHolder() { + this.evictionsMetric = new CounterMetric(); + this.totalMetric = new CounterMetric(); + this.hitCount = new CounterMetric(); + this.missCount = new CounterMetric(); + this.entries = new CounterMetric(); + } + + public StatsHolder(StreamInput in) throws IOException { + // Read and write the values of the counter metrics. They should always be positive + this.evictionsMetric = new CounterMetric(); + this.evictionsMetric.inc(in.readVLong()); + this.totalMetric = new CounterMetric(); + this.totalMetric.inc(in.readVLong()); + this.hitCount = new CounterMetric(); + this.hitCount.inc(in.readVLong()); + this.missCount = new CounterMetric(); + this.missCount.inc(in.readVLong()); + this.entries = new CounterMetric(); + this.entries.inc(in.readVLong()); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeVLong(evictionsMetric.count()); + out.writeVLong(totalMetric.count()); + out.writeVLong(hitCount.count()); + out.writeVLong(missCount.count()); + out.writeVLong(entries.count()); + } + + public void add(StatsHolder otherStats) { + // Add the argument's metrics to this object's metrics. + evictionsMetric.inc(otherStats.evictionsMetric.count()); + totalMetric.inc(otherStats.totalMetric.count()); + hitCount.inc(otherStats.hitCount.count()); + missCount.inc(otherStats.missCount.count()); + entries.inc(otherStats.entries.count()); + } + + public long getEvictions() { + return evictionsMetric.count(); + } + + public long getMemorySize() { + return totalMetric.count(); + } + + public long getHitCount() { + return hitCount.count(); + } + + public long getMissCount() { + return missCount.count(); + } + + public long getEntries() { + return entries.count(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.humanReadableField(RequestCacheStats.Fields.MEMORY_SIZE_IN_BYTES, RequestCacheStats.Fields.MEMORY_SIZE, new ByteSizeValue(getMemorySize())); + builder.field(RequestCacheStats.Fields.EVICTIONS, getEvictions()); + builder.field(RequestCacheStats.Fields.HIT_COUNT, getHitCount()); + builder.field(RequestCacheStats.Fields.MISS_COUNT, getMissCount()); + builder.field(RequestCacheStats.Fields.ENTRIES, getEntries()); + return builder; + } +} diff --git a/server/src/main/java/org/opensearch/indices/TierType.java b/server/src/main/java/org/opensearch/indices/TierType.java index 9a286fd26151b..900df2fce57c5 100644 --- a/server/src/main/java/org/opensearch/indices/TierType.java +++ b/server/src/main/java/org/opensearch/indices/TierType.java @@ -10,6 +10,17 @@ public enum TierType { - ON_HEAP, - DISK; + ON_HEAP("on_heap"), + DISK("disk"); + + private final String stringValue; + + TierType(String stringValue) { + // Associate each TierType with a string representation, for use in API responses and elsewhere + this.stringValue = stringValue; + } + + public String getStringValue() { + return this.stringValue; + } } diff --git a/server/src/test/java/org/opensearch/indices/IndicesRequestCacheTests.java b/server/src/test/java/org/opensearch/indices/IndicesRequestCacheTests.java index 2bd2bd53a787a..6e75a0e8102c2 100644 --- a/server/src/test/java/org/opensearch/indices/IndicesRequestCacheTests.java +++ b/server/src/test/java/org/opensearch/indices/IndicesRequestCacheTests.java @@ -196,8 +196,8 @@ public void testSpillover() throws Exception { assertEquals(maxNumInHeap * heapKeySize, requestCacheStats.stats().getMemorySizeInBytes()); // TODO: disk weight bytes assertEquals(1, requestCacheStats.stats().getEvictions()); - assertEquals(1, requestCacheStats.stats(TierType.DISK).getHitCount()); - assertEquals(maxNumInHeap + 1, requestCacheStats.stats(TierType.DISK).getMissCount()); + assertEquals(1, requestCacheStats.stats().getHitCount(TierType.DISK)); + assertEquals(maxNumInHeap + 1, requestCacheStats.stats().getMissCount(TierType.DISK)); assertEquals(0, requestCacheStats.stats().getHitCount()); assertEquals(maxNumInHeap + 2, requestCacheStats.stats().getMissCount()); assertEquals(maxNumInHeap, cache.tieredCacheHandler.count(TierType.ON_HEAP)); @@ -209,8 +209,8 @@ public void testSpillover() throws Exception { BytesReference firstValueAgain = cache.getOrCompute(entity, loader, reader, termBytesArr[0]); assertEquals(1, requestCacheStats.stats().getEvictions()); - assertEquals(2, requestCacheStats.stats(TierType.DISK).getHitCount()); - assertEquals(maxNumInHeap + 1, requestCacheStats.stats(TierType.DISK).getMissCount()); + assertEquals(2, requestCacheStats.stats().getHitCount(TierType.DISK)); + assertEquals(maxNumInHeap + 1, requestCacheStats.stats().getMissCount(TierType.DISK)); assertEquals(1, requestCacheStats.stats().getHitCount()); assertEquals(maxNumInHeap + 3, requestCacheStats.stats().getMissCount()); assertEquals(maxNumInHeap, cache.tieredCacheHandler.count(TierType.ON_HEAP));