github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/lease/store_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" // Only used for time types. 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 14 corelease "github.com/juju/juju/core/lease" 15 ) 16 17 // StoreSimpleRaceSuite tests what happens when two stores interfere with 18 // each other when creating stores and/or leases. 19 type StoreSimpleRaceSuite struct { 20 FixtureSuite 21 } 22 23 var _ = gc.Suite(&StoreSimpleRaceSuite{}) 24 25 func (s *StoreSimpleRaceSuite) TestClaimLease_BlockedBy_ClaimLease(c *gc.C) { 26 sut := s.EasyFixture(c) 27 blocker := s.NewFixture(c, FixtureParams{Id: "blocker"}) 28 29 // Set up a hook to grab the lease "name" just before the next txn runs. 30 defer txntesting.SetBeforeHooks(c, sut.Runner, func() { 31 err := blocker.Store.ClaimLease(key("name"), corelease.Request{"ha-haa", time.Minute}) 32 c.Check(err, jc.ErrorIsNil) 33 })() 34 35 // Try to grab the lease "name", and fail. 36 err := sut.Store.ClaimLease(key("name"), corelease.Request{"trying", time.Second}) 37 c.Check(err, gc.Equals, corelease.ErrInvalid) 38 39 // The store that failed has refreshed state (as it had to, in order 40 // to discover the reason for the invalidity). 41 c.Check(key("name"), sut.Holder(), "ha-haa") 42 c.Check(key("name"), sut.Expiry(), sut.Zero.Add(time.Minute)) 43 } 44 45 func (s *StoreSimpleRaceSuite) TestClaimLease_Pathological(c *gc.C) { 46 sut := s.EasyFixture(c) 47 blocker := s.NewFixture(c, FixtureParams{Id: "blocker"}) 48 49 // Set up hooks to claim a lease just before every transaction, but remove 50 // it again before the SUT goes and looks to figure out what it should do. 51 interfere := jujutxn.TestHook{ 52 Before: func() { 53 err := blocker.Store.ClaimLease(key("name"), corelease.Request{"ha-haa", time.Second}) 54 c.Check(err, jc.ErrorIsNil) 55 }, 56 After: func() { 57 blocker.GlobalClock.Advance(time.Minute) 58 err := blocker.Store.ExpireLease(key("name")) 59 c.Check(err, jc.ErrorIsNil) 60 }, 61 } 62 defer txntesting.SetTestHooks( 63 c, sut.Runner, 64 interfere, interfere, interfere, 65 )() 66 67 // Try to claim, and watch the poor thing collapse in exhaustion. 68 err := sut.Store.ClaimLease(key("name"), corelease.Request{"trying", time.Minute}) 69 c.Check(err, gc.ErrorMatches, "cannot satisfy request: state changing too quickly; try again soon") 70 } 71 72 // StoreTrickyRaceSuite tests what happens when two stores interfere with 73 // each other when extending and/or expiring leases. 74 type StoreTrickyRaceSuite struct { 75 FixtureSuite 76 sut *Fixture 77 blocker *Fixture 78 } 79 80 var _ = gc.Suite(&StoreTrickyRaceSuite{}) 81 82 func (s *StoreTrickyRaceSuite) SetUpTest(c *gc.C) { 83 s.FixtureSuite.SetUpTest(c) 84 s.sut = s.EasyFixture(c) 85 err := s.sut.Store.ClaimLease(key("name"), corelease.Request{"holder", time.Minute}) 86 c.Assert(err, jc.ErrorIsNil) 87 s.blocker = s.NewFixture(c, FixtureParams{Id: "blocker"}) 88 } 89 90 func (s *StoreTrickyRaceSuite) TestExtendLease_WorksDespite_ShorterExtendLease(c *gc.C) { 91 92 shorterRequest := 90 * time.Second 93 longerRequest := 120 * time.Second 94 95 // Set up hooks to extend the lease by a little, before the SUT's extend 96 // gets a chance; and then to verify state after it's applied its retry. 97 defer txntesting.SetRetryHooks(c, s.sut.Runner, func() { 98 err := s.blocker.Store.ExtendLease(key("name"), corelease.Request{"holder", shorterRequest}) 99 c.Check(err, jc.ErrorIsNil) 100 }, func() { 101 err := s.blocker.Store.Refresh() 102 c.Check(err, jc.ErrorIsNil) 103 c.Check(key("name"), s.blocker.Expiry(), s.blocker.Zero.Add(longerRequest)) 104 })() 105 106 // Extend the lease. 107 err := s.sut.Store.ExtendLease(key("name"), corelease.Request{"holder", longerRequest}) 108 c.Check(err, jc.ErrorIsNil) 109 } 110 111 func (s *StoreTrickyRaceSuite) TestExtendLease_WorksDespite_LongerExtendLease(c *gc.C) { 112 113 shorterRequest := 90 * time.Second 114 longerRequest := 120 * time.Second 115 116 // Set up hooks to extend the lease by a lot, before the SUT's extend can. 117 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 118 err := s.blocker.Store.ExtendLease(key("name"), corelease.Request{"holder", longerRequest}) 119 c.Check(err, jc.ErrorIsNil) 120 })() 121 122 // Extend the lease by a little. 123 err := s.sut.Store.ExtendLease(key("name"), corelease.Request{"holder", shorterRequest}) 124 c.Check(err, jc.ErrorIsNil) 125 126 // The SUT was refreshed, and knows that the lease is really valid for longer. 127 c.Check(key("name"), s.sut.Expiry(), s.sut.Zero.Add(longerRequest)) 128 } 129 130 func (s *StoreTrickyRaceSuite) TestExtendLease_BlockedBy_ExpireLease(c *gc.C) { 131 132 // Set up a hook to expire the lease before the extend gets a chance. 133 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 134 s.blocker.GlobalClock.Advance(90 * time.Second) 135 err := s.blocker.Store.ExpireLease(key("name")) 136 c.Check(err, jc.ErrorIsNil) 137 })() 138 139 // Try to extend; check it aborts. 140 err := s.sut.Store.ExtendLease(key("name"), corelease.Request{"holder", 2 * time.Minute}) 141 c.Check(err, gc.Equals, corelease.ErrInvalid) 142 143 // The SUT has been refreshed, and you can see why the operation was invalid. 144 c.Check(key("name"), s.sut.Holder(), "") 145 } 146 147 func (s *StoreTrickyRaceSuite) TestExtendLease_BlockedBy_ExpireThenReclaimDifferentHolder(c *gc.C) { 148 149 // Set up a hook to expire and reclaim the lease before the extend gets a 150 // chance. 151 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 152 s.blocker.GlobalClock.Advance(90 * time.Second) 153 err := s.blocker.Store.ExpireLease(key("name")) 154 c.Check(err, jc.ErrorIsNil) 155 err = s.blocker.Store.ClaimLease(key("name"), corelease.Request{"different-holder", time.Minute}) 156 c.Check(err, jc.ErrorIsNil) 157 })() 158 159 // Try to extend; check it aborts. 160 err := s.sut.Store.ExtendLease(key("name"), corelease.Request{"holder", 2 * time.Minute}) 161 c.Check(err, gc.Equals, corelease.ErrInvalid) 162 163 // The SUT has been refreshed, and you can see why the operation was invalid. 164 c.Check(key("name"), s.sut.Holder(), "different-holder") 165 } 166 167 func (s *StoreTrickyRaceSuite) TestExtendLease_WorksDespite_ExpireThenReclaimSameHolder(c *gc.C) { 168 169 // Set up hooks to expire and reclaim the lease before the extend gets a 170 // chance; and to verify that the second attempt successfully extends. 171 defer txntesting.SetRetryHooks(c, s.sut.Runner, func() { 172 s.blocker.GlobalClock.Advance(90 * time.Second) 173 s.blocker.LocalClock.Advance(90 * time.Second) 174 err := s.blocker.Store.ExpireLease(key("name")) 175 c.Check(err, jc.ErrorIsNil) 176 err = s.blocker.Store.ClaimLease(key("name"), corelease.Request{"holder", time.Minute}) 177 c.Check(err, jc.ErrorIsNil) 178 }, func() { 179 err := s.blocker.Store.Refresh() 180 c.Check(err, jc.ErrorIsNil) 181 c.Check(key("name"), s.blocker.Expiry(), s.blocker.Zero.Add(5*time.Minute)) 182 })() 183 184 // Try to extend; check it worked. 185 err := s.sut.Store.ExtendLease(key("name"), corelease.Request{"holder", 5 * time.Minute}) 186 c.Check(err, jc.ErrorIsNil) 187 } 188 189 func (s *StoreTrickyRaceSuite) TestExtendLease_Pathological(c *gc.C) { 190 191 // Set up hooks to remove the lease just before every transaction, but 192 // replace it before the SUT goes and looks to figure out what it should do. 193 interfere := jujutxn.TestHook{ 194 Before: func() { 195 s.blocker.GlobalClock.Advance(time.Minute + time.Second) 196 s.blocker.LocalClock.Advance(time.Minute + time.Second) 197 err := s.blocker.Store.ExpireLease(key("name")) 198 c.Check(err, jc.ErrorIsNil) 199 }, 200 After: func() { 201 err := s.blocker.Store.ClaimLease(key("name"), corelease.Request{"holder", time.Second}) 202 c.Check(err, jc.ErrorIsNil) 203 }, 204 } 205 defer txntesting.SetTestHooks( 206 c, s.sut.Runner, 207 interfere, interfere, interfere, 208 )() 209 210 // Try to extend, and watch the poor thing collapse in exhaustion. 211 err := s.sut.Store.ExtendLease(key("name"), corelease.Request{"holder", 3 * time.Minute}) 212 c.Check(err, gc.ErrorMatches, "cannot satisfy request: state changing too quickly; try again soon") 213 } 214 215 func (s *StoreTrickyRaceSuite) TestExpireLease_BlockedBy_ExtendLease(c *gc.C) { 216 217 // Set up a hook to extend the lease before the expire gets a chance. 218 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 219 s.blocker.GlobalClock.Advance(90 * time.Second) 220 s.blocker.LocalClock.Advance(90 * time.Second) 221 err := s.blocker.Store.ExtendLease(key("name"), corelease.Request{"holder", 30 * time.Second}) 222 c.Check(err, jc.ErrorIsNil) 223 })() 224 225 // Try to expire; check it aborts. 226 s.sut.GlobalClock.Advance(90 * time.Second) 227 err := s.sut.Store.ExpireLease(key("name")) 228 c.Check(err, gc.Equals, corelease.ErrInvalid) 229 230 // The SUT has been refreshed, and you can see why the operation was invalid. 231 s.sut.LocalClock.Advance(90 * time.Second) 232 c.Check(key("name"), s.sut.Expiry(), s.sut.Zero.Add(2*time.Minute)) 233 } 234 235 func (s *StoreTrickyRaceSuite) TestExpireLease_BlockedBy_ExpireLease(c *gc.C) { 236 237 // Set up a hook to expire the lease before the SUT gets a chance. 238 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 239 s.blocker.GlobalClock.Advance(90 * time.Second) 240 err := s.blocker.Store.ExpireLease(key("name")) 241 c.Check(err, jc.ErrorIsNil) 242 })() 243 244 // Try to expire; check it aborts. 245 s.sut.GlobalClock.Advance(90 * time.Second) 246 err := s.sut.Store.ExpireLease(key("name")) 247 c.Check(err, gc.Equals, corelease.ErrInvalid) 248 249 // The SUT has been refreshed, and you can see why the operation was invalid. 250 c.Check(key("name"), s.sut.Holder(), "") 251 } 252 253 func (s *StoreTrickyRaceSuite) TestExpireLease_BlockedBy_ExpireThenReclaim(c *gc.C) { 254 255 // Set up a hook to expire the lease and then reclaim it. 256 defer txntesting.SetBeforeHooks(c, s.sut.Runner, func() { 257 s.blocker.GlobalClock.Advance(90 * time.Second) 258 err := s.blocker.Store.ExpireLease(key("name")) 259 c.Check(err, jc.ErrorIsNil) 260 err = s.blocker.Store.ClaimLease(key("name"), corelease.Request{"holder", time.Minute}) 261 c.Check(err, jc.ErrorIsNil) 262 })() 263 264 // Try to expire; check it aborts. 265 s.sut.GlobalClock.Advance(90 * time.Second) 266 err := s.sut.Store.ExpireLease(key("name")) 267 c.Check(err, gc.Equals, corelease.ErrInvalid) 268 269 // The SUT has been refreshed, and you can see why the operation was invalid. 270 s.sut.LocalClock.Advance(90 * time.Second) 271 c.Check(key("name"), s.sut.Expiry(), s.sut.Zero.Add(150*time.Second)) 272 }