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

util.OpLog.scala Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2010, 2011 10gen, Inc. 
 * Copyright (c) 2009, 2010 Novus Partners, Inc. 
 * 
 * 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.
 *
 * For questions and comments about this product, please see the project page at:
 *
 *     http://github.com/mongodb/casbah
 * 
 */

package com.mongodb.casbah
package util

import org.bson.types.BSONTimestamp
import com.mongodb.Bytes

import com.mongodb.casbah.Imports._
import com.mongodb.casbah.commons.Logging

import scala.util.control.Exception._

class MongoOpLog(mongo: MongoConnection = MongoConnection(),
  startTimestamp: Option[BSONTimestamp] = None,
  namespace: Option[String] = None) extends Iterator[MongoOpLogEntry] with Logging {

  implicit object BSONTimestampOk extends ValidDateOrNumericType[org.bson.types.BSONTimestamp]

  protected val local = mongo("local")
  protected val oplog = local("oplog.$main")

  val tsp = verifyOpLog

  log.debug("Beginning monitoring OpLog at '%s'", tsp)

  val q = namespace match {
    case Some(ns) => ("ts" $gt tsp) ++ ("ns" -> ns)
    case None => "ts" $gt tsp
  }

  log.debug("OpLog Filter: '%s'", q)

  val cursor = oplog.find(q)

  cursor.options = Bytes.QUERYOPTION_TAILABLE
  cursor.options = Bytes.QUERYOPTION_AWAITDATA

  def hasNext = cursor.hasNext

  def next = MongoOpLogEntry(cursor.next)

  def verifyOpLog: BSONTimestamp = {
    // Verify the oplog exists 
    val last = oplog.find().sort(MongoDBObject("$natural" -> 1)).limit(1)
    assume(last.hasNext,
      "No oplog found. mongod must be a --master or belong to a Replica Set.")
    /**
     * If a startTimestamp was specified attempt to sync from there.
     * An exception is thrown if the timestamp isn't found because
     * you won't be able to sync. 
     */
    startTimestamp match {
      case Some(ts) => {
        oplog.findOne(MongoDBObject("ts" -> ts)).orElse(
          throw new Exception("No oplog entry for requested start timestamp."))
        ts
      }
      case None => last.next().as[BSONTimestamp]("ts")
    }
  }

}

object MongoOpLogEntry {
  def apply(entry: MongoDBObject) = entry("op") match {
    case InsertOp =>
      MongoInsertOperation(
        entry.as[BSONTimestamp]("ts"),
        entry.as[Long]("h"),
        entry.as[String]("ns"),
        entry.as[DBObject]("o"))
    case UpdateOp =>
      MongoUpdateOperation(
        entry.as[BSONTimestamp]("ts"),
        entry.as[Long]("h"),
        entry.as[String]("ns"),
        entry.as[DBObject]("o"),
        entry.as[DBObject]("o2"))
    case DeleteOp =>
      MongoInsertOperation(
        entry.as[BSONTimestamp]("ts"),
        entry.as[Long]("h"),
        entry.as[String]("ns"),
        entry.as[DBObject]("o"))
  }
}

sealed trait MongoOpType { def typeCode: String }
case object InsertOp extends MongoOpType { val typeCode = "i" }
case object UpdateOp extends MongoOpType { val typeCode = "u" }
case object DeleteOp extends MongoOpType { val typeCode = "d" }

sealed trait MongoOpLogEntry {
  val timestamp: BSONTimestamp
  lazy val ts = timestamp

  val opID: Long
  lazy val h = opID

  val opType: MongoOpType
  lazy val op = opType

  val namespace: String
  lazy val ns = namespace

  val document: MongoDBObject
  lazy val o = document

}

case class MongoInsertOperation(timestamp: BSONTimestamp, opID: Long, namespace: String, document: MongoDBObject) extends MongoOpLogEntry {
  val opType = InsertOp
}

case class MongoUpdateOperation(timestamp: BSONTimestamp, opID: Long, namespace: String, document: MongoDBObject, documentID: MongoDBObject) extends MongoOpLogEntry {
  val opType = UpdateOp
  lazy val o2 = documentID
}

case class MongoDeleteOperation(timestamp: BSONTimestamp, opID: Long, namespace: String, document: MongoDBObject) extends MongoOpLogEntry {
  val opType = DeleteOp
}

// vim: set ts=2 sw=2 sts=2 et:




© 2015 - 2025 Weber Informatics LLC | Privacy Policy