ソースを参照

further force epsilon greedy to embed more generic HostPool; still some type assertions needed

Dan Frank 12 年 前
コミット
22283bf1ec
2 ファイル変更35 行追加18 行削除
  1. 29 18
      epsilon_greedy.go
  2. 6 0
      hostpool.go

+ 29 - 18
epsilon_greedy.go

@@ -1,8 +1,10 @@
 package hostpool
 
 import (
+	"errors"
 	"log"
 	"math/rand"
+	"sync"
 	"time"
 )
 
@@ -22,7 +24,8 @@ func (r *epsilonHostPoolResponse) Mark(err error) {
 }
 
 type epsilonGreedyHostPool struct {
-	standardHostPool               // TODO - would be nifty if we could embed HostPool and Locker interfaces
+	HostPool
+	sync.Locker
 	epsilon                float32 // this is our exploration factor
 	decayDuration          time.Duration
 	EpsilonValueCalculator // embed the epsilonValueCalculator
@@ -50,7 +53,8 @@ func NewEpsilonGreedy(hosts []string, decayDuration time.Duration, calc EpsilonV
 	}
 	stdHP := New(hosts).(*standardHostPool)
 	p := &epsilonGreedyHostPool{
-		standardHostPool:       *stdHP,
+		HostPool:               stdHP,
+		Locker:                 stdHP,
 		epsilon:                float32(initialEpsilon),
 		decayDuration:          decayDuration,
 		EpsilonValueCalculator: calc,
@@ -58,7 +62,7 @@ func NewEpsilonGreedy(hosts []string, decayDuration time.Duration, calc EpsilonV
 	}
 
 	// allocate structures
-	for _, h := range p.hostList {
+	for _, h := range stdHP.hostList {
 		h.epsilonCounts = make([]int64, epsilonBuckets)
 		h.epsilonValues = make([]int64, epsilonBuckets)
 	}
@@ -82,7 +86,7 @@ func (p *epsilonGreedyHostPool) epsilonGreedyDecay() {
 }
 func (p *epsilonGreedyHostPool) performEpsilonGreedyDecay() {
 	p.Lock()
-	for _, h := range p.hostList {
+	for _, h := range p.HostPool.(*standardHostPool).hostList {
 		h.epsilonIndex += 1
 		h.epsilonIndex = h.epsilonIndex % epsilonBuckets
 		h.epsilonCounts[h.epsilonIndex] = 0
@@ -93,17 +97,15 @@ func (p *epsilonGreedyHostPool) performEpsilonGreedyDecay() {
 
 func (p *epsilonGreedyHostPool) Get() HostPoolResponse {
 	p.Lock()
-	defer p.Unlock()
-	host := p.getEpsilonGreedy()
-	started := time.Now()
-	return &epsilonHostPoolResponse{
-		HostPoolResponse: &standardHostPoolResponse{host: host, pool: &p.standardHostPool},
-		started:          started,
-		pool:             p,
+	host, err := p.getEpsilonGreedy()
+	p.Unlock()
+	if err != nil {
+		host = p.HostPool.Get().Host()
 	}
+	return p.responseForHostName(host)
 }
 
-func (p *epsilonGreedyHostPool) getEpsilonGreedy() string {
+func (p *epsilonGreedyHostPool) getEpsilonGreedy() (string, error) {
 	var hostToUse *hostEntry
 
 	// this is our exploration phase
@@ -112,14 +114,14 @@ func (p *epsilonGreedyHostPool) getEpsilonGreedy() string {
 		if p.epsilon < minEpsilon {
 			p.epsilon = minEpsilon
 		}
-		return p.getRoundRobin()
+		return "", errors.New("Exploration")
 	}
 
 	// calculate values for each host in the 0..1 range (but not ormalized)
 	var possibleHosts []*hostEntry
 	now := time.Now()
 	var sumValues float64
-	for _, h := range p.hostList {
+	for _, h := range p.HostPool.(*standardHostPool).hostList {
 		if h.canTryHost(now) {
 			v := h.getWeightedAverageResponseTime()
 			if v > 0 {
@@ -153,13 +155,13 @@ func (p *epsilonGreedyHostPool) getEpsilonGreedy() string {
 		if len(possibleHosts) != 0 {
 			log.Println("Failed to randomly choose a host, Dan loses")
 		}
-		return p.getRoundRobin()
+		return "", errors.New("No host chosen")
 	}
 
 	if hostToUse.dead {
-		hostToUse.willRetryHost(p.maxRetryInterval)
+		hostToUse.willRetryHost(p.HostPool.(*standardHostPool).maxRetryInterval)
 	}
-	return hostToUse.host
+	return hostToUse.host, nil
 }
 
 func (p *epsilonGreedyHostPool) recordTiming(eHostR *epsilonHostPoolResponse) {
@@ -168,7 +170,7 @@ func (p *epsilonGreedyHostPool) recordTiming(eHostR *epsilonHostPoolResponse) {
 
 	p.Lock()
 	defer p.Unlock()
-	h, ok := p.hosts[host]
+	h, ok := p.HostPool.(*standardHostPool).hosts[host]
 	if !ok {
 		log.Fatalf("host %s not in HostPool %v", host, p.Hosts())
 	}
@@ -176,6 +178,15 @@ func (p *epsilonGreedyHostPool) recordTiming(eHostR *epsilonHostPoolResponse) {
 	h.epsilonValues[h.epsilonIndex] += int64(duration.Seconds() * 1000)
 }
 
+func (p *epsilonGreedyHostPool) responseForHostName(host string) HostPoolResponse {
+	started := time.Now()
+	return &epsilonHostPoolResponse{
+		HostPoolResponse: p.HostPool.responseForHostName(host),
+		started:          started,
+		pool:             p,
+	}
+}
+
 // --- timer: this just exists for testing
 
 type timer interface {

+ 6 - 0
hostpool.go

@@ -40,6 +40,8 @@ type HostPool interface {
 
 	ResetAll()
 	Hosts() []string
+
+	responseForHostName(string) HostPoolResponse
 }
 
 type standardHostPool struct {
@@ -175,3 +177,7 @@ func (p *standardHostPool) Hosts() []string {
 	}
 	return hosts
 }
+
+func (p *standardHostPool) responseForHostName(host string) HostPoolResponse {
+	return &standardHostPoolResponse{host: host, pool: p}
+}