Java tutorial
/* * Copyright 2013 Robert Gacki <robert.gacki@cgi.com> * * 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 de.otto.mongodb.profiler.op; import com.google.common.collect.Lists; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.CommandResult; import com.mongodb.DB; import com.mongodb.DBObject; import de.otto.mongodb.profiler.AbstractAsyncProfiler; import de.otto.mongodb.profiler.InitializingProfiler; import de.otto.mongodb.profiler.op.analyze.Analyzer; import de.otto.mongodb.profiler.util.ProfilerThreadFactory; import org.joda.time.DateTime; import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static com.google.common.base.Preconditions.checkNotNull; import static de.otto.mongodb.profiler.util.Async.shutdownAndAwaitTermination; /** * The default implementation of the OpProfiler. Uses a fetcher to fetch operations from the profiling collection and * add them to a queue for processing. Fetching and processing is asynchronous. * * @author Robert Gacki */ public class DefaultOpProfiler extends AbstractAsyncProfiler implements OpProfiler, InitializingProfiler { public static final long DEFAULT_FETCHER_DELAY = 5L; public static final long DEFAULT_FETCHER_INITIAL_DELAY = 0L; public static final TimeUnit DEFAULT_FETCHER_TIMEUNIT = TimeUnit.SECONDS; public static final long DEFAULT_ANALYZER_DELAY = 5L; public static final long DEFAULT_ANALYZER_INITIAL_DELAY = 2L; public static final TimeUnit DEFAULT_ANALYZER_TIMEUNIT = TimeUnit.SECONDS; private final ScheduledExecutorService fetcherExecutorService; private final ScheduledExecutorService analyzerExecutorService; private final DB db; private final Queue<DBObject> rawDataQueue; private final Map<String, OpProfile> profiles; private final AtomicReference<DateTime> explanationResetMark; private final OpProfileDataFetcher opProfileDataFetcher; private final AtomicReference<OpProfile.OutlierConfiguration> outlierConfiguration; private final Collection<? extends Analyzer> analyzers; private volatile boolean shutdown = false; public DefaultOpProfiler(final boolean execute, final DB db, final Collection<? extends Analyzer> analyzers, final ThreadGroup threadGroup) { this(execute, db, analyzers, Executors.newSingleThreadScheduledExecutor( new ProfilerThreadFactory(OpProfiler.class, "Fetcher", threadGroup)), Executors.newSingleThreadScheduledExecutor( new ProfilerThreadFactory(OpProfiler.class, "Analyzer", threadGroup))); } protected DefaultOpProfiler(final boolean execute, final DB db, final Collection<? extends Analyzer> analyzers, final ScheduledExecutorService fetcherService, final ScheduledExecutorService analyzerService) { super(execute); this.db = checkNotNull(db); this.analyzers = new ArrayList<>(checkNotNull(analyzers)); this.rawDataQueue = new LinkedTransferQueue<>(); this.fetcherExecutorService = checkNotNull(fetcherService); this.analyzerExecutorService = checkNotNull(analyzerService); this.profiles = Collections.synchronizedMap(new HashMap<String, OpProfile>()); this.explanationResetMark = new AtomicReference<>(DateTime.now()); this.opProfileDataFetcher = new OpProfileDataFetcher(getExecutionGuard(), db, rawDataQueue); this.outlierConfiguration = new AtomicReference<>(); } @Override public synchronized void reset() { rawDataQueue.clear(); profiles.clear(); explainNextQueries(); } @Override public synchronized boolean continueProfiling() { if (shutdown) { return false; } return super.continueProfiling(); } @Override public synchronized DateTime continueProfiling(long duration, TimeUnit timeUnit) { if (shutdown) { return null; } return super.continueProfiling(duration, timeUnit); } @Override public ProfilingLevel getProfilingLevel() { final CommandResult result = db.command(BasicDBObjectBuilder.start("profile", -1).get()); result.throwOnError(); return ProfilingLevel.forValue(result.getInt("was", Integer.MIN_VALUE)); } @Override public void setProfilingLevel(ProfilingLevel level) { db.command(BasicDBObjectBuilder.start("profile", level.value).get()).throwOnError(); } @Override public Collection<QueryProfile> getQueryProfiles() { final ArrayList<QueryProfile> queries = new ArrayList<>(); for (OpProfile profile : profiles.values()) { if (profile instanceof QueryProfile) { queries.add(((QueryProfile) profile)); } } return queries; } @Override public QueryProfile getQueryProfile(String id) { final OpProfile profile = profiles.get(id); return profile != null && profile instanceof QueryProfile ? ((QueryProfile) profile) : null; } @Override public Collection<? extends OpProfile> getProfiles() { return Lists.newArrayList(profiles.values()); } @Override public OpProfile getProfile(String id) { return profiles.get(id); } @Override public void explainNextQueries() { explanationResetMark.set(DateTime.now()); } @Override public synchronized void initialize() { if (shutdown) { throw new IllegalStateException("Profiler is shut down!"); } fetcherExecutorService.scheduleWithFixedDelay(opProfileDataFetcher, DEFAULT_FETCHER_INITIAL_DELAY, DEFAULT_FETCHER_DELAY, DEFAULT_FETCHER_TIMEUNIT); final Runnable profileAnalyzer = new OpProfileAnalyzer(db, rawDataQueue, profiles, explanationResetMark, outlierConfiguration, analyzers); analyzerExecutorService.scheduleWithFixedDelay(profileAnalyzer, DEFAULT_ANALYZER_INITIAL_DELAY, DEFAULT_ANALYZER_DELAY, DEFAULT_ANALYZER_TIMEUNIT); } @Override public synchronized void shutdown() { if (shutdown) { return; } shutdown = true; stopProfiling(); try { // Try to close the cursor opProfileDataFetcher.close(); } catch (Exception e) { // ignore } try { shutdownAndAwaitTermination(fetcherExecutorService, 60, TimeUnit.SECONDS); } finally { try { shutdownAndAwaitTermination(analyzerExecutorService, 60, TimeUnit.SECONDS); } finally { // Close cursor again in case it has been reopened opProfileDataFetcher.close(); } } } @Override public OpProfile.OutlierConfiguration getOutlierConfiguration() { return outlierConfiguration.get(); } @Override public void setOutlierConfiguration(OpProfile.OutlierConfiguration configuration) { this.outlierConfiguration.set(configuration); } }