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.mongodb.*; import de.otto.mongodb.profiler.AbstractAsyncProfiler; import de.otto.mongodb.profiler.util.Logger; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import java.util.*; import static de.otto.mongodb.profiler.util.MongoSupport.require; /** * Fetches the data from the op profiling collection and queues it for analyzation. * * @author Robert Gacki */ class OpProfileDataFetcher implements Runnable, AutoCloseable { private static final Logger logger = Logger.getLogger(OpProfileDataFetcher.class); public static final String COLLECTION = "system.profile"; public static final Set<String> IGNORABLE_NS = Collections.unmodifiableSet(new HashSet<String>() { { add(".system.namespaces"); add(".system.indexes"); add(".system.profile"); add(".system.js"); add(".system.users"); } }); private final AbstractAsyncProfiler.ExecutionGuard executionGuard; private final DB db; private final Queue<DBObject> rawDataQueue; private DateTime lastTs; private DBCursor cursor; private final Object cursorMutex = new Object(); public OpProfileDataFetcher(final AbstractAsyncProfiler.ExecutionGuard executionGuard, final DB db, final Queue<DBObject> rawDataQueue) { this.executionGuard = executionGuard; this.db = db; this.rawDataQueue = rawDataQueue; this.lastTs = null; } @Override public void close() { synchronized (cursorMutex) { if (cursor != null) { cursor.close(); cursor = null; } lastTs = null; } } private DBCursor getCursor() { synchronized (cursorMutex) { // Close stale cursor if (cursor != null && cursor.getCursorId() == 0L) { cursor.close(); cursor = null; } // Create new cursor if (cursor == null && db.collectionExists(COLLECTION)) { if (lastTs == null) { lastTs = DateTime.now(DateTimeZone.UTC); } final DBCollection collection = db.getCollection(COLLECTION); final DBObject query = QueryBuilder.start() .and(QueryBuilder.start("ns").notEquals(collection.getFullName()).get(), QueryBuilder.start("ts").greaterThan(lastTs.toDate()).get()) .get(); final DBObject sortBy = new BasicDBObject("$natural", 1); final DBCursor cursor = collection.find(query).sort(sortBy).batchSize(100) .addOption(Bytes.QUERYOPTION_TAILABLE).addOption(Bytes.QUERYOPTION_AWAITDATA); this.cursor = cursor; } } return cursor; } private boolean acceptNs(String ns) { for (String ignorableNs : IGNORABLE_NS) { if (ns.endsWith(ignorableNs)) { return false; } } return true; } @Override public synchronized void run() { try { if (!executionGuard.execute() || !db.collectionExists(COLLECTION)) { close(); return; } DateTime newLastTs = lastTs; int added = 0; final DBCursor cursor = getCursor(); if (cursor != null) { while (cursor.hasNext()) { // Cursor#hasNext blockiert, bis neue Ergebnisse eintreffen if (!executionGuard.execute()) { close(); return; } final DBObject dbo = cursor.next(); final String ns = require("ns", String.class, dbo); if (acceptNs(ns)) { final DateTime newLastTsCandidate = new DateTime(require("ts", Date.class, dbo).getTime()); if (newLastTs == null || newLastTsCandidate.isAfter(newLastTs)) { newLastTs = newLastTsCandidate; } if (rawDataQueue.offer(dbo)) { added += 1; } } } } logger.trace("Added {} profiling data entries.", added); lastTs = newLastTs; } catch (MongoException.CursorNotFound e) { close(); } catch (RuntimeException e) { logger.warn(e, "Failed to run!"); close(); } } }