All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ehcache.jsr107.Eh107CacheStatisticsMXBean Maven / Gradle / Ivy

There is a newer version: 3.10.8
Show newest version
/*
 * Copyright Terracotta, Inc.
 *
 * Licensed 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.ehcache.jsr107;

import org.ehcache.Cache;
import org.ehcache.core.InternalCache;
import org.ehcache.core.statistics.CacheOperationOutcomes;
import org.ehcache.core.statistics.StoreOperationOutcomes;
import org.ehcache.core.statistics.BulkOps;
import org.terracotta.context.ContextManager;
import org.terracotta.context.TreeNode;
import org.terracotta.context.query.Matcher;
import org.terracotta.context.query.Matchers;
import org.terracotta.context.query.Query;
import org.terracotta.statistics.OperationStatistic;
import org.terracotta.statistics.StatisticsManager;
import org.terracotta.statistics.derived.LatencySampling;
import org.terracotta.statistics.derived.MinMaxAverage;
import org.terracotta.statistics.jsr166e.LongAdder;
import org.terracotta.statistics.observer.ChainedOperationObserver;

import java.util.Collections;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;

import static java.util.EnumSet.allOf;
import static org.terracotta.context.query.Matchers.attributes;
import static org.terracotta.context.query.Matchers.context;
import static org.terracotta.context.query.Matchers.hasAttribute;
import static org.terracotta.context.query.QueryBuilder.queryBuilder;

/**
 * @author Ludovic Orban
 */
class Eh107CacheStatisticsMXBean extends Eh107MXBean implements javax.cache.management.CacheStatisticsMXBean {

  private final CompensatingCounters compensatingCounters = new CompensatingCounters();
  private final OperationStatistic get;
  private final OperationStatistic put;
  private final OperationStatistic remove;
  private final OperationStatistic putIfAbsent;
  private final OperationStatistic replace;
  private final OperationStatistic conditionalRemove;
  private final OperationStatistic authorityEviction;
  private final Map bulkMethodEntries;
  private final LatencyMonitor averageGetTime;
  private final LatencyMonitor averagePutTime;
  private final LatencyMonitor averageRemoveTime;

  Eh107CacheStatisticsMXBean(String cacheName, Eh107CacheManager cacheManager, InternalCache cache) {
    super(cacheName, cacheManager, "CacheStatistics");
    this.bulkMethodEntries = cache.getBulkMethodEntries();

    get = findCacheStatistic(cache, CacheOperationOutcomes.GetOutcome.class, "get");
    put = findCacheStatistic(cache, CacheOperationOutcomes.PutOutcome.class, "put");
    remove = findCacheStatistic(cache, CacheOperationOutcomes.RemoveOutcome.class, "remove");
    putIfAbsent = findCacheStatistic(cache, CacheOperationOutcomes.PutIfAbsentOutcome.class, "putIfAbsent");
    replace = findCacheStatistic(cache, CacheOperationOutcomes.ReplaceOutcome.class, "replace");
    conditionalRemove = findCacheStatistic(cache, CacheOperationOutcomes.ConditionalRemoveOutcome.class, "conditionalRemove");
    authorityEviction = findAuthoritativeTierStatistic(cache, StoreOperationOutcomes.EvictionOutcome.class, "eviction");

    averageGetTime = new LatencyMonitor(allOf(CacheOperationOutcomes.GetOutcome.class));
    get.addDerivedStatistic(averageGetTime);
    averagePutTime = new LatencyMonitor(allOf(CacheOperationOutcomes.PutOutcome.class));
    put.addDerivedStatistic(averagePutTime);
    averageRemoveTime= new LatencyMonitor(allOf(CacheOperationOutcomes.RemoveOutcome.class));
    remove.addDerivedStatistic(averageRemoveTime);
  }

  @Override
  public void clear() {
    compensatingCounters.snapshot();
    averageGetTime.clear();
    averagePutTime.clear();
    averageRemoveTime.clear();
  }

  @Override
  public long getCacheHits() {
    return normalize(getHits() - compensatingCounters.cacheHits - compensatingCounters.bulkGetHits);
  }

  @Override
  public float getCacheHitPercentage() {
    long cacheHits = getCacheHits();
    return normalize((float) cacheHits / (cacheHits + getCacheMisses())) * 100.0f;
  }

  @Override
  public long getCacheMisses() {
    return normalize(getMisses() - compensatingCounters.cacheMisses - compensatingCounters.bulkGetMiss);
  }

  @Override
  public float getCacheMissPercentage() {
    long cacheMisses = getCacheMisses();
    return normalize((float) cacheMisses / (getCacheHits() + cacheMisses)) * 100.0f;
  }

  @Override
  public long getCacheGets() {
    return normalize(getHits() + getMisses()
                     - compensatingCounters.cacheGets
                     - compensatingCounters.bulkGetHits
                     - compensatingCounters.bulkGetMiss);
  }

  @Override
  public long getCachePuts() {
    return normalize(getBulkCount(BulkOps.PUT_ALL) - compensatingCounters.bulkPuts +
        put.sum(EnumSet.of(CacheOperationOutcomes.PutOutcome.PUT)) +
        put.sum(EnumSet.of(CacheOperationOutcomes.PutOutcome.UPDATED)) +
        putIfAbsent.sum(EnumSet.of(CacheOperationOutcomes.PutIfAbsentOutcome.PUT)) +
        replace.sum(EnumSet.of(CacheOperationOutcomes.ReplaceOutcome.HIT)) -
        compensatingCounters.cachePuts);
  }

  @Override
  public long getCacheRemovals() {
    return normalize(getBulkCount(BulkOps.REMOVE_ALL) - compensatingCounters.bulkRemovals +
        remove.sum(EnumSet.of(CacheOperationOutcomes.RemoveOutcome.SUCCESS)) +
        conditionalRemove.sum(EnumSet.of(CacheOperationOutcomes.ConditionalRemoveOutcome.SUCCESS)) -
        compensatingCounters.cacheRemovals);
  }

  @Override
  public long getCacheEvictions() {
    return normalize(authorityEviction.sum(EnumSet.of(StoreOperationOutcomes.EvictionOutcome.SUCCESS)) - compensatingCounters.cacheEvictions);
  }

  @Override
  public float getAverageGetTime() {
    return (float) averageGetTime.value();
  }

  @Override
  public float getAveragePutTime() {
    return (float) averagePutTime.value();
  }

  @Override
  public float getAverageRemoveTime() {
    return (float) averageRemoveTime.value();
  }

  private long getMisses() {
    return getBulkCount(BulkOps.GET_ALL_MISS) +
        get.sum(EnumSet.of(CacheOperationOutcomes.GetOutcome.MISS_NO_LOADER, CacheOperationOutcomes.GetOutcome.MISS_WITH_LOADER)) +
        putIfAbsent.sum(EnumSet.of(CacheOperationOutcomes.PutIfAbsentOutcome.PUT)) +
        replace.sum(EnumSet.of(CacheOperationOutcomes.ReplaceOutcome.MISS_NOT_PRESENT)) +
        conditionalRemove.sum(EnumSet.of(CacheOperationOutcomes.ConditionalRemoveOutcome.FAILURE_KEY_MISSING));
  }

  private long getHits() {
    return getBulkCount(BulkOps.GET_ALL_HITS) +
        get.sum(EnumSet.of(CacheOperationOutcomes.GetOutcome.HIT_NO_LOADER, CacheOperationOutcomes.GetOutcome.HIT_WITH_LOADER)) +
        putIfAbsent.sum(EnumSet.of(CacheOperationOutcomes.PutIfAbsentOutcome.PUT)) +
        replace.sum(EnumSet.of(CacheOperationOutcomes.ReplaceOutcome.HIT, CacheOperationOutcomes.ReplaceOutcome.MISS_PRESENT)) +
        conditionalRemove.sum(EnumSet.of(CacheOperationOutcomes.ConditionalRemoveOutcome.SUCCESS, CacheOperationOutcomes.ConditionalRemoveOutcome.FAILURE_KEY_PRESENT));
  }

  private long getBulkCount(BulkOps bulkOps) {
    return bulkMethodEntries.get(bulkOps).longValue();
  }

  private static long normalize(long value) {
    return Math.max(0, value);
  }

  private static float normalize(float value) {
    if (Float.isNaN(value)) {
      return 0.0f;
    }
    return Math.min(1.0f, Math.max(0.0f, value));
  }

  static > OperationStatistic findCacheStatistic(Cache cache, Class type, String statName) {
    Query query = queryBuilder()
        .children()
        .filter(context(attributes(Matchers.>allOf(hasAttribute("name", statName), hasAttribute("type", type)))))
        .build();

    Set result = query.execute(Collections.singleton(ContextManager.nodeFor(cache)));
    if (result.size() > 1) {
      throw new RuntimeException("result must be unique");
    }
    if (result.isEmpty()) {
      throw new RuntimeException("result must not be null");
    }
    return (OperationStatistic) result.iterator().next().getContext().attributes().get("this");
  }

  > OperationStatistic findAuthoritativeTierStatistic(Cache cache, Class type, String statName) {
    Query storeQuery = queryBuilder()
        .children()
        .children()
        .filter(context(attributes(Matchers.>allOf(
            hasAttribute("tags", new Matcher>() {
              @Override
              protected boolean matchesSafely(Set object) {
                return object.containsAll(Collections.singleton("store"));
              }
            })))))
        .build();

    Set storeResult = storeQuery.execute(Collections.singleton(ContextManager.nodeFor(cache)));
    if (storeResult.size() > 1) {
      throw new RuntimeException("store result must be unique");
    }
    if (storeResult.isEmpty()) {
      throw new RuntimeException("store result must not be null");
    }
    Object authoritativeTier = storeResult.iterator().next().getContext().attributes().get("authoritativeTier");

    Query statQuery = queryBuilder()
        .children()
        .filter(context(attributes(Matchers.>allOf(hasAttribute("name", statName), hasAttribute("type", type)))))
        .build();

    Set statResult = statQuery.execute(Collections.singleton(StatisticsManager.nodeFor(authoritativeTier)));
    if (statResult.size() > 1) {
      throw new RuntimeException("stat result must be unique");
    }
    if (statResult.isEmpty()) {
      throw new RuntimeException("stat result must not be null");
    }

    return (OperationStatistic) statResult.iterator().next().getContext().attributes().get("this");
  }

  class CompensatingCounters {
    volatile long cacheHits;
    volatile long cacheMisses;
    volatile long cacheGets;
    volatile long bulkGetHits;
    volatile long bulkGetMiss;
    volatile long cachePuts;
    volatile long bulkPuts;
    volatile long cacheRemovals;
    volatile long bulkRemovals;
    volatile long cacheEvictions;

    void snapshot() {
      cacheHits += Eh107CacheStatisticsMXBean.this.getCacheHits();
      cacheMisses += Eh107CacheStatisticsMXBean.this.getCacheMisses();
      cacheGets += Eh107CacheStatisticsMXBean.this.getCacheGets();
      bulkGetHits += Eh107CacheStatisticsMXBean.this.getBulkCount(BulkOps.GET_ALL_HITS);
      bulkGetMiss += Eh107CacheStatisticsMXBean.this.getBulkCount(BulkOps.GET_ALL_MISS);
      cachePuts += Eh107CacheStatisticsMXBean.this.getCachePuts();
      bulkPuts += Eh107CacheStatisticsMXBean.this.getBulkCount(BulkOps.PUT_ALL);
      cacheRemovals += Eh107CacheStatisticsMXBean.this.getCacheRemovals();
      bulkRemovals += Eh107CacheStatisticsMXBean.this.getBulkCount(BulkOps.REMOVE_ALL);
      cacheEvictions += Eh107CacheStatisticsMXBean.this.getCacheEvictions();
    }
  }

  private static class LatencyMonitor> implements ChainedOperationObserver {

    private final LatencySampling sampling;
    private volatile MinMaxAverage average;

    public LatencyMonitor(Set targets) {
      this.sampling = new LatencySampling(targets, 1.0);
      this.average = new MinMaxAverage();
      sampling.addDerivedStatistic(average);
    }

    @Override
    public void begin(long time) {
      sampling.begin(time);
    }

    @Override
    public void end(long time, T result) {
      sampling.end(time, result);
    }

    @Override
    public void end(long time, T result, long... parameters) {
      sampling.end(time, result, parameters);
    }

    public double value() {
      Double value = average.mean();
      if (value == null) {
        //Someone involved with 107 can't do math
        return 0;
      } else {
        //We use nanoseconds, 107 uses microseconds
        return value / 1000f;
      }
    }

    public synchronized void clear() {
      sampling.removeDerivedStatistic(average);
      average = new MinMaxAverage();
      sampling.addDerivedStatistic(average);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy