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  }