github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/state/lease/client_race_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package lease_test 5 6 import ( 7 "time" 8 9 jc "github.com/juju/testing/checkers" 10 jujutxn "github.com/juju/txn" 11 txntesting "github.com/juju/txn/testing" 12 gc "gopkg.in/check.v1" 13 _ "gopkg.in/mgo.v2/bson" 14 15 "github.com/juju/juju/state/lease" 16 ) 17 18 // ClientSimpleRaceSuite tests what happens when two clients interfere with 19 // each other when creating clients and/or leases. 20 type ClientSimpleRaceSuite struct { 21 FixtureSuite 22 } 23 24 var _ = gc.Suite(&ClientSimpleRaceSuite{}) 25 26 func (s *ClientSimpleRaceSuite) TestNewClient_WorksDespite_CreateClockRace(c *gc.C) { 27 config := func(id string) lease.ClientConfig { 28 return lease.ClientConfig{ 29 Id: id, 30 Namespace: "ns", 31 Collection: "leases", 32 Mongo: NewMongo(s.db), 33 Clock: lease.SystemClock{}, 34 } 35 } 36 sutConfig := config("sut") 37 sutRunner := sutConfig.Mongo.(*Mongo).runner 38 39 // Set up a hook to create the clock doc (and write some important data to 40 // it) by creating another client before the SUT gets a chance. 41 defer txntesting.SetBeforeHooks(c, sutRunner, func() { 42 client, err := lease.NewClient(config("blocker")) 43 c.Check(err, jc.ErrorIsNil) 44 err = client.ClaimLease("somewhere", lease.Request{"someone", time.Minute}) 45 c.Check(err, jc.ErrorIsNil) 46 })() 47 48 // Create a client against an apparently-empty namespace. 49 client, err := lease.NewClient(sutConfig) 50 c.Check(err, jc.ErrorIsNil) 51 52 // Despite the scramble, it's generated with recent lease data and no error. 53 leases := client.Leases() 54 info, found := leases["somewhere"] 55 c.Check(found, jc.IsTrue) 56 c.Check(info.Holder, gc.Equals, "someone") 57 } 58 59 func (s *ClientSimpleRaceSuite) TestClaimLease_BlockedBy_ClaimLease(c *gc.C) { 60 sut := s.EasyFixture(c) 61 blocker := s.NewFixture(c, FixtureParams{Id: "blocker"}) 62 63 // Set up a hook to grab the lease "name" just before the next txn runs. 64 defer txntesting.SetBeforeHooks(c, sut.Runner, func() { 65 err := blocker.Client.ClaimLease("name", lease.Request{"ha-haa", time.Minute}) 66 c.Check(err, jc.ErrorIsNil) 67 })() 68 69 // Try to grab the lease "name", and fail. 70 err := sut.Client.ClaimLease("name", lease.Request{"trying", time.Second}) 71 c.Check(err, gc.Equals, lease.ErrInvalid) 72 73 // The client that failed has refreshed state (as it had to, in order 74 // to discover the reason for the invalidity). 75 c.Check("name", sut.Holder(), "ha-haa") 76 c.Check("name", sut.Expiry(), sut.Zero.Add(time.Minute)) 77 } 78 79 func (s *ClientSimpleRaceSuite) TestClaimLease_Pathological(c *gc.C) { 80 sut := s.EasyFixture(c) 81 blocker := s.NewFixture(c, FixtureParams{Id: "blocker"}) 82 83 // Set up hooks to claim a lease just before every transaction, but remove 84 // it again before the SUT goes and looks to figure out what it should do. 85 interfere := jujutxn.TestHook{ 86 Before: func() { 87 err := blocker.Client.ClaimLease("name", lease.Request{"ha-haa", time.Second}) 88 c.Check(err, jc.ErrorIsNil) 89 }, 90 After: func() { 91 blocker.Clock.Advance(time.Minute) 92 err := blocker.Client.ExpireLease("name") 93 c.Check(err, jc.ErrorIsNil) 94 }, 95 } 96 defer txntesting.SetTestHooks( 97 c, sut.Runner, 98 interfere, interfere, interfere, 99 )() 100 101 // Try to claim, and watch the poor thing collapse in exhaustion. 102 err := sut.Client.ClaimLease("name", lease.Request{"trying", time.Minute}) 103 c.Check(err, gc.ErrorMatches, "state changing too quickly; try again soon") 104 } 105 106 // ClientTrickyRaceSuite tests what happens when two clients interfere with 107 // each other when extending and/or expiring leases. 108 type ClientTrickyRaceSuite struct { 109 FixtureSuite 110 sut *Fixture 111 blocker *Fixture 112 } 113 114 var _ = gc.Suite(&ClientTrickyRaceSuite{}) 115 116 func (s *ClientTrickyRaceSuite) SetUpTest(c *gc.C) { 117 s.FixtureSuite.SetUpTest(c) 118 s.sut = s.EasyFixture(c) 119 err := s.sut.Client.ClaimLease("name", lease.Request{"holder", time.Minute}) 120 c.Assert(err, jc.ErrorIsNil) 121 s.blocker = s.NewFixture(c, FixtureParams{Id: "blocker"}) 122 } 123 124 func (s *ClientTrickyRaceSuite) TestExtendLease_WorksDespite_ShorterExtendLease(c *gc.C) { 125 126 shorterRequest := 90 * time.Second 127 longerRequest := 120 * time.Second 128 129 // Set up hooks to extend the lease by a little, before the SUT's extend 130 // gets a chance; and then to verify state after it's applied its retry. 131 defer txntesting.SetRetryHooks(c, s.sut.Runner, func() { 132 err := s.blocker.Client.ExtendLease("name", lease.Request{"holder", shorterRequest}) 133 c.Check(err, jc.ErrorIsNil) 134 }, func() { 135 err := s.blocker.Client.Refresh() 136 c.Check(err, jc.ErrorIsNil) 137 c.Check("name", s.blocker.Expiry(), s.blocker.Zero.Add(longerRequest)) 138 })() 139 140 // Extend the lease. 141 err := s.sut.Client.ExtendLease("name", lease.Request{"holder", longerRequest}) 142 c.Check(err, jc.ErrorIsNil) 143 } 144 145 func (s *ClientTrickyRaceSuite) TestExtendLease_WorksDespite_LongerExtendLease(c *gc.C) { 146 147 shorterRequest := 90 * time.Second 148 longerRequest := 120 * time.Second 149 150 // Set up hooks to extend the lease by a lot, before the SUT's extend can. 151 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 152 err := s.blocker.Client.ExtendLease("name", lease.Request{"holder", longerRequest}) 153 c.Check(err, jc.ErrorIsNil) 154 })() 155 156 // Extend the lease by a little. 157 err := s.sut.Client.ExtendLease("name", lease.Request{"holder", shorterRequest}) 158 c.Check(err, jc.ErrorIsNil) 159 160 // The SUT was refreshed, and knows that the lease is really valid for longer. 161 c.Check("name", s.sut.Expiry(), s.sut.Zero.Add(longerRequest)) 162 } 163 164 func (s *ClientTrickyRaceSuite) TestExtendLease_BlockedBy_ExpireLease(c *gc.C) { 165 166 // Set up a hook to expire the lease before the extend gets a chance. 167 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 168 s.blocker.Clock.Advance(90 * time.Second) 169 err := s.blocker.Client.ExpireLease("name") 170 c.Check(err, jc.ErrorIsNil) 171 })() 172 173 // Try to extend; check it aborts. 174 err := s.sut.Client.ExtendLease("name", lease.Request{"holder", 2 * time.Minute}) 175 c.Check(err, gc.Equals, lease.ErrInvalid) 176 177 // The SUT has been refreshed, and you can see why the operation was invalid. 178 c.Check("name", s.sut.Holder(), "") 179 } 180 181 func (s *ClientTrickyRaceSuite) TestExtendLease_BlockedBy_ExpireThenReclaimDifferentHolder(c *gc.C) { 182 183 // Set up a hook to expire and reclaim the lease before the extend gets a 184 // chance. 185 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 186 s.blocker.Clock.Advance(90 * time.Second) 187 err := s.blocker.Client.ExpireLease("name") 188 c.Check(err, jc.ErrorIsNil) 189 err = s.blocker.Client.ClaimLease("name", lease.Request{"different-holder", time.Minute}) 190 c.Check(err, jc.ErrorIsNil) 191 })() 192 193 // Try to extend; check it aborts. 194 err := s.sut.Client.ExtendLease("name", lease.Request{"holder", 2 * time.Minute}) 195 c.Check(err, gc.Equals, lease.ErrInvalid) 196 197 // The SUT has been refreshed, and you can see why the operation was invalid. 198 c.Check("name", s.sut.Holder(), "different-holder") 199 } 200 201 func (s *ClientTrickyRaceSuite) TestExtendLease_WorksDespite_ExpireThenReclaimSameHolder(c *gc.C) { 202 203 // Set up hooks to expire and reclaim the lease before the extend gets a 204 // chance; and to verify that the second attempt successfully extends. 205 defer txntesting.SetRetryHooks(c, s.sut.Runner, func() { 206 s.blocker.Clock.Advance(90 * time.Second) 207 err := s.blocker.Client.ExpireLease("name") 208 c.Check(err, jc.ErrorIsNil) 209 err = s.blocker.Client.ClaimLease("name", lease.Request{"holder", time.Minute}) 210 c.Check(err, jc.ErrorIsNil) 211 }, func() { 212 err := s.blocker.Client.Refresh() 213 c.Check(err, jc.ErrorIsNil) 214 c.Check("name", s.blocker.Expiry(), s.blocker.Zero.Add(5*time.Minute)) 215 })() 216 217 // Try to extend; check it worked. 218 err := s.sut.Client.ExtendLease("name", lease.Request{"holder", 5 * time.Minute}) 219 c.Check(err, jc.ErrorIsNil) 220 } 221 222 func (s *ClientTrickyRaceSuite) TestExtendLease_Pathological(c *gc.C) { 223 224 // Set up hooks to remove the lease just before every transaction, but 225 // replace it before the SUT goes and looks to figure out what it should do. 226 interfere := jujutxn.TestHook{ 227 Before: func() { 228 s.blocker.Clock.Advance(time.Minute + time.Second) 229 err := s.blocker.Client.ExpireLease("name") 230 c.Check(err, jc.ErrorIsNil) 231 }, 232 After: func() { 233 err := s.blocker.Client.ClaimLease("name", lease.Request{"holder", time.Second}) 234 c.Check(err, jc.ErrorIsNil) 235 }, 236 } 237 defer txntesting.SetTestHooks( 238 c, s.sut.Runner, 239 interfere, interfere, interfere, 240 )() 241 242 // Try to extend, and watch the poor thing collapse in exhaustion. 243 err := s.sut.Client.ExtendLease("name", lease.Request{"holder", time.Minute}) 244 c.Check(err, gc.ErrorMatches, "state changing too quickly; try again soon") 245 } 246 247 func (s *ClientTrickyRaceSuite) TestExpireLease_BlockedBy_ExtendLease(c *gc.C) { 248 249 // Set up a hook to extend the lease before the expire gets a chance. 250 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 251 s.blocker.Clock.Advance(90 * time.Second) 252 err := s.blocker.Client.ExtendLease("name", lease.Request{"holder", 30 * time.Second}) 253 c.Check(err, jc.ErrorIsNil) 254 })() 255 256 // Try to expire; check it aborts. 257 s.sut.Clock.Advance(90 * time.Second) 258 err := s.sut.Client.ExpireLease("name") 259 c.Check(err, gc.Equals, lease.ErrInvalid) 260 261 // The SUT has been refreshed, and you can see why the operation was invalid. 262 c.Check("name", s.sut.Expiry(), s.sut.Zero.Add(2*time.Minute)) 263 } 264 265 func (s *ClientTrickyRaceSuite) TestExpireLease_BlockedBy_ExpireLease(c *gc.C) { 266 267 // Set up a hook to expire the lease before the SUT gets a chance. 268 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 269 s.blocker.Clock.Advance(90 * time.Second) 270 err := s.blocker.Client.ExpireLease("name") 271 c.Check(err, jc.ErrorIsNil) 272 })() 273 274 // Try to expire; check it aborts. 275 s.sut.Clock.Advance(90 * time.Second) 276 err := s.sut.Client.ExpireLease("name") 277 c.Check(err, gc.Equals, lease.ErrInvalid) 278 279 // The SUT has been refreshed, and you can see why the operation was invalid. 280 c.Check("name", s.sut.Holder(), "") 281 } 282 283 func (s *ClientTrickyRaceSuite) TestExpireLease_BlockedBy_ExpireThenReclaim(c *gc.C) { 284 285 // Set up a hook to expire the lease and then reclaim it. 286 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 287 s.blocker.Clock.Advance(90 * time.Second) 288 err := s.blocker.Client.ExpireLease("name") 289 c.Check(err, jc.ErrorIsNil) 290 err = s.blocker.Client.ClaimLease("name", lease.Request{"holder", time.Minute}) 291 c.Check(err, jc.ErrorIsNil) 292 })() 293 294 // Try to expire; check it aborts. 295 s.sut.Clock.Advance(90 * time.Second) 296 err := s.sut.Client.ExpireLease("name") 297 c.Check(err, gc.Equals, lease.ErrInvalid) 298 299 // The SUT has been refreshed, and you can see why the operation was invalid. 300 c.Check("name", s.sut.Expiry(), s.sut.Zero.Add(150*time.Second)) 301 }