All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wordnik.system.mongodb.OplogTailThread Maven / Gradle / Ivy

The newest version!
// Copyright (C) 2012  Wordnik, Inc.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.  This program is distributed in the hope that it
// will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.  You should have received a copy
// of the GNU Lesser General Public License along with this program.  If not,
// see .

package com.wordnik.system.mongodb;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List;
import java.util.ArrayList;

import org.bson.types.BSONTimestamp;

import com.mongodb.BasicDBObject;
import com.mongodb.Bytes;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.wordnik.util.PrintFormat;

public class OplogTailThread extends Thread {
  protected boolean enableOutput = true;
  protected boolean running = false;
  protected boolean killMe = false;
  protected long reportInterval = 10000;
  protected List inclusions;
  protected List exclusions;
  protected List processors = new ArrayList();
  protected DBCollection oplog;
  protected String OPLOG_LAST_FILENAME = "last_timestamp.txt";
  protected boolean exitOnStopThread = false;

  public OplogTailThread(OplogRecordProcessor processor, DBCollection oplog){
    this.oplog = oplog;
    this.processors.add(processor);
    setName("oplog");
  }

  public OplogTailThread(OplogRecordProcessor processor, DBCollection oplog, String threadName){
    this.oplog = oplog;
    this.processors.add(processor);
    setName(threadName);
  }

  public void setOutputEnabled(boolean enabled) {
    this.enableOutput = enabled;
  }

  public void setStopFilename(String filename) {
    this.OPLOG_LAST_FILENAME = filename;
  }

  public void addOplogProcessor(OplogRecordProcessor processor) {
    this.processors.add(processor);
  }

  public void setBaseDir(String dir){
    if(dir != null){
      OPLOG_LAST_FILENAME = dir + File.separator + OPLOG_LAST_FILENAME;
    }
  }

  public void setBaseDir(String dir, String fileName){
    if(dir != null && fileName != null){
      OPLOG_LAST_FILENAME = dir + File.separator + fileName;
    }
  }

  public void setExitOnStopThread(Boolean isExit){
    exitOnStopThread = isExit;
  }

  public void setInclusions(List inclusions){
    this.inclusions = inclusions;
  }

  public void setExclusions(List exclusions){
    this.exclusions = exclusions;
  }

  public void writeLastTimestamp(BSONTimestamp ts){
    if(ts == null){
      return;
    }
    Writer writer = null;
    try{
      OutputStream out = new FileOutputStream(new File(OPLOG_LAST_FILENAME));
      writer = new OutputStreamWriter(out, "UTF-8");
      writer.write(Integer.toString(ts.getTime()) + "|" + Integer.toString(ts.getInc()));
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      if(writer != null){
        try{writer.close();}
        catch(Exception e){}
      }
    }
  }

  public BSONTimestamp getLastTimestamp() {
    BufferedReader input = null;
    try{
      File file = new File(OPLOG_LAST_FILENAME);
      if(!file.exists()){
        return null;
      }
      input = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF8"));
      String line = input.readLine();
      String[] parts = line.split("\\|");
      return new BSONTimestamp(Integer.parseInt(parts[0]),Integer.parseInt(parts[1]));
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      if(input != null){
        try{input.close();}
        catch(Exception e){}
      }
    }
    return null;
  }

  public void run(){
    running = true;
    BSONTimestamp lastTimestamp = null;
    try{
      lastTimestamp = getLastTimestamp();

      long lastWrite = 0;
      long startTime = System.currentTimeMillis();
      long lastOutput = System.currentTimeMillis();
      while(true){
        try{
          if(killMe) {
            System.out.println("exiting thread");
            return;
          }
          DBCursor cursor = null;
          if(lastTimestamp != null){
            cursor = oplog.find( new BasicDBObject( "ts" , new BasicDBObject( "$gt" , lastTimestamp ) ) );
            cursor.addOption( Bytes.QUERYOPTION_OPLOGREPLAY );
          }
          else{
            cursor = oplog.find();
          }
          cursor.addOption( Bytes.QUERYOPTION_TAILABLE );
          cursor.addOption( Bytes.QUERYOPTION_AWAITDATA );
          long count = 0;
          long skips = 0;

          while (!killMe && cursor.hasNext() ){
            DBObject x = cursor.next();
            if(!killMe) {
              lastTimestamp = (BSONTimestamp)x.get("ts");
              if(shouldWrite(x)){
                for(OplogRecordProcessor processor : processors)
                  processor.processRecord((BasicDBObject)x);
                count++;
              }
              else {
                skips++;
              }
              if(System.currentTimeMillis() - lastWrite > 1000){
                writeLastTimestamp(lastTimestamp);
                lastWrite = System.currentTimeMillis();
              }
              long duration = System.currentTimeMillis() - lastOutput;
              if(duration > reportInterval){
                report(this.getName(), count, skips, System.currentTimeMillis() - startTime);
                lastOutput = System.currentTimeMillis();
              }
            }
          }
        }
        catch(com.mongodb.MongoException.CursorNotFound ex){
          writeLastTimestamp(lastTimestamp);
          System.out.println("Cursor not found, waiting");
          Thread.sleep(2000);
        }
        catch(com.mongodb.MongoInternalException ex){
          System.out.println("Cursor not found, waiting");
          writeLastTimestamp(lastTimestamp);
          ex.printStackTrace();
        }
        catch(com.mongodb.MongoException ex){
          writeLastTimestamp(lastTimestamp);
          System.out.println("Internal exception, waiting");
          Thread.sleep(2000);
        }
        catch(Exception ex){
          killMe = true;
          writeLastTimestamp(lastTimestamp);
          ex.printStackTrace();
          break;
        }
      }
      Thread.sleep(1000);
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally{
      writeLastTimestamp(lastTimestamp);
      try{
        for(OplogRecordProcessor processor : processors) {
          processor.close("oplog");
        }
      }
      catch(Exception e){
        e.printStackTrace();
      }
    }
    running = false;
  }

  boolean shouldWrite(DBObject obj){
    String ns = (String)obj.get("ns");

    if(ns == null || "".equals(ns)){
      return false;
    }
    if(exclusions.size() == 0 && inclusions.size() == 0){
      return true;
    }
    if(exclusions.contains(ns)){
      return false;
    }
    if(inclusions.contains(ns) || inclusions.contains("*")){
      return true;
    }
    //  check database-level inclusion
    if(ns.indexOf('.') > 0 && inclusions.contains(ns.substring(0, ns.indexOf('.')))){
      return true;
    }

    return false;
  }

  void report(String collectionName, long count, long skips, long duration){
    double brate = (double)count / ((duration) / 1000.0);
    if(enableOutput)
      System.out.println(collectionName + ": " + PrintFormat.LONG_FORMAT.format(count) + " records, " + PrintFormat.LONG_FORMAT.format(brate) + " req/sec, " + PrintFormat.LONG_FORMAT.format(skips) + " skips");
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy