de.otto.mongodb.profiler.op.OpProfileDataFetcher.java Source code

Java tutorial

Introduction

Here is the source code for de.otto.mongodb.profiler.op.OpProfileDataFetcher.java

Source

/*
 *  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();
        }
    }
}