func (t *logTailer) tailOplog() error { recentIds := t.recentIds.AsSet() newParams := t.params newParams.StartID = t.lastID // (t.lastID + 1) once Id is a sequential int. oplogSel := append(t.paramsToSelector(newParams, "o."), bson.DocElem{"ns", logsDB + "." + logsC}, ) oplog := t.params.Oplog if oplog == nil { oplog = mongo.GetOplog(t.session) } minOplogTs := t.lastTime.Add(-oplogOverlap) oplogTailer := mongo.NewOplogTailer(mongo.NewOplogSession(oplog, oplogSel), minOplogTs) defer oplogTailer.Stop() logger.Tracef("LogTailer starting oplog tailing: recent id count=%d, lastTime=%s, minOplogTs=%s", recentIds.Length(), t.lastTime, minOplogTs) skipCount := 0 for { select { case <-t.tomb.Dying(): return errors.Trace(tomb.ErrDying) case oplogDoc, ok := <-oplogTailer.Out(): if !ok { return errors.Annotate(oplogTailer.Err(), "oplog tailer died") } doc := new(logDoc) err := oplogDoc.UnmarshalObject(doc) if err != nil { return errors.Annotate(err, "oplog unmarshalling failed") } if recentIds.Contains(doc.Id) { // This document has already been reported. skipCount++ if skipCount%1000 == 0 { logger.Tracef("LogTailer duplicates skipped: %d", skipCount) } continue } rec, err := logDocToRecord(doc) if err != nil { return errors.Annotate(err, "deserialization failed (possible DB corruption)") } select { case <-t.tomb.Dying(): return errors.Trace(tomb.ErrDying) case t.logCh <- rec: } } } }
func (s *oplogSuite) TestWithRealOplog(c *gc.C) { _, session := s.startMongoWithReplicaset(c) // Watch for oplog entries for the "bar" collection in the "foo" // DB. oplog := mongo.GetOplog(session) tailer := mongo.NewOplogTailer( mongo.NewOplogSession( oplog, bson.D{{"ns", "foo.bar"}}, ), time.Now().Add(-time.Minute), ) defer tailer.Stop() assertOplog := func(expectedOp string, expectedObj, expectedUpdate bson.D) { doc := s.getNextOplog(c, tailer) c.Assert(doc.Operation, gc.Equals, expectedOp) var actualObj bson.D err := doc.UnmarshalObject(&actualObj) c.Assert(err, jc.ErrorIsNil) c.Assert(actualObj, jc.DeepEquals, expectedObj) var actualUpdate bson.D err = doc.UnmarshalUpdate(&actualUpdate) c.Assert(err, jc.ErrorIsNil) c.Assert(actualUpdate, jc.DeepEquals, expectedUpdate) } // Insert into foo.bar and see that the oplog entry is reported. db := session.DB("foo") coll := db.C("bar") s.insertDoc(c, session, coll, bson.M{"_id": "thing"}) assertOplog("i", bson.D{{"_id", "thing"}}, nil) // Update foo.bar and see the update reported. err := coll.UpdateId("thing", bson.M{"$set": bson.M{"blah": 42}}) c.Assert(err, jc.ErrorIsNil) assertOplog("u", bson.D{{"$set", bson.D{{"blah", 42}}}}, bson.D{{"_id", "thing"}}) // Insert into another collection (shouldn't be reported due to filter). s.insertDoc(c, session, db.C("elsewhere"), bson.M{"_id": "boo"}) s.assertNoOplog(c, tailer) }