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 }