Description:
state: Make unit ids unique. Fix #1174610
This changes the way unit names are generated
when adding a new unit to a service. Before,
we used serviceDoc.UnitSeq to generate the
last part of a unit name. Now, we use the
sequence collection with a key "s#<svcname>#unit"
as the unit ids sequence (as we do for machine,
containers, etc.).
This is a schema change (removing of UnitSeq from
the service document). So it should be handled the
same way as any schema change during an upgrade,
but this CL does not do that.
I realized the sequence collection is not created
as all the other collections in state.Initialize,
so I added it there.
Also added a test to guarantee unit ids increase
each time for the same service name (like the bug
suggests and how it was before).
Per fwereade's suggestion, I changed the error
message AddUnit() returns when a service is not
found: "service is no longer alive" rather than
"service "blah" not found", and also fixed a
test that relied on that.
"labix.org/v2/mgo"
gc "launchpad.net/gocheck"
@@ -815,13 +816,13 @@
err = s.mysql.Destroy()
c.Assert(err, gc.IsNil)
_, err = s.mysql.AddUnit()
- c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql":
service is not alive`)
+ c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql":
service is no longer alive`)
err = u.EnsureDead()
c.Assert(err, gc.IsNil)
err = u.Remove()
c.Assert(err, gc.IsNil)
_, err = s.mysql.AddUnit()
- c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql":
service "mysql" not found`)
+ c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql":
service is no longer alive`)
}
func (s *ServiceSuite) TestReadUnit(c *gc.C) {
@@ -1429,3 +1430,24 @@
c.Assert(state.GetServiceOwnerTag(service), gc.Equals, "")
c.Assert(service.GetOwnerTag(), gc.Equals, "user-admin")
}
+
+func (s *ServiceSuite) TestUnitIdsAreUnique(c *gc.C) {
+ for i, charmName := range []string{"wordpress", "riak", "dummy"} {
+ // Add a new charm and service with the same name.
+ charm := s.AddTestingCharm(c, charmName)
+ service := s.AddTestingService(c, "myservice", charm)
+
+ // Add a unit and check the id increases each time.
+ unit, err := service.AddUnit()
+ c.Check(err, gc.IsNil)
+ c.Check(unit.Name(), gc.Equals, "myservice/"+strconv.Itoa(i))
+
+ // Destroy the unit and service and retry.
+ err = unit.Destroy()
+ c.Check(err, gc.IsNil)
+ c.Check(unit.Refresh(), jc.Satisfies, errors.IsNotFoundError)
+ err = service.Destroy()
+ c.Check(err, gc.IsNil)
+ c.Check(service.Refresh(), jc.Satisfies, errors.IsNotFoundError)
+ }
+}
Reviewers: mp+206765_ code.launchpad. net,
Message:
Please take a look.
Description:
state: Make unit ids unique. Fix #1174610
This changes the way unit names are generated
when adding a new unit to a service. Before,
we used serviceDoc.UnitSeq to generate the
last part of a unit name. Now, we use the
sequence collection with a key "s#<svcname>#unit"
as the unit ids sequence (as we do for machine,
containers, etc.).
This is a schema change (removing of UnitSeq from
the service document). So it should be handled the
same way as any schema change during an upgrade,
but this CL does not do that.
I realized the sequence collection is not created
as all the other collections in state.Initialize,
so I added it there.
Also added a test to guarantee unit ids increase
each time for the same service name (like the bug
suggests and how it was before).
Per fwereade's suggestion, I changed the error
message AddUnit() returns when a service is not
found: "service is no longer alive" rather than
"service "blah" not found", and also fixed a
test that relied on that.
https:/ /code.launchpad .net/~dimitern/ juju-core/ 300-lp- 1174610- unit-ids- unique/ +merge/ 206765
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/64890044/
Affected files (+35, -14 lines): test.go
A [revision details]
M state/open.go
M state/sequence.go
M state/service.go
M state/service_
M state/state.go
Index: [revision details] 20140217133244- ukuzvegy1wqly6n m
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: tarmac-
+New revision:
<email address hidden>
Index: state/open.go ns"), ers"), nfo{Capped: true, MaxBytes: logSize}
=== modified file 'state/open.go'
--- state/open.go 2014-02-07 18:38:02 +0000
+++ state/open.go 2014-02-17 16:32:56 +0000
@@ -255,6 +255,7 @@
annotations: db.C("annotatio
statuses: db.C("statuses"),
stateServers: db.C("stateServ
+ sequences: db.C("sequence"),
}
log := db.C("txns.log")
logInfo := mgo.CollectionI
Index: state/sequence.go
=== modified file 'state/sequence.go'
--- state/sequence.go 2013-07-09 10:32:23 +0000
+++ state/sequence.go 2014-02-17 16:32:56 +0000
@@ -16,7 +16,7 @@
}
func (s *State) sequence(name string) (int, error) { "sequence" ).Find( D{{"_id" , name}}) FindId( name)
- query := s.db.C(
+ query := s.sequences.
inc := mgo.Change{
Update: bson.M{"$inc": bson.M{"counter": 1}},
Upsert: true,
Index: state/service.go
=== modified file 'state/service.go'
--- state/service.go 2014-01-15 06:52:56 +0000
+++ state/service.go 2014-02-17 16:32:56 +0000
@@ -37,7 +37,6 @@
CharmURL *charm.URL
ForceCharm bool
Life Life
- UnitSeq int
UnitCount int
RelationCount int
Exposed bool
@@ -529,15 +528,11 @@
// newUnitName returns the next unit name. Find(D{ {"_id", s.doc.Name} }).Apply( change, NotFoundf( "service %q", s) s.globalKey( ) + "#unit") Itoa(result. UnitSeq)
func (s *Service) newUnitName() (string, error) {
- change := mgo.Change{Update: D{{"$inc", D{{"unitseq", 1}}}}}
- result := serviceDoc{}
- if _, err := s.st.services.
&result); err == mgo.ErrNotFound {
- return "", errors.
- } else if err != nil {
- return "", fmt.Errorf("cannot increment unit sequence: %v", err)
+ seq, err := s.st.sequence(
+ if err != nil {
+ return "", err
}
- name := s.doc.Name + "/" + strconv.
- return name, nil
+ return s.doc.Name + "/" + strconv.Itoa(seq), nil
}
// addUnitOps returns a unique name for a new unit, and a list of txn IsNotFoundError (err) { straints( ) s.st.services, s.doc.Name); err != nil { "inconsistent state")
operations
@@ -591,7 +586,7 @@
})
} else {
scons, err := s.Constraints()
- if err != nil {
+ if err != nil && !errors.
return "", nil, err
}
econs, err := s.st.EnvironCon
@@ -631,7 +626,7 @@
if alive, err := isAlive(
return nil, err
} else if !alive {
- return nil, fmt.Errorf("service is not alive")
+ return nil, fmt.Errorf("service is no longer alive")
}
return nil, fmt.Errorf(
} else if err != nil {
Index: state/service_ test.go service_ test.go' test.go 2014-01-15 06:52:56 +0000 test.go 2014-02-17 16:32:56 +0000
=== modified file 'state/
--- state/service_
+++ state/service_
@@ -6,6 +6,7 @@
import (
"fmt"
"sort"
+ "strconv"
"labix. org/v2/ mgo" net/gocheck"
gc "launchpad.
@@ -815,13 +816,13 @@
err = s.mysql.Destroy()
c.Assert(err, gc.IsNil)
_, err = s.mysql.AddUnit()
- c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql":
service is not alive`)
+ c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql":
service is no longer alive`)
err = u.EnsureDead()
c.Assert(err, gc.IsNil)
err = u.Remove()
c.Assert(err, gc.IsNil)
_, err = s.mysql.AddUnit()
- c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql":
service "mysql" not found`)
+ c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql":
service is no longer alive`)
}
func (s *ServiceSuite) TestReadUnit(c *gc.C) { state.GetServic eOwnerTag( service) , gc.Equals, "") service. GetOwnerTag( ), gc.Equals, "user-admin") nique(c *gc.C) { "wordpress" , "riak", "dummy"} { rm(c, charmName) vice(c, "myservice", charm) unit.Name( ), gc.Equals, "myservice/ "+strconv. Itoa(i) ) unit.Refresh( ), jc.Satisfies, errors. IsNotFoundError ) service. Refresh( ), jc.Satisfies, errors. IsNotFoundError )
@@ -1429,3 +1430,24 @@
c.Assert(
c.Assert(
}
+
+func (s *ServiceSuite) TestUnitIdsAreU
+ for i, charmName := range []string{
+ // Add a new charm and service with the same name.
+ charm := s.AddTestingCha
+ service := s.AddTestingSer
+
+ // Add a unit and check the id increases each time.
+ unit, err := service.AddUnit()
+ c.Check(err, gc.IsNil)
+ c.Check(
+
+ // Destroy the unit and service and retry.
+ err = unit.Destroy()
+ c.Check(err, gc.IsNil)
+ c.Check(
+ err = service.Destroy()
+ c.Check(err, gc.IsNil)
+ c.Check(
+ }
+}
Index: state/state.go
=== modified file 'state/state.go'
--- state/state.go 2014-02-05 17:46:02 +0000
+++ state/state.go 2014-02-17 16:32:56 +0000
@@ -64,6 +64,7 @@
annotations *mgo.Collection
statuses *mgo.Collection
stateServers *mgo.Collection
+ sequences *mgo.Collection
runner *txn.Runner
transactionHooks chan ([]transactionHook)
watcher *watcher.Watcher