github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/mongo/oplog_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package mongo_test
     5  
     6  import (
     7  	"errors"
     8  	"reflect"
     9  	"time"
    10  
    11  	jujutesting "github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/mgo.v2"
    15  	"gopkg.in/mgo.v2/bson"
    16  
    17  	"github.com/juju/juju/mongo"
    18  	coretesting "github.com/juju/juju/testing"
    19  	"github.com/juju/juju/worker/peergrouper"
    20  )
    21  
    22  type oplogSuite struct {
    23  	coretesting.BaseSuite
    24  }
    25  
    26  var _ = gc.Suite(&oplogSuite{})
    27  
    28  func (s *oplogSuite) TestWithRealOplog(c *gc.C) {
    29  	_, session := s.startMongoWithReplicaset(c)
    30  
    31  	// Watch for oplog entries for the "bar" collection in the "foo"
    32  	// DB.
    33  	oplog := mongo.GetOplog(session)
    34  	tailer := mongo.NewOplogTailer(
    35  		mongo.NewOplogSession(
    36  			oplog,
    37  			bson.D{{"ns", "foo.bar"}},
    38  		),
    39  		time.Now().Add(-time.Minute),
    40  	)
    41  	defer tailer.Stop()
    42  
    43  	assertOplog := func(expectedOp string, expectedObj, expectedUpdate bson.D) {
    44  		doc := s.getNextOplog(c, tailer)
    45  		c.Assert(doc.Operation, gc.Equals, expectedOp)
    46  
    47  		var actualObj bson.D
    48  		err := doc.UnmarshalObject(&actualObj)
    49  		c.Assert(err, jc.ErrorIsNil)
    50  		// In Mongo 3.6, the documents add a "$V" to every document
    51  		// https://jira.mongodb.org/browse/SERVER-32240
    52  		// It seems to track the client information about what created the doc.
    53  		if len(actualObj) > 1 && actualObj[0].Name == "$v" {
    54  			actualObj = actualObj[1:]
    55  		}
    56  		c.Assert(actualObj, jc.DeepEquals, expectedObj)
    57  
    58  		var actualUpdate bson.D
    59  		err = doc.UnmarshalUpdate(&actualUpdate)
    60  		c.Assert(err, jc.ErrorIsNil)
    61  		c.Assert(actualUpdate, jc.DeepEquals, expectedUpdate)
    62  	}
    63  
    64  	// Insert into foo.bar and see that the oplog entry is reported.
    65  	db := session.DB("foo")
    66  	coll := db.C("bar")
    67  	s.insertDoc(c, session, coll, bson.M{"_id": "thing"})
    68  	assertOplog("i", bson.D{{"_id", "thing"}}, nil)
    69  
    70  	// Update foo.bar and see the update reported.
    71  	err := coll.UpdateId("thing", bson.M{"$set": bson.M{"blah": 42}})
    72  	c.Assert(err, jc.ErrorIsNil)
    73  	assertOplog("u", bson.D{{"$set", bson.D{{"blah", 42}}}}, bson.D{{"_id", "thing"}})
    74  
    75  	// Insert into another collection (shouldn't be reported due to filter).
    76  	s.insertDoc(c, session, db.C("elsewhere"), bson.M{"_id": "boo"})
    77  	s.assertNoOplog(c, tailer)
    78  }
    79  
    80  func (s *oplogSuite) TestHonoursInitialTs(c *gc.C) {
    81  	_, session := s.startMongo(c)
    82  
    83  	t := time.Now()
    84  
    85  	oplog := s.makeFakeOplog(c, session)
    86  	for offset := -1; offset <= 1; offset++ {
    87  		tDoc := t.Add(time.Duration(offset) * time.Second)
    88  		s.insertDoc(c, session, oplog,
    89  			&mongo.OplogDoc{Timestamp: mongo.NewMongoTimestamp(tDoc)},
    90  		)
    91  	}
    92  
    93  	tailer := mongo.NewOplogTailer(mongo.NewOplogSession(oplog, nil), t)
    94  	defer tailer.Stop()
    95  
    96  	for offset := 0; offset <= 1; offset++ {
    97  		doc := s.getNextOplog(c, tailer)
    98  		tExpected := t.Add(time.Duration(offset) * time.Second)
    99  		c.Assert(doc.Timestamp, gc.Equals, mongo.NewMongoTimestamp(tExpected))
   100  	}
   101  }
   102  
   103  func (s *oplogSuite) TestStops(c *gc.C) {
   104  	_, session := s.startMongo(c)
   105  
   106  	oplog := s.makeFakeOplog(c, session)
   107  	tailer := mongo.NewOplogTailer(mongo.NewOplogSession(oplog, nil), time.Time{})
   108  	defer tailer.Stop()
   109  
   110  	s.insertDoc(c, session, oplog, &mongo.OplogDoc{Timestamp: 1})
   111  	s.getNextOplog(c, tailer)
   112  
   113  	err := tailer.Stop()
   114  	c.Assert(err, jc.ErrorIsNil)
   115  
   116  	s.assertStopped(c, tailer)
   117  	c.Assert(tailer.Err(), jc.ErrorIsNil)
   118  }
   119  
   120  func (s *oplogSuite) TestRestartsOnErrCursor(c *gc.C) {
   121  	session := newFakeSession(
   122  		// First iterator terminates with an ErrCursor
   123  		newFakeIterator(mgo.ErrCursor, &mongo.OplogDoc{Timestamp: 1, OperationId: 99}),
   124  		newFakeIterator(nil, &mongo.OplogDoc{Timestamp: 2, OperationId: 42}),
   125  	)
   126  	tailer := mongo.NewOplogTailer(session, time.Time{})
   127  	defer tailer.Stop()
   128  
   129  	// First, ensure that the tailer is seeing oplog rows and handles
   130  	// the ErrCursor that occurs at the end.
   131  	doc := s.getNextOplog(c, tailer)
   132  	c.Check(doc.Timestamp, gc.Equals, bson.MongoTimestamp(1))
   133  	session.checkLastArgs(c, mongo.NewMongoTimestamp(time.Time{}), nil)
   134  
   135  	// Ensure that the tailer continues after getting a new iterator.
   136  	doc = s.getNextOplog(c, tailer)
   137  	c.Check(doc.Timestamp, gc.Equals, bson.MongoTimestamp(2))
   138  	session.checkLastArgs(c, bson.MongoTimestamp(1), []int64{99})
   139  }
   140  
   141  func (s *oplogSuite) TestNoRepeatsAfterIterRestart(c *gc.C) {
   142  	// A bunch of documents with the same timestamp but different ids.
   143  	// These will be split across 2 iterators.
   144  	docs := make([]*mongo.OplogDoc, 11)
   145  	for i := 0; i < 10; i++ {
   146  		id := int64(i + 10)
   147  		docs[i] = &mongo.OplogDoc{
   148  			Timestamp:   1,
   149  			OperationId: id,
   150  		}
   151  	}
   152  	// Add one more with a different timestamp.
   153  	docs[10] = &mongo.OplogDoc{
   154  		Timestamp:   2,
   155  		OperationId: 42,
   156  	}
   157  	session := newFakeSession(
   158  		// First block of documents, all time 1
   159  		newFakeIterator(nil, docs[:5]...),
   160  		// Second block, some time 1, one time 2
   161  		newFakeIterator(nil, docs[5:]...),
   162  	)
   163  	tailer := mongo.NewOplogTailer(session, time.Time{})
   164  	defer tailer.Stop()
   165  
   166  	for id := int64(10); id < 15; id++ {
   167  		doc := s.getNextOplog(c, tailer)
   168  		c.Assert(doc.Timestamp, gc.Equals, bson.MongoTimestamp(1))
   169  		c.Assert(doc.OperationId, gc.Equals, id)
   170  	}
   171  
   172  	// Check the query doesn't exclude any in the first request.
   173  	session.checkLastArgs(c, mongo.NewMongoTimestamp(time.Time{}), nil)
   174  
   175  	// The OplogTailer will fall off the end of the iterator and get a new one.
   176  
   177  	// Ensure that only previously unreported entries are now reported.
   178  	for id := int64(15); id < 20; id++ {
   179  		doc := s.getNextOplog(c, tailer)
   180  		c.Assert(doc.Timestamp, gc.Equals, bson.MongoTimestamp(1))
   181  		c.Assert(doc.OperationId, gc.Equals, id)
   182  	}
   183  
   184  	// Check we got the next block correctly
   185  	session.checkLastArgs(c, bson.MongoTimestamp(1), []int64{10, 11, 12, 13, 14})
   186  
   187  	doc := s.getNextOplog(c, tailer)
   188  	c.Assert(doc.Timestamp, gc.Equals, bson.MongoTimestamp(2))
   189  	c.Assert(doc.OperationId, gc.Equals, int64(42))
   190  }
   191  
   192  func (s *oplogSuite) TestDiesOnFatalError(c *gc.C) {
   193  	expectedErr := errors.New("oh no, the collection went away!")
   194  	session := newFakeSession(
   195  		newFakeIterator(expectedErr, &mongo.OplogDoc{Timestamp: 1}),
   196  	)
   197  
   198  	tailer := mongo.NewOplogTailer(session, time.Time{})
   199  	defer tailer.Stop()
   200  
   201  	doc := s.getNextOplog(c, tailer)
   202  	c.Assert(doc.Timestamp, gc.Equals, bson.MongoTimestamp(1))
   203  	s.assertStopped(c, tailer)
   204  	c.Assert(tailer.Err(), gc.Equals, expectedErr)
   205  }
   206  
   207  func (s *oplogSuite) TestNewMongoTimestamp(c *gc.C) {
   208  	t := time.Date(2015, 6, 24, 12, 47, 0, 0, time.FixedZone("somewhere", 5*3600))
   209  
   210  	expected := bson.MongoTimestamp(6163845091342417920)
   211  	c.Assert(mongo.NewMongoTimestamp(t), gc.Equals, expected)
   212  	c.Assert(mongo.NewMongoTimestamp(t.In(time.UTC)), gc.Equals, expected)
   213  }
   214  
   215  func (s *oplogSuite) TestNewMongoTimestampBeforeUnixEpoch(c *gc.C) {
   216  	c.Assert(mongo.NewMongoTimestamp(time.Time{}), gc.Equals, bson.MongoTimestamp(0))
   217  }
   218  
   219  func (s *oplogSuite) startMongoWithReplicaset(c *gc.C) (*jujutesting.MgoInstance, *mgo.Session) {
   220  	inst := &jujutesting.MgoInstance{
   221  		Params: []string{
   222  			"--replSet", "juju",
   223  		},
   224  	}
   225  	err := inst.Start(nil)
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	s.AddCleanup(func(*gc.C) { inst.Destroy() })
   228  
   229  	// Initiate replicaset.
   230  	info := inst.DialInfo()
   231  	args := peergrouper.InitiateMongoParams{
   232  		DialInfo:       info,
   233  		MemberHostPort: inst.Addr(),
   234  	}
   235  	err = peergrouper.InitiateMongoServer(args)
   236  	c.Assert(err, jc.ErrorIsNil)
   237  
   238  	return inst, s.dialMongo(c, inst)
   239  }
   240  
   241  func (s *oplogSuite) startMongo(c *gc.C) (*jujutesting.MgoInstance, *mgo.Session) {
   242  	var inst jujutesting.MgoInstance
   243  	err := inst.Start(nil)
   244  	c.Assert(err, jc.ErrorIsNil)
   245  	s.AddCleanup(func(*gc.C) { inst.Destroy() })
   246  	return &inst, s.dialMongo(c, &inst)
   247  }
   248  
   249  func (s *oplogSuite) dialMongo(c *gc.C, inst *jujutesting.MgoInstance) *mgo.Session {
   250  	session, err := inst.Dial()
   251  	c.Assert(err, jc.ErrorIsNil)
   252  	s.AddCleanup(func(*gc.C) { session.Close() })
   253  	return session
   254  }
   255  
   256  func (s *oplogSuite) makeFakeOplog(c *gc.C, session *mgo.Session) *mgo.Collection {
   257  	db := session.DB("foo")
   258  	oplog := db.C("oplog.fake")
   259  	err := oplog.Create(&mgo.CollectionInfo{
   260  		Capped:   true,
   261  		MaxBytes: 1024 * 1024,
   262  	})
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	return oplog
   265  }
   266  
   267  func (s *oplogSuite) insertDoc(c *gc.C, srcSession *mgo.Session, coll *mgo.Collection, doc interface{}) {
   268  	session := srcSession.Copy()
   269  	defer session.Close()
   270  	err := coll.With(session).Insert(doc)
   271  	c.Assert(err, jc.ErrorIsNil)
   272  }
   273  
   274  func (s *oplogSuite) getNextOplog(c *gc.C, tailer *mongo.OplogTailer) *mongo.OplogDoc {
   275  	select {
   276  	case doc, ok := <-tailer.Out():
   277  		if !ok {
   278  			c.Fatalf("tailer unexpectedly died: %v", tailer.Err())
   279  		}
   280  		return doc
   281  	case <-time.After(coretesting.LongWait):
   282  		c.Fatal("timed out waiting for oplog doc")
   283  	}
   284  	return nil
   285  }
   286  
   287  func (s *oplogSuite) assertNoOplog(c *gc.C, tailer *mongo.OplogTailer) {
   288  	select {
   289  	case _, ok := <-tailer.Out():
   290  		if !ok {
   291  			c.Fatalf("tailer unexpectedly died: %v", tailer.Err())
   292  		}
   293  		c.Fatal("unexpected oplog activity reported")
   294  	case <-time.After(coretesting.ShortWait):
   295  		// Success
   296  	}
   297  }
   298  
   299  func (s *oplogSuite) assertStopped(c *gc.C, tailer *mongo.OplogTailer) {
   300  	// Output should close.
   301  	select {
   302  	case _, ok := <-tailer.Out():
   303  		c.Assert(ok, jc.IsFalse)
   304  	case <-time.After(coretesting.LongWait):
   305  		c.Fatal("tailer output should have closed")
   306  	}
   307  
   308  	// OplogTailer should die.
   309  	select {
   310  	case <-tailer.Dying():
   311  		// Success.
   312  	case <-time.After(coretesting.LongWait):
   313  		c.Fatal("tailer should have died")
   314  	}
   315  }
   316  
   317  type fakeIterator struct {
   318  	docs    []*mongo.OplogDoc
   319  	pos     int
   320  	err     error
   321  	timeout bool
   322  }
   323  
   324  func (i *fakeIterator) Next(result interface{}) bool {
   325  	if i.pos >= len(i.docs) {
   326  		return false
   327  	}
   328  	target := reflect.ValueOf(result).Elem()
   329  	target.Set(reflect.ValueOf(*i.docs[i.pos]))
   330  	i.pos++
   331  	return true
   332  }
   333  
   334  func (i *fakeIterator) Timeout() bool {
   335  	if i.pos < len(i.docs) {
   336  		return false
   337  	}
   338  	return i.timeout
   339  }
   340  
   341  func (i *fakeIterator) Close() error {
   342  	if i.pos < len(i.docs) {
   343  		return nil
   344  	}
   345  	return i.err
   346  }
   347  
   348  func newFakeIterator(err error, docs ...*mongo.OplogDoc) *fakeIterator {
   349  	return &fakeIterator{docs: docs, err: err}
   350  }
   351  
   352  type iterArgs struct {
   353  	timestamp  bson.MongoTimestamp
   354  	excludeIds []int64
   355  }
   356  
   357  type fakeSession struct {
   358  	iterators []*fakeIterator
   359  	pos       int
   360  	args      chan iterArgs
   361  }
   362  
   363  var timeoutIterator = fakeIterator{timeout: true}
   364  
   365  func (s *fakeSession) NewIter(ts bson.MongoTimestamp, ids []int64) mongo.Iterator {
   366  	if s.pos >= len(s.iterators) {
   367  		// We've run out of results - at this point the calls to get
   368  		// more data would just keep timing out.
   369  		return &timeoutIterator
   370  	}
   371  	select {
   372  	case <-time.After(coretesting.LongWait):
   373  		panic("took too long to save args")
   374  	case s.args <- iterArgs{ts, ids}:
   375  	}
   376  	result := s.iterators[s.pos]
   377  	s.pos++
   378  	return result
   379  }
   380  
   381  func (s *fakeSession) Close() {}
   382  
   383  func (s *fakeSession) checkLastArgs(c *gc.C, ts bson.MongoTimestamp, ids []int64) {
   384  	select {
   385  	case <-time.After(coretesting.LongWait):
   386  		c.Logf("timeout getting iter args - test problem")
   387  		c.FailNow()
   388  	case res := <-s.args:
   389  		c.Check(res.timestamp, gc.Equals, ts)
   390  		c.Check(res.excludeIds, gc.DeepEquals, ids)
   391  	}
   392  }
   393  
   394  func newFakeSession(iterators ...*fakeIterator) *fakeSession {
   395  	return &fakeSession{
   396  		iterators: iterators,
   397  		args:      make(chan iterArgs, 5),
   398  	}
   399  }