github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/sequence_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"sort"
     8  
     9  	"github.com/juju/mgo/v3/bson"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/state"
    14  )
    15  
    16  var _ = gc.Suite(&sequenceSuite{})
    17  
    18  type sequenceSuite struct {
    19  	ConnSuite
    20  }
    21  
    22  func (s *sequenceSuite) TestSequence(c *gc.C) {
    23  	s.incAndCheck(c, s.State, "foo", 0)
    24  	s.checkDocCount(c, 1)
    25  	s.checkDoc(c, s.State.ModelUUID(), "foo", 1)
    26  
    27  	s.incAndCheck(c, s.State, "foo", 1)
    28  	s.checkDocCount(c, 1)
    29  	s.checkDoc(c, s.State.ModelUUID(), "foo", 2)
    30  }
    31  
    32  func (s *sequenceSuite) TestMultipleSequences(c *gc.C) {
    33  	s.incAndCheck(c, s.State, "foo", 0)
    34  	s.incAndCheck(c, s.State, "bar", 0)
    35  	s.incAndCheck(c, s.State, "bar", 1)
    36  	s.incAndCheck(c, s.State, "foo", 1)
    37  	s.incAndCheck(c, s.State, "bar", 2)
    38  
    39  	s.checkDocCount(c, 2)
    40  	s.checkDoc(c, s.State.ModelUUID(), "foo", 2)
    41  	s.checkDoc(c, s.State.ModelUUID(), "bar", 3)
    42  }
    43  
    44  func (s *sequenceSuite) TestSequenceWithMultipleModels(c *gc.C) {
    45  	state1 := s.State
    46  	state2 := s.Factory.MakeModel(c, nil)
    47  	defer state2.Close()
    48  
    49  	s.incAndCheck(c, state1, "foo", 0)
    50  	s.incAndCheck(c, state2, "foo", 0)
    51  	s.incAndCheck(c, state1, "foo", 1)
    52  	s.incAndCheck(c, state2, "foo", 1)
    53  	s.incAndCheck(c, state1, "foo", 2)
    54  
    55  	s.checkDocCount(c, 2)
    56  	s.checkDoc(c, state1.ModelUUID(), "foo", 3)
    57  	s.checkDoc(c, state2.ModelUUID(), "foo", 2)
    58  }
    59  
    60  func (s *sequenceSuite) TestSequences(c *gc.C) {
    61  	state1 := s.State
    62  	state2 := s.Factory.MakeModel(c, nil)
    63  	defer state2.Close()
    64  
    65  	s.incAndCheck(c, state1, "foo", 0)
    66  	s.incAndCheck(c, state2, "foo", 0)
    67  	s.incAndCheck(c, state1, "foo", 1)
    68  	s.incAndCheck(c, state2, "foo", 1)
    69  	s.incAndCheck(c, state1, "foo", 2)
    70  	s.incAndCheck(c, state1, "bar", 0)
    71  	s.incAndCheck(c, state2, "baz", 0)
    72  
    73  	sequences, err := state1.Sequences()
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	c.Assert(sequences, jc.DeepEquals, map[string]int{
    76  		"foo": 3, "bar": 1,
    77  	})
    78  }
    79  
    80  func (s *sequenceSuite) TestSequenceWithMin(c *gc.C) {
    81  	st := s.State
    82  	modelUUID := st.ModelUUID()
    83  	const name = "foo"
    84  
    85  	value, err := state.SequenceWithMin(st, name, 3)
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	c.Check(value, gc.Equals, 3)
    88  	s.checkDoc(c, modelUUID, name, 4)
    89  
    90  	value, err = state.SequenceWithMin(st, name, 3)
    91  	c.Assert(err, jc.ErrorIsNil)
    92  	c.Check(value, gc.Equals, 4)
    93  	s.checkDoc(c, modelUUID, name, 5)
    94  
    95  	value, err = state.SequenceWithMin(st, name, 3)
    96  	c.Assert(err, jc.ErrorIsNil)
    97  	c.Check(value, gc.Equals, 5)
    98  	s.checkDoc(c, modelUUID, name, 6)
    99  
   100  	value, err = state.SequenceWithMin(st, name, 10)
   101  	c.Assert(err, jc.ErrorIsNil)
   102  	c.Check(value, gc.Equals, 10)
   103  	s.checkDoc(c, modelUUID, name, 11)
   104  }
   105  
   106  func (s *sequenceSuite) TestMultipleSequenceWithMin(c *gc.C) {
   107  	st := s.State
   108  
   109  	value, err := state.SequenceWithMin(st, "foo", 3)
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	c.Check(value, gc.Equals, 3)
   112  
   113  	value, err = state.SequenceWithMin(st, "bar", 2)
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	c.Check(value, gc.Equals, 2)
   116  
   117  	value, err = state.SequenceWithMin(st, "foo", 3)
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	c.Check(value, gc.Equals, 4)
   120  
   121  	value, err = state.SequenceWithMin(st, "bar", 2)
   122  	c.Assert(err, jc.ErrorIsNil)
   123  	c.Check(value, gc.Equals, 3)
   124  }
   125  
   126  func (s *sequenceSuite) TestMigrationSequenceWithMin(c *gc.C) {
   127  	// Test local charm migration where incoming charms may have
   128  	// the same curl with non-sequential revisions.
   129  	st := s.State
   130  
   131  	value, err := state.SequenceWithMin(st, "foo", 0)
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	c.Check(value, gc.Equals, 0)
   134  
   135  	value, err = state.SequenceWithMin(st, "foo", 2)
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	c.Check(value, gc.Equals, 2)
   138  
   139  	found, err := state.Sequence(st, "foo")
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	c.Check(found, gc.Equals, 3)
   142  }
   143  
   144  func (s *sequenceSuite) TestContention(c *gc.C) {
   145  	const name = "foo"
   146  	const goroutines = 2
   147  	const iterations = 10
   148  	st := s.State
   149  
   150  	type results struct {
   151  		values  []int
   152  		numErrs int
   153  	}
   154  	resultsCh := make(chan results)
   155  
   156  	workFunc := func(nextSeq func() (int, error)) {
   157  		var r results
   158  		for i := 0; i < iterations; i++ {
   159  			v, err := nextSeq()
   160  			if err != nil {
   161  				c.Logf("sequence increment failed: %v", err)
   162  				r.numErrs++
   163  			}
   164  			r.values = append(r.values, v)
   165  		}
   166  		resultsCh <- r
   167  	}
   168  
   169  	go workFunc(func() (int, error) {
   170  		return state.Sequence(st, name)
   171  	})
   172  
   173  	go workFunc(func() (int, error) {
   174  		return state.SequenceWithMin(st, name, 0)
   175  	})
   176  
   177  	var seenValues sort.IntSlice
   178  	var seenErrs int
   179  	for i := 0; i < goroutines; i++ {
   180  		r := <-resultsCh
   181  		seenValues = append(seenValues, r.values...)
   182  		seenErrs += r.numErrs
   183  	}
   184  	c.Assert(seenErrs, gc.Equals, 0)
   185  
   186  	numExpected := goroutines * iterations
   187  	c.Assert(len(seenValues), gc.Equals, numExpected)
   188  	seenValues.Sort()
   189  	for i := 0; i < numExpected; i++ {
   190  		c.Assert(seenValues[i], gc.Equals, i, gc.Commentf("index %d", i))
   191  	}
   192  }
   193  
   194  func (s *sequenceSuite) TestEnsureCreate(c *gc.C) {
   195  	err := state.SequenceEnsure(s.State, "foo", 3)
   196  	c.Assert(err, jc.ErrorIsNil)
   197  	s.incAndCheck(c, s.State, "foo", 3)
   198  }
   199  
   200  func (s *sequenceSuite) TestEnsureSet(c *gc.C) {
   201  	s.incAndCheck(c, s.State, "foo", 0)
   202  	err := state.SequenceEnsure(s.State, "foo", 5)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	s.incAndCheck(c, s.State, "foo", 5)
   205  }
   206  
   207  func (s *sequenceSuite) TestEnsureBackwards(c *gc.C) {
   208  	s.incAndCheck(c, s.State, "foo", 0)
   209  	s.incAndCheck(c, s.State, "foo", 1)
   210  	s.incAndCheck(c, s.State, "foo", 2)
   211  
   212  	err := state.SequenceEnsure(s.State, "foo", 1)
   213  	c.Assert(err, jc.ErrorIsNil)
   214  
   215  	s.incAndCheck(c, s.State, "foo", 3)
   216  }
   217  
   218  func (s *sequenceSuite) TestResetSequence(c *gc.C) {
   219  	err := state.SequenceEnsure(s.State, "foo", 3)
   220  	c.Assert(err, jc.ErrorIsNil)
   221  	s.incAndCheck(c, s.State, "foo", 3)
   222  
   223  	err = state.ResetSequence(s.State, "foo")
   224  	c.Assert(err, jc.ErrorIsNil)
   225  
   226  	s.incAndCheck(c, s.State, "foo", 0)
   227  }
   228  func (s *sequenceSuite) incAndCheck(c *gc.C, st *state.State, name string, expectedCount int) {
   229  	value, err := state.Sequence(st, name)
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	c.Check(value, gc.Equals, expectedCount)
   232  }
   233  
   234  func (s *sequenceSuite) checkDocCount(c *gc.C, expectedCount int) {
   235  	coll, closer := state.GetRawCollection(s.State, "sequence")
   236  	defer closer()
   237  	count, err := coll.Count()
   238  	c.Assert(err, jc.ErrorIsNil)
   239  	c.Check(count, gc.Equals, expectedCount)
   240  }
   241  
   242  func (s *sequenceSuite) checkDoc(c *gc.C, modelUUID, name string, value int) {
   243  	coll, closer := state.GetRawCollection(s.State, "sequence")
   244  	defer closer()
   245  
   246  	docID := modelUUID + ":" + name
   247  	var doc bson.M
   248  	err := coll.FindId(docID).One(&doc)
   249  	c.Assert(err, jc.ErrorIsNil)
   250  	c.Check(doc["_id"], gc.Equals, docID)
   251  	c.Check(doc["name"], gc.Equals, name)
   252  	c.Check(doc["model-uuid"], gc.Equals, modelUUID)
   253  	c.Check(doc["counter"], gc.Equals, value)
   254  }