package io.ebeaninternal.server.autotune.service;

import io.ebean.bean.CallStack;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.AutoTuneConfig;
import io.ebean.config.AutoTuneMode;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.autotune.ProfilingListener;
import io.ebeaninternal.server.autotune.model.Origin;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;

import javax.persistence.PersistenceException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 */
public class BaseQueryTuner {

  private final boolean queryTuning;

  private final boolean profiling;

  private final AutoTuneMode mode;

  
Map of the tuned query details per profile query point.
/** * Map of the tuned query details per profile query point. */
private final Map<String, TunedQueryInfo> tunedQueryInfoMap = new ConcurrentHashMap<>(); private final SpiEbeanServer server; private final ProfilingListener profilingListener;
Flag set true when there is no profiling or query tuning.
/** * Flag set true when there is no profiling or query tuning. */
private final boolean skipAll; BaseQueryTuner(AutoTuneConfig config, SpiEbeanServer server, ProfilingListener profilingListener) { this.server = server; this.profilingListener = profilingListener; this.mode = config.getMode(); this.queryTuning = config.isQueryTuning(); this.profiling = config.isProfiling(); this.skipAll = !queryTuning && !profiling; }
Return all the current tuned query entries.
/** * Return all the current tuned query entries. */
public Collection<TunedQueryInfo> getAll() { return tunedQueryInfoMap.values(); }
Put a query tuning entry.
/** * Put a query tuning entry. */
public void put(Origin origin) { tunedQueryInfoMap.put(origin.getKey(), new TunedQueryInfo(origin)); }
Load the tuned query information.
/** * Load the tuned query information. */
public void load(String key, TunedQueryInfo queryInfo) { tunedQueryInfoMap.put(key, queryInfo); }
Return the detail currently used for tuning. This returns null if there is currently no matching tuning.
/** * Return the detail currently used for tuning. * This returns null if there is currently no matching tuning. */
public OrmQueryDetail get(String key) { TunedQueryInfo info = tunedQueryInfoMap.get(key); return (info == null) ? null : info.getTunedDetail(); }
Auto tune the query and enable profiling.
/** * Auto tune the query and enable profiling. */
boolean tuneQuery(SpiQuery<?> query) { if (skipAll || !tunableQuery(query)) { return false; } if (query.getProfilingListener() != null) { // profiling secondary query return false; } if (!useTuning(query)) { if (profiling) { profiling(query, server.createCallStack()); } return false; } if (query.getParentNode() != null) { // This is a +lazy/+query query with profiling on. // We continue to collect the profiling information. query.setProfilingListener(profilingListener); return true; } // create a query point to identify the query CallStack stack = server.createCallStack(); ObjectGraphNode origin = query.setOrigin(stack); if (profiling) { if (profilingListener.isProfileRequest(origin, query)) { // collect more profiling based on profiling rate etc query.setProfilingListener(profilingListener); } } if (queryTuning) { // get current "tuned fetch" for this query point TunedQueryInfo tuneInfo = tunedQueryInfoMap.get(origin.getOriginQueryPoint().getKey()); return tuneInfo != null && tuneInfo.tuneQuery(query); } return false; }
Return false for row count, find ids, subQuery, delete and Versions queries.

These queries are not applicable for autoTune in that they don't have a select/fetch (fetch group).

We also exclude queries that are explicitly set to load the L2 bean cache as we want full beans in that case.

/** * Return false for row count, find ids, subQuery, delete and Versions queries. * <p> * These queries are not applicable for autoTune in that they don't have a select/fetch (fetch group). * </p> * <p> * We also exclude queries that are explicitly set to load the L2 bean cache as we want full beans * in that case. * </p> */
private boolean tunableQuery(SpiQuery<?> query) { SpiQuery.Type type = query.getType(); switch (type) { case COUNT: case ID_LIST: case UPDATE: case DELETE: case SUBQUERY: return false; default: // not using autoTune when explicitly loading the l2 bean cache // or when using Versions query return !query.isForceHitDatabase() && SpiQuery.TemporalMode.VERSIONS != query.getTemporalMode(); } } private void profiling(SpiQuery<?> query, CallStack stack) { // create a query point to identify the query ObjectGraphNode origin = query.setOrigin(stack); if (profilingListener.isProfileRequest(origin, query)) { // collect more profiling based on profiling rate etc query.setProfilingListener(profilingListener); } }
Return true if we should try to tune this query.
/** * Return true if we should try to tune this query. */
private boolean useTuning(SpiQuery<?> query) { Boolean autoTune = query.isAutoTune(); if (autoTune != null) { // explicitly set... return autoTune; } else { // determine using implicit mode... switch (mode) { case DEFAULT_ON: return true; case DEFAULT_OFF: return false; case DEFAULT_ONIFEMPTY: return query.isDetailEmpty(); default: throw new PersistenceException("Invalid AutoTuneMode " + mode); } } }
Return the keys as a set.
/** * Return the keys as a set. */
public Set<String> keySet() { return tunedQueryInfoMap.keySet(); } }