Change ConnectionMaker from being focused on PeerName to being focused on address

This commit is contained in:
Bryan Boreham committed 2014-09-08 12:42:06 +01:00
1 parent b69682a626
commit 0a94388f0a
2 files changed
+42 -66

No files matched your search

+34 -55
View File
@@ -33,8 +33,7 @@ func StartConnectionMaker(router *Router) *ConnectionMaker {
state := &ConnectionMaker{
router: router,
queryChan: queryChan,
failedConnections: make(map[PeerName]*FailedConnection),
attempting: make(map[ConnectionMakerPair]bool)}
targets: make(map[string]*Target)}
go state.queryLoop(queryChan)
return state
}
@@ -57,7 +56,8 @@ func (cm *ConnectionMaker) String() string {
func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteraction) {
var tick <-chan time.Time
maybeTick := func() {
if tick == nil && len(cm.failedConnections) > 0 {
// would be nice to optimise this to stop ticking when there is nothing worth trying
if tick == nil {
tick = time.After(5 * time.Second)
}
}
@@ -70,47 +70,31 @@ func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteractio
switch {
case query.code == CMEnsure:
if query.name != cm.router.Ourself.Name {
cm.addToFailedConnection(query.name, query.foundAt)
cm.addToTargets(query.name, query.foundAt)
maybeTick()
}
case query.code == CMStatus:
query.resultChan <- cm.status()
case query.code == CMConnSucceeded:
delete(cm.attempting, ConnectionMakerPair{query.foundAt, query.name})
delete(cm.failedConnections[query.name].foundAt, query.foundAt)
cm.targets[query.foundAt].attempting = false
case query.code == CMConnFailed:
delete(cm.attempting, ConnectionMakerPair{query.foundAt, query.name})
cm.targets[query.foundAt].attempting = false
default:
log.Fatal("Unexpected connection maker query:", query)
}
case now := <-tick:
ConnectionLoop:
for name, failedConn := range cm.failedConnections {
if now.After(failedConn.tryAfter) {
if _, found := cm.router.Ourself.ConnectionTo(name); found {
delete(cm.failedConnections, name)
continue
} else if len(failedConn.foundAt) == 0 {
delete(cm.failedConnections, name)
continue
} else if failedConn.attemptCount == MaxAttemptCount {
delete(cm.failedConnections, name)
for address, target := range cm.targets {
if target.conn == nil && !target.attempting && now.After(target.tryAfter) {
if target.attemptCount == MaxAttemptCount {
// FIXME
continue
}
// wait until all current attempts to contact this peer have failed before starting a new batch
for target := range failedConn.foundAt {
if cm.attempting[ConnectionMakerPair{target, name}] {
continue ConnectionLoop
}
}
after, interval := tryAfter(failedConn.tryInterval)
failedConn.tryInterval = interval
failedConn.tryAfter = after
failedConn.attemptCount += 1
for target := range failedConn.foundAt {
cm.attempting[ConnectionMakerPair{target, name}] = true
go cm.attemptConnection(target, name)
}
after, interval := tryAfter(target.tryInterval)
target.tryInterval = interval
target.tryAfter = after
target.attemptCount += 1
target.attempting = true;
go cm.attemptConnection(address, target.commandLine)
}
}
tick = nil
@@ -119,58 +103,53 @@ func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteractio
}
}
func (cm *ConnectionMaker) addToFailedConnection(name PeerName, foundAt string) {
failed := cm.failedConnections[name]
if failed == nil {
func (cm *ConnectionMaker) addToTargets(name PeerName, foundAt string) {
target := cm.targets[foundAt]
if target == nil {
after, interval := tryAfter(InitialInterval)
failed = &FailedConnection{
foundAt: make(map[string]bool),
target = &Target{
tryInterval: interval,
tryAfter: after}
}
foundAtHost, foundAtPortStr, err := net.SplitHostPort(foundAt)
if err == nil {
// ensure port-less version is there
failed.foundAt[foundAtHost] = true
cm.targets[foundAtHost] = target
if foundAtPort, err := strconv.Atoi(foundAtPortStr); err == nil && foundAtPort != Port {
failed.foundAt[foundAt] = true
cm.targets[foundAt] = target
}
} else {
// can't split it, assume it must not have port on it
failed.foundAt[foundAt] = true
cm.targets[foundAt] = target
}
cm.failedConnections[name] = failed
}
func (cm *ConnectionMaker) status() string {
var buf bytes.Buffer
for name, failedConn := range cm.failedConnections {
tryingNow := false
foundAt := make([]string, 0, len(failedConn.foundAt))
for target := range failedConn.foundAt {
foundAt = append(foundAt, target)
tryingNow = tryingNow || cm.attempting[ConnectionMakerPair{target, name}]
}
if tryingNow {
buf.WriteString(fmt.Sprintf("%s (%v attempts, trying again now): %v\n", name, failedConn.attemptCount, foundAt))
for address, target := range cm.targets {
if (target.conn != nil) {
buf.WriteString(fmt.Sprintf("%s connected to: %v\n", address, target.conn))
} else if target.attempting {
buf.WriteString(fmt.Sprintf("%s (%v attempts, trying again now)\n", address, target.attemptCount))
} else {
buf.WriteString(fmt.Sprintf("%s (%v attempts, next at %v): %v\n", name, failedConn.attemptCount, failedConn.tryAfter, foundAt))
buf.WriteString(fmt.Sprintf("%s (%v attempts, next at %v)\n", address, target.attemptCount, target.tryAfter))
}
}
return buf.String()
}
func (cm *ConnectionMaker) attemptConnection(foundAt string, targetName PeerName) {
func (cm *ConnectionMaker) attemptConnection(address string, acceptNewPeer bool) {
log.Println("Attempting connection to ", address)
var conncode = CMConnSucceeded
if err := cm.router.Ourself.CreateConnection(foundAt, targetName); err != nil {
if err := cm.router.Ourself.CreateConnection(address, UnknownPeerName); err != nil {
log.Println(err)
conncode = CMConnFailed
}
// Tell the query loop we've finished this attempt
cm.queryChan <- &ConnectionMakerInteraction{
Interaction: Interaction{code: conncode},
name: targetName,
foundAt: foundAt}
name: UnknownPeerName,
foundAt: address}
}
func tryAfter(interval time.Duration) (time.Time, time.Duration) {
+8 -11
View File
@@ -159,22 +159,19 @@ type ConnectionMakerInteraction struct {
name PeerName
}
type ConnectionMakerPair struct {
foundAt string
name PeerName
}
type ConnectionMaker struct {
router *Router
queryChan chan<- *ConnectionMakerInteraction
failedConnections map[PeerName]*FailedConnection
attempting map[ConnectionMakerPair]bool
targets map[string]*Target
}
type FailedConnection struct {
foundAt map[string]bool
tryAfter time.Time
tryInterval time.Duration
// Information about an address where we may find a peer
type Target struct {
commandLine bool // was this address given to us on the command line?
attempting bool // are we currently attempting to connect to this address?
conn *Connection
tryAfter time.Time // next time to try this address
tryInterval time.Duration // backoff time on next failure
attemptCount int
}