private void doMetricsStuffForOneSlice(
      LowLevelCacheCounters qfCounters, StripeData stripe, boolean isMissed) {
    // Slice boundaries may not match split boundaries due to torn rows in either direction,
    // so this counter may not be consistent with splits. This is also why we increment
    // requested bytes here, instead of based on the split - we don't want the metrics to be
    // inconsistent with each other. No matter what we determine here, at least we'll account
    // for both in the same manner.
    long bytes = stripe.lastEnd - stripe.knownTornStart;
    metrics.incrCacheRequestedBytes(bytes);
    if (!isMissed) {
      metrics.incrCacheHitBytes(bytes);
    }
    if (qfCounters != null) {
      if (isMissed) {
        qfCounters.recordCacheMiss(bytes);
      } else {
        qfCounters.recordCacheHit(bytes);
      }
    }
  }
RW lock ensures we have a consistent view of the file data, which is important given that
we generate "stripe" boundaries arbitrarily. Reading buffer data itself doesn't require
that this lock is held; however, everything else in stripes list does.
TODO: make more granular? We only care that each one reader sees consistent boundaries.
      So, we could shallow-copy the stripes list, then have individual locks inside each.
llap-server/src/java/org/apache/hadoop/hive/llap/cache/SerDeLowLevelCacheImpl.java