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

org.ehcache.impl.internal.statistics.StatsUtils 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.impl.internal.statistics;

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

import org.ehcache.Cache;
import org.ehcache.core.statistics.StoreOperationOutcomes;
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 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;

/**
 * Class allowing to query cache and tier statistics
 */
final class StatsUtils {

  private StatsUtils() {}

  static Matcher> hasTag(final String tag) {
    return hasAttribute("tags", new Matcher>() {
      @Override
      protected boolean matchesSafely(Set object) {
        return object.contains(tag);
      }
    });
  }

  static Matcher> hasProperty(final String key, final String value) {
    return hasAttribute("properties", new Matcher>() {
      @Override
      protected boolean matchesSafely(Map properties) {
        Object val = properties.get(key);
        return val != null && value.equals(val);
      }
    });
  }

  /**
   * Search for a statistic on the descendant of the context that matches the tag and statistic name.
   *
   * @param context the context of the query
   * @param discriminator a filter on the discriminator property
   * @param tag the tag we are looking for
   * @param statName statistic name
   * @param  type of the statistic that will be returned
   * @return the wanted statistic or null if no such statistic is found
   * @throws RuntimeException when more than one matching statistic is found
   */
  static  Optional findStatisticOnDescendants(Object context, String discriminator, String tag, String statName) {

    @SuppressWarnings("unchecked")
    Set statResult = queryBuilder()
      .descendants()
      .filter(context(attributes(Matchers.allOf(
        hasAttribute("name", statName),
        hasProperty("discriminator", discriminator),
        hasTag(tag)))))
      .build().execute(Collections.singleton(ContextManager.nodeFor(context)));

    if (statResult.size() > 1) {
      throw new RuntimeException("One stat expected for " + statName + " but found " + statResult.size());
    }

    if (statResult.size() == 1) {
      @SuppressWarnings("unchecked")
      T result = (T) statResult.iterator().next().getContext().attributes().get("this");
      return Optional.ofNullable(result);
    }

    // No such stat in this context
    return Optional.empty();
  }

  /**
   * Search for a statistic on the descendant of the context that matches the tag and statistic name.
   *
   * @param context the context of the query
   * @param tag the tag we are looking for
   * @param statName statistic name
   * @param  type of the statistic that will be returned
   * @return the wanted statistic or null if no such statistic is found
   * @throws RuntimeException when more than one matching statistic is found
   */
  static  Optional findStatisticOnDescendants(Object context, String tag, String statName) {

    @SuppressWarnings("unchecked")
    Set statResult = queryBuilder()
      .descendants()
      .filter(context(attributes(Matchers.allOf(
        hasAttribute("name", statName),
        hasTag(tag)))))
      .build().execute(Collections.singleton(ContextManager.nodeFor(context)));

    if (statResult.size() > 1) {
      throw new RuntimeException("One stat expected for " + statName + " but found " + statResult.size());
    }

    if (statResult.size() == 1) {
      @SuppressWarnings("unchecked")
      T result = (T) statResult.iterator().next().getContext().attributes().get("this");
      return Optional.ofNullable(result);
    }

    // No such stat in this context
    return Optional.empty();
  }

  /**
   * Find an operation statistic attached (as a children) to this context that matches the statistic name and type
   *
   * @param context the context of the query
   * @param type type of the operation statistic
   * @param statName statistic name
   * @param  type of the operation statistic content
   * @return the operation statistic searched for
   * @throws RuntimeException if 0 or more than 1 result is found
   */
  static > OperationStatistic findOperationStatisticOnChildren(Object context, Class type, String statName) {
    @SuppressWarnings("unchecked")
    Query query = queryBuilder()
      .children()
      .filter(context(attributes(Matchers.allOf(hasAttribute("name", statName), hasAttribute("type", type)))))
      .build();

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

  /**
   * Find the list of tiers of a cache. We assume a lot of things here.
   * 
    *
  • The "eviction" statistic is available on the tier
  • *
  • That the tiers have only one tag attribute
  • *
  • That this tag contains the tier name
  • *
  • That the only descendants having an "eviction" statistic are the tiers
  • *
* * @param cache the context for looking for tiers * @return an array of tier names * @throws RuntimeException if not tiers are found or if tiers have multiple tags */ static String[] findTiers(Cache cache) { // Here I'm randomly taking the eviction observer because it exists on all tiers @SuppressWarnings("unchecked") Query statQuery = queryBuilder() .descendants() .filter(context(attributes(Matchers.allOf(hasAttribute("name", "eviction"), hasAttribute("type", StoreOperationOutcomes.EvictionOutcome.class))))) .build(); Set statResult = statQuery.execute(Collections.singleton(ContextManager.nodeFor(cache))); if (statResult.isEmpty()) { throw new RuntimeException("Failed to find tiers using the eviction observer, valid result Set sizes must 1 or more"); } String[] tiers = new String[statResult.size()]; int i = 0; for (TreeNode treeNode : statResult) { Set tags = (Set) treeNode.getContext().attributes().get("tags"); if (tags.size() != 1) { throw new RuntimeException("We expect tiers to have only one tag"); } String storeType = tags.iterator().next().toString(); tiers[i++] = storeType; } return tiers; } /** * Find the lowest tier from a list of tier. We assume a lot of things here that the tiers depth * magically matches the alphabetical order. * * @param tiers all tiers * @return the lowest tier */ static String findLowestTier(String[] tiers) { //if only 1 store then you don't need to find the lowest tier if (tiers.length == 1) { return tiers[0]; } //we expect at least one tier if (tiers.length == 0) { throw new RuntimeException("No existing tier"); } // We rely here on the alphabetical order matching the depth order so from highest to lowest we have // OnHeap, OffHeap, Disk, Clustered String lowestTier = tiers[0]; for (int i = 1; i < tiers.length; i++) { if (tiers[i].compareTo(lowestTier) < 0) { lowestTier = tiers[i]; } } return lowestTier; } }