converger: Update the API for errors and naming

We updated the API and behaviour to make two changes:

1) We remove the log.* stuff, and replace the Fatal calls with straight
calls to panic(). These are meant to be like an assert, and shouldn't
happen unless there is a user error or a bug.

2) We made the !uuid.IsValid() checks return an error instead of causing
a panic. These returns errors instead, and makes the process safer for
users who are okay calling a function that won't have an effect.
This commit is contained in:
James Shubin
2016-08-30 03:44:02 -04:00
parent 6bf32c978a
commit f5fb135793
2 changed files with 34 additions and 17 deletions

View File

@@ -18,7 +18,7 @@
package main package main
import ( import (
"log" "fmt"
"sync" "sync"
"time" "time"
) )
@@ -27,7 +27,7 @@ import (
type Converger interface { // TODO: need a better name type Converger interface { // TODO: need a better name
Register() ConvergerUUID Register() ConvergerUUID
IsConverged(ConvergerUUID) bool // is the UUID converged ? IsConverged(ConvergerUUID) bool // is the UUID converged ?
SetConverged(ConvergerUUID, bool) // set the converged state of the UUID SetConverged(ConvergerUUID, bool) error // set the converged state of the UUID
Unregister(ConvergerUUID) Unregister(ConvergerUUID)
Start() Start()
Pause() Pause()
@@ -39,10 +39,12 @@ type Converger interface { // TODO: need a better name
// you'll need to use part of the Converger interface to Register initially too // you'll need to use part of the Converger interface to Register initially too
type ConvergerUUID interface { type ConvergerUUID interface {
ID() uint64 // get Id ID() uint64 // get Id
Name() string // get a friendly name
SetName(string)
IsValid() bool // has Id been initialized ? IsValid() bool // has Id been initialized ?
InvalidateID() // set Id to nil InvalidateID() // set Id to nil
IsConverged() bool IsConverged() bool
SetConverged(bool) SetConverged(bool) error
Unregister() Unregister()
ConvergedTimer() <-chan time.Time ConvergedTimer() <-chan time.Time
} }
@@ -62,6 +64,7 @@ type converger struct {
type convergerUUID struct { type convergerUUID struct {
converger Converger converger Converger
id uint64 id uint64
name string // user defined, friendly name
} }
// NewConverger builds a new converger struct // NewConverger builds a new converger struct
@@ -85,31 +88,32 @@ func (obj *converger) Register() ConvergerUUID {
return &convergerUUID{ return &convergerUUID{
converger: obj, converger: obj,
id: obj.lastid, id: obj.lastid,
name: fmt.Sprintf("%d", obj.lastid), // some default
} }
} }
// IsConverged gets the converged status of a uuid // IsConverged gets the converged status of a uuid
func (obj *converger) IsConverged(uuid ConvergerUUID) bool { func (obj *converger) IsConverged(uuid ConvergerUUID) bool {
if !uuid.IsValid() { if !uuid.IsValid() {
log.Fatal("Id of ConvergerUUID is nil!") panic(fmt.Sprintf("Id of ConvergerUUID(%s) is nil!", uuid.Name()))
} }
obj.mutex.RLock() obj.mutex.RLock()
isConverged, found := obj.status[uuid.ID()] // lookup isConverged, found := obj.status[uuid.ID()] // lookup
obj.mutex.RUnlock() obj.mutex.RUnlock()
if !found { if !found {
log.Fatal("Id of ConvergerUUID is unregistered!") panic("Id of ConvergerUUID is unregistered!")
} }
return isConverged return isConverged
} }
// SetConverged updates the converger with the converged state of the UUID // SetConverged updates the converger with the converged state of the UUID
func (obj *converger) SetConverged(uuid ConvergerUUID, isConverged bool) { func (obj *converger) SetConverged(uuid ConvergerUUID, isConverged bool) error {
if !uuid.IsValid() { if !uuid.IsValid() {
log.Fatal("Id of ConvergerUUID is nil!") return fmt.Errorf("Id of ConvergerUUID(%s) is nil!", uuid.Name())
} }
obj.mutex.Lock() obj.mutex.Lock()
if _, found := obj.status[uuid.ID()]; !found { if _, found := obj.status[uuid.ID()]; !found {
log.Fatal("Id of ConvergerUUID is unregistered!") panic("Id of ConvergerUUID is unregistered!")
} }
obj.status[uuid.ID()] = isConverged // set obj.status[uuid.ID()] = isConverged // set
obj.mutex.Unlock() // unlock *before* poke or deadlock! obj.mutex.Unlock() // unlock *before* poke or deadlock!
@@ -118,6 +122,7 @@ func (obj *converger) SetConverged(uuid ConvergerUUID, isConverged bool) {
// this allows us to send events, even if we haven't started... // this allows us to send events, even if we haven't started...
go func() { obj.channel <- struct{}{} }() go func() { obj.channel <- struct{}{} }()
} }
return nil
} }
// isConverged returns true if *every* registered uuid has converged // isConverged returns true if *every* registered uuid has converged
@@ -135,7 +140,7 @@ func (obj *converger) isConverged() bool {
// Unregister dissociates the ConvergedUUID from the converged checking // Unregister dissociates the ConvergedUUID from the converged checking
func (obj *converger) Unregister(uuid ConvergerUUID) { func (obj *converger) Unregister(uuid ConvergerUUID) {
if !uuid.IsValid() { if !uuid.IsValid() {
log.Fatal("Id of ConvergerUUID is nil!") panic(fmt.Sprintf("Id of ConvergerUUID(%s) is nil!", uuid.Name()))
} }
obj.mutex.Lock() obj.mutex.Lock()
delete(obj.status, uuid.ID()) delete(obj.status, uuid.ID())
@@ -160,13 +165,13 @@ func (obj *converger) Pause() { // FIXME: add a sync ACK on pause before return
// have joined the map, then it might appears as if we converged before we did! // have joined the map, then it might appears as if we converged before we did!
func (obj *converger) Loop(startPaused bool) { func (obj *converger) Loop(startPaused bool) {
if obj.control == nil { if obj.control == nil {
log.Fatal("Converger not initialized correctly") panic("Converger not initialized correctly")
} }
if startPaused { // start paused without racing if startPaused { // start paused without racing
select { select {
case e := <-obj.control: case e := <-obj.control:
if !e { if !e {
log.Fatal("Converger expected true!") panic("Converger expected true!")
} }
} }
} }
@@ -174,13 +179,13 @@ func (obj *converger) Loop(startPaused bool) {
select { select {
case e := <-obj.control: // expecting "false" which means pause! case e := <-obj.control: // expecting "false" which means pause!
if e { if e {
log.Fatal("Converger expected false!") panic("Converger expected false!")
} }
// now i'm paused... // now i'm paused...
select { select {
case e := <-obj.control: case e := <-obj.control:
if !e { if !e {
log.Fatal("Converger expected true!") panic("Converger expected true!")
} }
// restart // restart
// kick once to refresh the check... // kick once to refresh the check...
@@ -230,6 +235,16 @@ func (obj *convergerUUID) ID() uint64 {
return obj.id return obj.id
} }
// Name returns a user defined name for the specific convergerUUID.
func (obj *convergerUUID) Name() string {
return obj.name
}
// SetName sets a user defined name for the specific convergerUUID.
func (obj *convergerUUID) SetName(name string) {
obj.name = name
}
// IsValid tells us if the id is valid or has already been destroyed // IsValid tells us if the id is valid or has already been destroyed
func (obj *convergerUUID) IsValid() bool { func (obj *convergerUUID) IsValid() bool {
return obj.id != 0 // an id of 0 is invalid return obj.id != 0 // an id of 0 is invalid
@@ -246,8 +261,8 @@ func (obj *convergerUUID) IsConverged() bool {
} }
// SetConverged is a helper function to the regular SetConverged notification // SetConverged is a helper function to the regular SetConverged notification
func (obj *convergerUUID) SetConverged(isConverged bool) { func (obj *convergerUUID) SetConverged(isConverged bool) error {
obj.converger.SetConverged(obj, isConverged) return obj.converger.SetConverged(obj, isConverged)
} }
// Unregister is a helper function to unregister myself // Unregister is a helper function to unregister myself

View File

@@ -678,6 +678,7 @@ func (obj *EmbdEtcd) CtxError(ctx context.Context, err error) (context.Context,
// CbLoop is the loop where callback execution is serialized // CbLoop is the loop where callback execution is serialized
func (obj *EmbdEtcd) CbLoop() { func (obj *EmbdEtcd) CbLoop() {
cuuid := obj.converger.Register() cuuid := obj.converger.Register()
cuuid.SetName("Etcd: CbLoop")
defer cuuid.Unregister() defer cuuid.Unregister()
if e := obj.Connect(false); e != nil { if e := obj.Connect(false); e != nil {
return // fatal return // fatal
@@ -731,6 +732,7 @@ func (obj *EmbdEtcd) CbLoop() {
// Loop is the main loop where everything is serialized // Loop is the main loop where everything is serialized
func (obj *EmbdEtcd) Loop() { func (obj *EmbdEtcd) Loop() {
cuuid := obj.converger.Register() cuuid := obj.converger.Register()
cuuid.SetName("Etcd: Loop")
defer cuuid.Unregister() defer cuuid.Unregister()
if e := obj.Connect(false); e != nil { if e := obj.Connect(false); e != nil {
return // fatal return // fatal