always use ReplicationState, and have a map from that to the rsfs

This commit is contained in:
Christian Schwarz
2018-08-16 11:02:34 +02:00
parent 094eced2c7
commit 5479463783
+60 -54
View File
@@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/bits"
"net" "net"
"sort" "sort"
"sync" "sync"
@@ -12,7 +13,7 @@ import (
) )
//go:generate stringer -type=ReplicationState //go:generate stringer -type=ReplicationState
type ReplicationState int type ReplicationState uint
const ( const (
Planning ReplicationState = 1 << iota Planning ReplicationState = 1 << iota
@@ -23,6 +24,22 @@ const (
ContextDone ContextDone
) )
func (s ReplicationState) rsf() replicationStateFunc {
idx := bits.TrailingZeros(uint(s))
if idx == bits.UintSize {
panic(s) // invalid value
}
m := []replicationStateFunc{
rsfPlanning,
rsfPlanningError,
rsfWorking,
rsfWorkingWait,
nil,
nil,
}
return m[idx]
}
type replicationQueueItem struct { type replicationQueueItem struct {
retriesSinceLastError int retriesSinceLastError int
fsr *FSReplication fsr *FSReplication
@@ -48,7 +65,7 @@ type Replication struct {
sleepUntil time.Time sleepUntil time.Time
} }
type replicationUpdater func(func(*Replication)) type replicationUpdater func(func(*Replication)) (newState ReplicationState)
type replicationStateFunc func(context.Context, EndpointPair, replicationUpdater) replicationStateFunc type replicationStateFunc func(context.Context, EndpointPair, replicationUpdater) replicationStateFunc
//go:generate stringer -type=FSReplicationState //go:generate stringer -type=FSReplicationState
@@ -138,25 +155,24 @@ type FSReplicationStep struct {
} }
func (r *Replication) Drive(ctx context.Context, ep EndpointPair, retryNow chan struct{}) { func (r *Replication) Drive(ctx context.Context, ep EndpointPair, retryNow chan struct{}) {
var u replicationUpdater = func(f func(*Replication)) { var u replicationUpdater = func(f func(*Replication)) ReplicationState {
r.lock.Lock() r.lock.Lock()
defer r.lock.Unlock() defer r.lock.Unlock()
f(r) if f != nil {
f(r)
}
return r.state
} }
var s replicationStateFunc = rsfPlanning var s replicationStateFunc = rsfPlanning
var pre, post ReplicationState var pre, post ReplicationState
for s != nil { for s != nil {
preTime := time.Now() preTime := time.Now()
u(func(r *Replication){ pre = u(nil)
pre = r.state
})
s = s(ctx, ep, u) s = s(ctx, ep, u)
delta := time.Now().Sub(preTime) delta := time.Now().Sub(preTime)
u(func(r *Replication){ post = u(nil)
post = r.state
})
getLogger(ctx). getLogger(ctx).
WithField("transition", fmt.Sprintf("%s => %s", pre, post)). WithField("transition", fmt.Sprintf("%s => %s", pre, post)).
WithField("duration", delta). WithField("duration", delta).
@@ -173,11 +189,10 @@ func rsfPlanning(ctx context.Context, ep EndpointPair, u replicationUpdater) rep
log := getLogger(ctx) log := getLogger(ctx)
handlePlanningError := func(err error) replicationStateFunc { handlePlanningError := func(err error) replicationStateFunc {
u(func(r *Replication){ return u(func(r *Replication) {
r.state = PlanningError
r.planningError = err r.planningError = err
}) r.state = PlanningError
return rsfPlanningError }).rsf()
} }
sfss, err := ep.Sender().ListFilesystems(ctx) sfss, err := ep.Sender().ListFilesystems(ctx)
@@ -273,13 +288,12 @@ func rsfPlanning(ctx context.Context, ep EndpointPair, u replicationUpdater) rep
} }
u(func(r *Replication){ return u(func(r *Replication) {
r.completed = completed r.completed = completed
r.pending = pending r.pending = pending
r.state = Working
r.planningError = nil r.planningError = nil
}) r.state = Working
return rsfWorking }).rsf()
} }
func rsfPlanningError(ctx context.Context, ep EndpointPair, u replicationUpdater) replicationStateFunc { func rsfPlanningError(ctx context.Context, ep EndpointPair, u replicationUpdater) replicationStateFunc {
@@ -290,25 +304,23 @@ func rsfPlanningError(ctx context.Context, ep EndpointPair, u replicationUpdater
t := time.NewTimer(sleepTime) // FIXME make constant onfigurable t := time.NewTimer(sleepTime) // FIXME make constant onfigurable
defer t.Stop() defer t.Stop()
select { select {
case <- ctx.Done(): case <-ctx.Done():
u(func(r *Replication){ return u(func(r *Replication) {
r.state = ContextDone r.state = ContextDone
r.contextError = ctx.Err() r.contextError = ctx.Err()
}) }).rsf()
return nil case <-t.C:
case <- t.C: return u(func(r *Replication) {
u(func(r *Replication){ r.state = Planning
r.state = Planning }).rsf()
})
return rsfPlanning
} }
} }
func rsfWorking(ctx context.Context, ep EndpointPair, u replicationUpdater) (rsfNext replicationStateFunc) { func rsfWorking(ctx context.Context, ep EndpointPair, u replicationUpdater) replicationStateFunc {
var active *replicationQueueItem var active *replicationQueueItem
u(func(r *Replication) { rsfNext := u(func(r *Replication) {
if r.active != nil { if r.active != nil {
active = r.active active = r.active
return return
@@ -349,17 +361,16 @@ func rsfWorking(ctx context.Context, ep EndpointPair, u replicationUpdater) (rsf
active = r.active active = r.active
r.pending = r.pending[1:] r.pending = r.pending[1:]
}) }).rsf()
if active == nil { if active == nil {
return rsfNext return rsfNext
} }
if active.fsr.state == FSRetryWait { if active.fsr.state == FSRetryWait {
u(func(r *Replication) { return u(func(r *Replication) {
r.state = WorkingWait r.state = WorkingWait
}) }).rsf()
return rsfWorkingWait
} }
if active.fsr.state != FSQueued { if active.fsr.state != FSQueued {
panic(active) panic(active)
@@ -367,7 +378,7 @@ func rsfWorking(ctx context.Context, ep EndpointPair, u replicationUpdater) (rsf
fsState := active.fsr.drive(ctx, ep) fsState := active.fsr.drive(ctx, ep)
u(func(r *Replication) { return u(func(r *Replication) {
if fsState&FSQueued != 0 { if fsState&FSQueued != 0 {
r.active.retriesSinceLastError = 0 r.active.retriesSinceLastError = 0
@@ -380,31 +391,26 @@ func rsfWorking(ctx context.Context, ep EndpointPair, u replicationUpdater) (rsf
panic(r.active) panic(r.active)
} }
}) }).rsf()
return rsfWorking
} }
func rsfWorkingWait(ctx context.Context, ep EndpointPair, u replicationUpdater) replicationStateFunc { func rsfWorkingWait(ctx context.Context, ep EndpointPair, u replicationUpdater) replicationStateFunc {
sleepTime := 10*time.Second sleepTime := 10 * time.Second
u(func(r* Replication){ u(func(r *Replication) {
r.sleepUntil = time.Now().Add(sleepTime) r.sleepUntil = time.Now().Add(sleepTime)
}) })
t := time.NewTimer(sleepTime) t := time.NewTimer(sleepTime)
defer t.Stop() defer t.Stop()
select { select {
case <- ctx.Done(): case <-ctx.Done():
u(func(r *Replication){ return u(func(r *Replication) {
r.state = ContextDone r.state = ContextDone
r.contextError = ctx.Err() r.contextError = ctx.Err()
}) }).rsf()
return nil case <-t.C:
case <- t.C: return u(func(r *Replication) {
u(func(r *Replication){ r.state = Working
r.state = Working }).rsf()
})
return rsfWorking
} }
} }