Skip to content

Commit

Permalink
Initial implementation for tiered node request cache stats
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Alfonsi committed Oct 20, 2023
1 parent 4680ea7 commit c300d8b
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<String, StatsHolder> 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<TierType, StatsHolder> 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;
}
Expand All @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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();
Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
15 changes: 13 additions & 2 deletions server/src/main/java/org/opensearch/indices/TierType.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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));
Expand Down

0 comments on commit c300d8b

Please sign in to comment.