| | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | |
| | | import java.lang.reflect.Method; |
| | | import java.util.ArrayList; |
| | | import java.util.*; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.server.admin.std.server.MonitorProviderCfg; |
| | | import org.opends.server.api.AttributeSyntax; |
| | | import org.opends.server.api.MonitorProvider; |
| | | import org.opends.server.backends.jeb.DatabaseContainer; |
| | | import org.opends.server.backends.jeb.EntryContainer; |
| | | import org.opends.server.backends.jeb.Index; |
| | | import org.opends.server.backends.jeb.RootContainer; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.Attributes; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.*; |
| | | |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.EnvironmentStats; |
| | | import com.sleepycat.je.JEVersion; |
| | | import com.sleepycat.je.StatsConfig; |
| | | import com.sleepycat.je.TransactionStats; |
| | | |
| | | import org.opends.server.util.TimeThread; |
| | | |
| | | |
| | | /** |
| | |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | /** |
| | | * Represents the statistical information kept for each search filter. |
| | | */ |
| | | private static class FilterStats implements Comparable<FilterStats> |
| | | { |
| | | private volatile Message failureReason = Message.EMPTY; |
| | | private long maxMatchingEntries = -1; |
| | | private final AtomicInteger hits = new AtomicInteger(); |
| | | |
| | | public int compareTo(FilterStats that) { |
| | | return this.hits.get() - that.hits.get(); |
| | | } |
| | | |
| | | private void update(int hitCount, Message failureReason) |
| | | { |
| | | this.hits.getAndAdd(hitCount); |
| | | this.failureReason = failureReason; |
| | | } |
| | | |
| | | private void update(int hitCount, long matchingEntries) |
| | | { |
| | | this.hits.getAndAdd(hitCount); |
| | | this.failureReason = Message.EMPTY; |
| | | synchronized(this) |
| | | { |
| | | if(matchingEntries > maxMatchingEntries) |
| | | { |
| | | maxMatchingEntries = matchingEntries; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * The name of this monitor instance. |
| | |
| | | */ |
| | | private RootContainer rootContainer; |
| | | |
| | | private int maxEntries = 1024; |
| | | private boolean filterUseEnabled = false; |
| | | private String startTimeStamp; |
| | | private final HashMap<SearchFilter, FilterStats> filterToStats = |
| | | new HashMap<SearchFilter, FilterStats>(); |
| | | private final AtomicInteger indexedSearchCount = new AtomicInteger(); |
| | | private final AtomicInteger unindexedSearchCount = new AtomicInteger(); |
| | | |
| | | /** |
| | | * Creates a new database environment monitor. |
| | | * @param name The monitor instance name. |
| | |
| | | addAttributesForStatsObject(monitorAttrs, environmentStats, "Environment"); |
| | | addAttributesForStatsObject(monitorAttrs, transactionStats, "Transaction"); |
| | | |
| | | AttributeBuilder needReindex = new AttributeBuilder("need-reindex"); |
| | | for(EntryContainer ec : rootContainer.getEntryContainers()) |
| | | { |
| | | List<DatabaseContainer> databases = new ArrayList<DatabaseContainer>(); |
| | | ec.listDatabases(databases); |
| | | for(DatabaseContainer dc : databases) |
| | | { |
| | | if(dc instanceof Index && !((Index)dc).isTrusted()) |
| | | { |
| | | needReindex.add(dc.getName()); |
| | | } |
| | | } |
| | | } |
| | | if(needReindex.size() > 0) |
| | | { |
| | | monitorAttrs.add(needReindex.toAttribute()); |
| | | } |
| | | |
| | | if(filterUseEnabled) |
| | | { |
| | | monitorAttrs.add(Attributes.create("filter-use-startTime", |
| | | startTimeStamp)); |
| | | AttributeBuilder builder = new AttributeBuilder("filter-use"); |
| | | |
| | | StringBuilder stringBuilder = new StringBuilder(); |
| | | synchronized(filterToStats) |
| | | { |
| | | for(Map.Entry<SearchFilter, FilterStats> entry : |
| | | filterToStats.entrySet()) |
| | | { |
| | | entry.getKey().toString(stringBuilder); |
| | | stringBuilder.append(" hits:"); |
| | | stringBuilder.append(entry.getValue().hits.get()); |
| | | stringBuilder.append(" maxmatches:"); |
| | | stringBuilder.append(entry.getValue().maxMatchingEntries); |
| | | stringBuilder.append(" message:"); |
| | | stringBuilder.append(entry.getValue().failureReason); |
| | | builder.add(stringBuilder.toString()); |
| | | stringBuilder.setLength(0); |
| | | } |
| | | } |
| | | monitorAttrs.add(builder.toAttribute()); |
| | | monitorAttrs.add(Attributes.create("filter-use-indexed", |
| | | String.valueOf(indexedSearchCount.get()))); |
| | | monitorAttrs.add(Attributes.create("filter-use-unindexed", |
| | | String.valueOf(unindexedSearchCount.get()))); |
| | | } |
| | | |
| | | return monitorAttrs; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Updates the index filter statistics with this latest search filter |
| | | * and the reason why an index was not used. |
| | | * |
| | | * @param searchFilter The search filter that was evaluated. |
| | | * @param failureMessage The reason why an index was not used. |
| | | */ |
| | | public void updateStats(SearchFilter searchFilter, Message failureMessage) |
| | | { |
| | | if(!filterUseEnabled) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | FilterStats stats; |
| | | synchronized(filterToStats) |
| | | { |
| | | stats = filterToStats.get(searchFilter); |
| | | |
| | | |
| | | if(stats != null) |
| | | { |
| | | stats.update(1, failureMessage); |
| | | } |
| | | else |
| | | { |
| | | stats = new FilterStats(); |
| | | stats.update(1, failureMessage); |
| | | removeLowestHit(); |
| | | filterToStats.put(searchFilter, stats); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Updates the index filter statistics with this latest search filter |
| | | * and the number of entries matched by the index lookup. |
| | | * |
| | | * @param searchFilter The search filter that was evaluated. |
| | | * @param matchingEntries The number of entries matched by the successful |
| | | * index lookup. |
| | | */ |
| | | public void updateStats(SearchFilter searchFilter, long matchingEntries) |
| | | { |
| | | if(!filterUseEnabled) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | FilterStats stats; |
| | | synchronized(filterToStats) |
| | | { |
| | | stats = filterToStats.get(searchFilter); |
| | | |
| | | |
| | | if(stats != null) |
| | | { |
| | | stats.update(1, matchingEntries); |
| | | } |
| | | else |
| | | { |
| | | stats = new FilterStats(); |
| | | stats.update(1, matchingEntries); |
| | | removeLowestHit(); |
| | | filterToStats.put(searchFilter, stats); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Enable or disable index filter statistics gathering. |
| | | * |
| | | * @param enabled <code>true></code> to enable index filter statics gathering. |
| | | */ |
| | | public void enableFilterUseStats(boolean enabled) |
| | | { |
| | | if(enabled && !filterUseEnabled) |
| | | { |
| | | startTimeStamp = TimeThread.getGMTTime(); |
| | | indexedSearchCount.set(0); |
| | | unindexedSearchCount.set(0); |
| | | } |
| | | else if(!enabled) |
| | | { |
| | | filterToStats.clear(); |
| | | } |
| | | filterUseEnabled = enabled; |
| | | } |
| | | |
| | | /** |
| | | * Indicates if index filter statistics gathering is enabled. |
| | | * |
| | | * @return <code>true</code> If index filter statistics gathering is enabled. |
| | | */ |
| | | public boolean isFilterUseEnabled() |
| | | { |
| | | return filterUseEnabled; |
| | | } |
| | | |
| | | /** |
| | | * Sets the maximum number of search filters statistics entries to keep |
| | | * before ones with the least hits will be removed. |
| | | * |
| | | * @param maxEntries The maximum number of search filters statistics |
| | | * entries to keep |
| | | */ |
| | | public void setMaxEntries(int maxEntries) { |
| | | this.maxEntries = maxEntries; |
| | | } |
| | | |
| | | /** |
| | | * Updates the statistics counter to include an indexed search. |
| | | */ |
| | | public void updateIndexedSearchCount() |
| | | { |
| | | indexedSearchCount.getAndIncrement(); |
| | | } |
| | | |
| | | /** |
| | | * Updates the statistics counter to include an unindexed search. |
| | | */ |
| | | public void updateUnindexedSearchCount() |
| | | { |
| | | unindexedSearchCount.getAndIncrement(); |
| | | } |
| | | |
| | | private void removeLowestHit() |
| | | { |
| | | while(!filterToStats.isEmpty() && filterToStats.size() > maxEntries) |
| | | { |
| | | Iterator<Map.Entry<SearchFilter, FilterStats>> i = |
| | | filterToStats.entrySet().iterator(); |
| | | Map.Entry<SearchFilter, FilterStats> lowest = i.next(); |
| | | Map.Entry<SearchFilter, FilterStats> entry; |
| | | while(lowest.getValue().hits.get() > 1 && i.hasNext()) |
| | | { |
| | | entry = i.next(); |
| | | if(entry.getValue().hits.get() < lowest.getValue().hits.get()) |
| | | { |
| | | lowest = entry; |
| | | } |
| | | } |
| | | |
| | | filterToStats.remove(lowest.getKey()); |
| | | } |
| | | } |
| | | } |