github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/watcher/txnwatcher_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package watcher_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/clock/testclock"
    10  	"github.com/juju/loggo"
    11  	gitjujutesting "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/txn"
    16  	"gopkg.in/tomb.v2"
    17  
    18  	"github.com/juju/juju/mongo"
    19  	"github.com/juju/juju/state/watcher"
    20  	"github.com/juju/juju/testing"
    21  )
    22  
    23  type TxnWatcherSuite struct {
    24  	gitjujutesting.MgoSuite
    25  	testing.BaseSuite
    26  
    27  	log          *mgo.Collection
    28  	stash        *mgo.Collection
    29  	runner       *txn.Runner
    30  	w            *watcher.TxnWatcher
    31  	ch           chan watcher.Change
    32  	iteratorFunc func() mongo.Iterator
    33  	clock        *testclock.Clock
    34  }
    35  
    36  var _ = gc.Suite(&TxnWatcherSuite{})
    37  
    38  func (s *TxnWatcherSuite) SetUpSuite(c *gc.C) {
    39  	s.MgoSuite.SetUpSuite(c)
    40  	s.BaseSuite.SetUpSuite(c)
    41  }
    42  
    43  func (s *TxnWatcherSuite) TearDownSuite(c *gc.C) {
    44  	s.BaseSuite.TearDownSuite(c)
    45  	s.MgoSuite.TearDownSuite(c)
    46  }
    47  
    48  func (s *TxnWatcherSuite) SetUpTest(c *gc.C) {
    49  	s.MgoSuite.SetUpTest(c)
    50  	s.BaseSuite.SetUpTest(c)
    51  
    52  	db := s.MgoSuite.Session.DB("juju")
    53  	s.log = db.C("txnlog")
    54  	s.log.Create(&mgo.CollectionInfo{
    55  		Capped:   true,
    56  		MaxBytes: 1000000,
    57  	})
    58  	s.stash = db.C("txn.stash")
    59  	s.runner = txn.NewRunner(db.C("txn"))
    60  	s.runner.ChangeLog(s.log)
    61  	s.clock = testclock.NewClock(time.Now())
    62  }
    63  
    64  func (s *TxnWatcherSuite) TearDownTest(c *gc.C) {
    65  	s.BaseSuite.TearDownTest(c)
    66  	s.MgoSuite.TearDownTest(c)
    67  }
    68  
    69  func (s *TxnWatcherSuite) advanceTime(c *gc.C, d time.Duration, waiters int) {
    70  	// Here we are assuming that there is one and only one thing
    71  	// using the After function on the testing clock, that being our
    72  	// watcher.
    73  	c.Assert(s.clock.WaitAdvance(d, testing.ShortWait, waiters), jc.ErrorIsNil)
    74  }
    75  
    76  func (s *TxnWatcherSuite) newWatcher(c *gc.C, expect int) (*watcher.TxnWatcher, *fakeHub) {
    77  	hub := newFakeHub(c, expect)
    78  	logger := loggo.GetLogger("test")
    79  	logger.SetLogLevel(loggo.TRACE)
    80  	w, err := watcher.NewTxnWatcher(watcher.TxnWatcherConfig{
    81  		ChangeLog: s.log,
    82  		Hub:       hub,
    83  		Clock:     s.clock,
    84  		Logger:    logger,
    85  	})
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	// Wait for the main loop to have started.
    88  	select {
    89  	case <-hub.started:
    90  	case <-time.After(testing.LongWait):
    91  		c.Error("txn worker failed to start")
    92  	}
    93  	s.AddCleanup(func(c *gc.C) {
    94  		c.Assert(w.Stop(), jc.ErrorIsNil)
    95  	})
    96  	return w, hub
    97  }
    98  
    99  func (s *TxnWatcherSuite) revno(c *gc.C, coll string, id interface{}) (revno int64) {
   100  	var doc struct {
   101  		Revno int64 `bson:"txn-revno"`
   102  	}
   103  	err := s.log.Database.C(coll).FindId(id).One(&doc)
   104  	c.Assert(err, jc.ErrorIsNil)
   105  	return doc.Revno
   106  }
   107  
   108  func (s *TxnWatcherSuite) insert(c *gc.C, coll string, id interface{}) (revno int64) {
   109  	ops := []txn.Op{{C: coll, Id: id, Insert: M{"n": 1}}}
   110  	err := s.runner.Run(ops, "", nil)
   111  	c.Assert(err, jc.ErrorIsNil)
   112  	revno = s.revno(c, coll, id)
   113  	c.Logf("insert(%#v, %#v) => revno %d", coll, id, revno)
   114  	return revno
   115  }
   116  
   117  func (s *TxnWatcherSuite) insertAll(c *gc.C, coll string, ids ...interface{}) (revnos []int64) {
   118  	var ops []txn.Op
   119  	for _, id := range ids {
   120  		ops = append(ops, txn.Op{C: coll, Id: id, Insert: M{"n": 1}})
   121  	}
   122  	err := s.runner.Run(ops, "", nil)
   123  	c.Assert(err, jc.ErrorIsNil)
   124  	for _, id := range ids {
   125  		revnos = append(revnos, s.revno(c, coll, id))
   126  	}
   127  	c.Logf("insertAll(%#v, %v) => revnos %v", coll, ids, revnos)
   128  	return revnos
   129  }
   130  
   131  func (s *TxnWatcherSuite) update(c *gc.C, coll string, id interface{}) (revno int64) {
   132  	ops := []txn.Op{{C: coll, Id: id, Update: M{"$inc": M{"n": 1}}}}
   133  	err := s.runner.Run(ops, "", nil)
   134  	c.Assert(err, jc.ErrorIsNil)
   135  	revno = s.revno(c, coll, id)
   136  	c.Logf("update(%#v, %#v) => revno %d", coll, id, revno)
   137  	return revno
   138  }
   139  
   140  func (s *TxnWatcherSuite) remove(c *gc.C, coll string, id interface{}) (revno int64) {
   141  	ops := []txn.Op{{C: coll, Id: id, Remove: true}}
   142  	err := s.runner.Run(ops, "", nil)
   143  	c.Assert(err, jc.ErrorIsNil)
   144  	c.Logf("remove(%#v, %#v) => revno -1", coll, id)
   145  	return -1
   146  }
   147  
   148  func (s *TxnWatcherSuite) TestErrAndDead(c *gc.C) {
   149  	w, _ := s.newWatcher(c, 0)
   150  	c.Assert(w.Err(), gc.Equals, tomb.ErrStillAlive)
   151  	select {
   152  	case <-w.Dead():
   153  		c.Fatalf("Dead channel fired unexpectedly")
   154  	default:
   155  	}
   156  	c.Assert(w.Stop(), jc.ErrorIsNil)
   157  	c.Assert(w.Err(), jc.ErrorIsNil)
   158  	select {
   159  	case <-w.Dead():
   160  	default:
   161  		c.Fatalf("Dead channel should have fired")
   162  	}
   163  }
   164  
   165  func (s *TxnWatcherSuite) TestInsert(c *gc.C) {
   166  	_, hub := s.newWatcher(c, 1)
   167  
   168  	revno := s.insert(c, "test", "a")
   169  
   170  	s.advanceTime(c, watcher.TxnWatcherShortWait, 1)
   171  	hub.waitForExpected(c)
   172  
   173  	c.Assert(hub.values, jc.DeepEquals, []watcher.Change{
   174  		{"test", "a", revno},
   175  	})
   176  }
   177  
   178  func (s *TxnWatcherSuite) TestUpdate(c *gc.C) {
   179  	s.insert(c, "test", "a")
   180  
   181  	_, hub := s.newWatcher(c, 1)
   182  	revno := s.update(c, "test", "a")
   183  
   184  	s.advanceTime(c, watcher.TxnWatcherShortWait, 1)
   185  	hub.waitForExpected(c)
   186  
   187  	c.Assert(hub.values, jc.DeepEquals, []watcher.Change{
   188  		{"test", "a", revno},
   189  	})
   190  }
   191  
   192  func (s *TxnWatcherSuite) TestRemove(c *gc.C) {
   193  	s.insert(c, "test", "a")
   194  
   195  	_, hub := s.newWatcher(c, 1)
   196  	revno := s.remove(c, "test", "a")
   197  
   198  	s.advanceTime(c, watcher.TxnWatcherShortWait, 1)
   199  	hub.waitForExpected(c)
   200  
   201  	c.Assert(hub.values, jc.DeepEquals, []watcher.Change{
   202  		{"test", "a", revno},
   203  	})
   204  }
   205  
   206  func (s *TxnWatcherSuite) TestWatchOrder(c *gc.C) {
   207  	_, hub := s.newWatcher(c, 3)
   208  
   209  	revno1 := s.insert(c, "test", "a")
   210  	revno2 := s.insert(c, "test", "b")
   211  	revno3 := s.insert(c, "test", "c")
   212  
   213  	s.advanceTime(c, watcher.TxnWatcherShortWait, 1)
   214  	hub.waitForExpected(c)
   215  
   216  	c.Assert(hub.values, jc.DeepEquals, []watcher.Change{
   217  		{"test", "a", revno1},
   218  		{"test", "b", revno2},
   219  		{"test", "c", revno3},
   220  	})
   221  }
   222  
   223  func (s *TxnWatcherSuite) TestTransactionWithMultiple(c *gc.C) {
   224  	_, hub := s.newWatcher(c, 3)
   225  
   226  	revnos := s.insertAll(c, "test", "a", "b", "c")
   227  
   228  	s.advanceTime(c, watcher.TxnWatcherShortWait, 1)
   229  	hub.waitForExpected(c)
   230  
   231  	c.Assert(hub.values, jc.DeepEquals, []watcher.Change{
   232  		{"test", "a", revnos[0]},
   233  		{"test", "b", revnos[1]},
   234  		{"test", "c", revnos[2]},
   235  	})
   236  }
   237  
   238  func (s *TxnWatcherSuite) TestScale(c *gc.C) {
   239  	const N = 500
   240  	const T = 10
   241  
   242  	_, hub := s.newWatcher(c, N)
   243  
   244  	c.Logf("Creating %d documents, %d per transaction...", N, T)
   245  	ops := make([]txn.Op, T)
   246  	for i := 0; i < (N / T); i++ {
   247  		ops = ops[:0]
   248  		for j := 0; j < T && i*T+j < N; j++ {
   249  			ops = append(ops, txn.Op{C: "test", Id: i*T + j, Insert: M{"n": 1}})
   250  		}
   251  		err := s.runner.Run(ops, "", nil)
   252  		c.Assert(err, jc.ErrorIsNil)
   253  	}
   254  
   255  	count, err := s.Session.DB("juju").C("test").Count()
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	c.Logf("Got %d documents in the collection...", count)
   258  	c.Assert(count, gc.Equals, N)
   259  
   260  	s.advanceTime(c, watcher.TxnWatcherShortWait, 1)
   261  	hub.waitForExpected(c)
   262  
   263  	for i := 0; i < N; i++ {
   264  		c.Assert(hub.values[i].Id, gc.Equals, i)
   265  	}
   266  }
   267  
   268  func (s *TxnWatcherSuite) TestInsertThenRemove(c *gc.C) {
   269  	_, hub := s.newWatcher(c, 2)
   270  
   271  	revno1 := s.insert(c, "test", "a")
   272  	s.advanceTime(c, watcher.TxnWatcherShortWait, 1)
   273  	revno2 := s.remove(c, "test", "a")
   274  	s.advanceTime(c, watcher.TxnWatcherShortWait, 2)
   275  
   276  	hub.waitForExpected(c)
   277  
   278  	c.Assert(hub.values, jc.DeepEquals, []watcher.Change{
   279  		{"test", "a", revno1},
   280  		{"test", "a", revno2},
   281  	})
   282  }
   283  
   284  func (s *TxnWatcherSuite) TestDoubleUpdate(c *gc.C) {
   285  	_, hub := s.newWatcher(c, 2)
   286  
   287  	revno1 := s.insert(c, "test", "a")
   288  	s.advanceTime(c, watcher.TxnWatcherShortWait, 1)
   289  	s.update(c, "test", "a")
   290  	revno3 := s.update(c, "test", "a")
   291  	s.advanceTime(c, watcher.TxnWatcherShortWait, 2)
   292  
   293  	hub.waitForExpected(c)
   294  
   295  	c.Assert(hub.values, jc.DeepEquals, []watcher.Change{
   296  		{"test", "a", revno1},
   297  		{"test", "a", revno3},
   298  	})
   299  }
   300  
   301  type fakeHub struct {
   302  	c       *gc.C
   303  	expect  int
   304  	values  []watcher.Change
   305  	started chan struct{}
   306  	done    chan struct{}
   307  }
   308  
   309  func newFakeHub(c *gc.C, expected int) *fakeHub {
   310  	return &fakeHub{
   311  		c:       c,
   312  		expect:  expected,
   313  		started: make(chan struct{}),
   314  		done:    make(chan struct{}),
   315  	}
   316  }
   317  
   318  func (hub *fakeHub) Publish(topic string, data interface{}) <-chan struct{} {
   319  	switch topic {
   320  	case watcher.TxnWatcherStarting:
   321  		close(hub.started)
   322  	case watcher.TxnWatcherCollection:
   323  		change := data.(watcher.Change)
   324  		hub.values = append(hub.values, change)
   325  		if len(hub.values) == hub.expect {
   326  			close(hub.done)
   327  		}
   328  	default:
   329  		hub.c.Errorf("unknown topic %q", topic)
   330  	}
   331  	return nil
   332  }
   333  
   334  func (hub *fakeHub) waitForExpected(c *gc.C) {
   335  	select {
   336  	case <-hub.done:
   337  	case <-time.After(testing.LongWait):
   338  		c.Error("hub didn't get the expected number of changes")
   339  	}
   340  }