/casbah-core/src/main/scala/util/OpLog.scala

https://github.com/derzzle/casbah · Scala · 144 lines · 87 code · 29 blank · 28 comment · 0 complexity · 8e29361985b431fc33252f00b9ad5ea8 MD5 · raw file

  1. /**
  2. * Copyright (c) 2010, 2011 10gen, Inc. <http://10gen.com>
  3. * Copyright (c) 2009, 2010 Novus Partners, Inc. <http://novus.com>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. * For questions and comments about this product, please see the project page at:
  18. *
  19. * http://github.com/mongodb/casbah
  20. *
  21. */
  22. package com.mongodb.casbah
  23. package util
  24. import org.bson.types.BSONTimestamp
  25. import com.mongodb.Bytes
  26. import com.mongodb.casbah.Imports._
  27. import scala.util.control.Exception._
  28. class MongoOpLog(mongo: MongoConnection = MongoConnection(),
  29. startTimestamp: Option[BSONTimestamp] = None,
  30. namespace: Option[String] = None) extends Iterator[MongoOpLogEntry] with Logging {
  31. implicit object BSONTimestampOk extends ValidDateOrNumericType[org.bson.types.BSONTimestamp]
  32. protected val local = mongo("local")
  33. protected val oplog = local("oplog.$main")
  34. val tsp = verifyOpLog
  35. log.debug("Beginning monitoring OpLog at '%s'", tsp)
  36. val q = namespace match {
  37. case Some(ns) => ("ts" $gt tsp) ++ ("ns" -> ns)
  38. case None => "ts" $gt tsp
  39. }
  40. log.debug("OpLog Filter: '%s'", q)
  41. val cursor = oplog.find(q)
  42. cursor.options = Bytes.QUERYOPTION_TAILABLE
  43. cursor.options = Bytes.QUERYOPTION_AWAITDATA
  44. def hasNext = cursor.hasNext
  45. def next = MongoOpLogEntry(cursor.next)
  46. def verifyOpLog: BSONTimestamp = {
  47. // Verify the oplog exists
  48. val last = oplog.find().sort(MongoDBObject("$natural" -> 1)).limit(1)
  49. assume(last.hasNext,
  50. "No oplog found. mongod must be a --master or belong to a Replica Set.")
  51. /**
  52. * If a startTimestamp was specified attempt to sync from there.
  53. * An exception is thrown if the timestamp isn't found because
  54. * you won't be able to sync.
  55. */
  56. startTimestamp match {
  57. case Some(ts) => {
  58. oplog.findOne(MongoDBObject("ts" -> ts)).orElse(
  59. throw new Exception("No oplog entry for requested start timestamp."))
  60. ts
  61. }
  62. case None => last.next().as[BSONTimestamp]("ts")
  63. }
  64. }
  65. }
  66. object MongoOpLogEntry {
  67. def apply(entry: MongoDBObject) = entry("op") match {
  68. case InsertOp =>
  69. MongoInsertOperation(
  70. entry.as[BSONTimestamp]("ts"),
  71. entry.as[Long]("h"),
  72. entry.as[String]("ns"),
  73. entry.as[DBObject]("o"))
  74. case UpdateOp =>
  75. MongoUpdateOperation(
  76. entry.as[BSONTimestamp]("ts"),
  77. entry.as[Long]("h"),
  78. entry.as[String]("ns"),
  79. entry.as[DBObject]("o"),
  80. entry.as[DBObject]("o2"))
  81. case DeleteOp =>
  82. MongoInsertOperation(
  83. entry.as[BSONTimestamp]("ts"),
  84. entry.as[Long]("h"),
  85. entry.as[String]("ns"),
  86. entry.as[DBObject]("o"))
  87. }
  88. }
  89. sealed trait MongoOpType { def typeCode: String }
  90. case object InsertOp extends MongoOpType { val typeCode = "i" }
  91. case object UpdateOp extends MongoOpType { val typeCode = "u" }
  92. case object DeleteOp extends MongoOpType { val typeCode = "d" }
  93. sealed trait MongoOpLogEntry {
  94. val timestamp: BSONTimestamp
  95. lazy val ts = timestamp
  96. val opID: Long
  97. lazy val h = opID
  98. val opType: MongoOpType
  99. lazy val op = opType
  100. val namespace: String
  101. lazy val ns = namespace
  102. val document: MongoDBObject
  103. lazy val o = document
  104. }
  105. case class MongoInsertOperation(timestamp: BSONTimestamp, opID: Long, namespace: String, document: MongoDBObject) extends MongoOpLogEntry {
  106. val opType = InsertOp
  107. }
  108. case class MongoUpdateOperation(timestamp: BSONTimestamp, opID: Long, namespace: String, document: MongoDBObject, documentID: MongoDBObject) extends MongoOpLogEntry {
  109. val opType = UpdateOp
  110. lazy val o2 = documentID
  111. }
  112. case class MongoDeleteOperation(timestamp: BSONTimestamp, opID: Long, namespace: String, document: MongoDBObject) extends MongoOpLogEntry {
  113. val opType = DeleteOp
  114. }
  115. // vim: set ts=2 sw=2 sts=2 et: