Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tiered stats to request cache response #8

Draft
wants to merge 10 commits into
base: framework-serialized
Choose a base branch
from

Conversation

peteralfonsi
Copy link
Owner

Description

Modifies the request cache's API to return statistics for additional cache tiers, like the upcoming disk tier. Also adds the number of entries to the response. Stats for the existing on-heap tier stayed where they were, in the "request_cache" object. This object has a new field, the "tiers" object. Each new tier, besides the on-heap tier, will have its stats returned here. If a certain tier is not enabled, its statistics will still be returned, with all its values set to 0.

Calling _nodes/stats/indices/request_cache now returns the following:

  "_nodes": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "cluster_name": "runTask",
  "nodes": {
    "3Xx_SnhhQACQu5jW9UJGrQ": {
      "timestamp": 1698099482892,
      "name": "runTask-0",
      "transport_address": "127.0.0.1:9300",
      "host": "127.0.0.1",
      "ip": "127.0.0.1:9300",
      "roles": [
        "cluster_manager",
        "data",
        "ingest",
        "remote_cluster_client"
      ],
      "attributes": {
        "testattr": "test",
        "shard_indexing_pressure_enabled": "true"
      },
      "indices": {
        "request_cache": {
          "memory_size_in_bytes": 0,
          "evictions": 0,
          "hit_count": 0,
          "miss_count": 0,
          "entries": 0,
          "tiers": {
            "disk": {
              "memory_size_in_bytes": 0,
              "evictions": 0,
              "hit_count": 0,
              "miss_count": 0,
              "entries": 0
            }
          }
        }
      }
    }
  }
}

Tested with unit tests for the overhauled RequestCacheStats, an integration test, and manual testing with the API.

Related Issues

Part of larger tiered caching feature.

Check List

  • New functionality includes testing.
    • All tests pass
  • New functionality has been documented.
    • New functionality has javadoc added
  • Commits are signed per the DCO using --signoff
  • [N/A] Commit changes are listed out in CHANGELOG.md file (See: Changelog)
  • Public documentation issue/PR created

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@peteralfonsi peteralfonsi marked this pull request as draft October 23, 2023 22:39
private long evictions;
private long hitCount;
private long missCount;
private Map<String, StatsHolder> map;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe initialize this map inline here. This way you don't need to worry about this not being intialized.
Like

map = new HashMap<>(){{
       for(TierType tierType: TierType.values()) {
           put(tierType.getStringValue(), new StatsHolder());
       }
    }};

evictions = in.readVLong();
hitCount = in.readVLong();
missCount = in.readVLong();
this();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are calling to initialize this map but looks error prone and wrong logically. As logically all variables inside this constructor should be initialized with StreamInput values. You can initialize the map inline as suggested above and we don't need this.

this.missCount = missCount;
public RequestCacheStats(Map<TierType, StatsHolder> inputMap) {
// Create a RequestCacheStats with multiple tiers' statistics
this();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again remove this.

import java.io.IOException;
import java.io.Serializable;

public class StatsHolder implements Serializable, Writeable, ToXContentFragment {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering this is specific to RequestCacheStats, better to move this inside RequestCacheStats itself.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also used in ShardRequestCache - I moved it out from this class because I wanted it to be used by RequestCacheStats as well

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But would this StatsHolder be used elsewhere? As this looks pretty generic though will only be used for requestCache. Considering this is specific to RequestCache, maybe we can still keep it inside ShardRequestCache as public. And use it from RequestCacheStats, should be fine.

// on a node with a maximum request cache size that we set.

@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0)
public class IndicesRequestCacheDiskTierIT extends OpenSearchIntegTestCase {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should ideally add these tests as part of IndicesRequestCacheIT. Lets check what is needed to do that.

Comment on lines 640 to 646

Settings.Builder builder = Settings.builder()
.put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), true)
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0);

assertAcked(client.admin().indices().prepareCreate("index").setMapping("k", "type=keyword").setSettings(builder).get());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this change is not required? Remove?

assertSearchResponse(resp);
IndicesRequestCacheIT.assertCacheState(client, "index", 0, i + 1, TierType.ON_HEAP, false);
IndicesRequestCacheIT.assertCacheState(client, "index", 0, i + 1, TierType.DISK, false);
System.out.println("request number " + i);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

System.out.println("request number " + i);
}

System.out.println("Num requests = " + numRequests);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

public class IndicesRequestCacheDiskTierIT extends OpenSearchIntegTestCase {
public void testDiskTierStats() throws Exception {
int heapSizeBytes = 1800; // enough to fit 2 queries, as each is 687 B
int requestSize = 687; // each request is 687 B
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did we calculate this? I guess manually?
Possible to create a request and after which we can estimate the size? Doing this we can dynamically generate this value.

Comment on lines +134 to +139
double getTimeEWMA = getTimeEWMAIfDisk(cachingTier);
if (value != null) {
tieredCacheEventListener.onHit(key, value, cachingTier.getTierType());
tieredCacheEventListener.onHit(key, value, cachingTier.getTierType(), getTimeEWMA);
return new CacheValue<>(value, cachingTier.getTierType());
}
tieredCacheEventListener.onMiss(key, cachingTier.getTierType());
tieredCacheEventListener.onMiss(key, cachingTier.getTierType(), getTimeEWMA);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem right. We should ideally put these get times inside Disk caching tier itself. So that if we have a different implementation of TieredService, we don't have to duplicate this work.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And as discussed having separate DiskCacheStats separately should be able to solve this.

@@ -12,11 +12,11 @@

public interface TieredCacheEventListener<K, V> {

void onMiss(K key, TierType tierType);
void onMiss(K key, TierType tierType, double getTimeEWMA);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding getTimeEWMA here isn't needed considering it will only be needed as part of stats. We need to rethink in terms of low level design. For such specific stats related to a particular tier, we can instead create separate DiskTierStats associated with disk tier for example, keep accumulating relevant stats there in memory. This stats can be eventually used inside ShardRequestCacheStats to pull in those values.

As there might be more values coming in later on which we need to add as part of stats, so this solution isn't extensible as we can't keep adding it here.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense. I just wasn't sure where to actually fetch the values from the disk tier, and I thought onHit and onMiss would be reasonable since that's when getTimeEWMA will actually change. But I agree it's not extensible to new stats which might change on some other frequency. Should we instead have some sort of background job to periodically gather stats from the disk tier?

@@ -52,7 +56,7 @@ public class EhcacheDiskCachingTier implements DiskCachingTier<IndicesRequestCac
private final String diskCacheFP; // the one to use for this node
private RemovalListener<IndicesRequestCache.Key, BytesReference> removalListener;
private ExponentiallyWeightedMovingAverage getTimeMillisEWMA;
private static final double GET_TIME_EWMA_ALPHA = 0.3; // This is the value used elsewhere in OpenSearch
private static final double GET_TIME_EWMA_ALPHA = 0.3; // This is the value used elsewhere in OpenSearch
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExponentiallyWeightedMovingAverage(GET_TIME_EWMA_ALPHA, 10). Keeping 10 as intialAvg doesn't seem right. Don't know the right value but should keeping it 0 be better?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I somewhat arbitrarily picked 10 since we expect a disk seek to take ~10 ms on a spinning disk but 0 might be better, yeah

@@ -52,7 +56,7 @@ public class EhcacheDiskCachingTier implements DiskCachingTier<IndicesRequestCac
private final String diskCacheFP; // the one to use for this node
private RemovalListener<IndicesRequestCache.Key, BytesReference> removalListener;
private ExponentiallyWeightedMovingAverage getTimeMillisEWMA;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets also have normal average as well. EWMA might be useful for recent stats and normal will give overall view of get time. As discussed, creating some separate DiskStats might be better. We can move such stats there.

import java.io.IOException;
import java.io.Serializable;

public class StatsHolder implements Serializable, Writeable, ToXContentFragment {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But would this StatsHolder be used elsewhere? As this looks pretty generic though will only be used for requestCache. Considering this is specific to RequestCache, maybe we can still keep it inside ShardRequestCache as public. And use it from RequestCacheStats, should be fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants