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  }