routing: stricter mission control state failure updates
This commit puts a mechanism in place to prevent a failure for a low amount from being overwritten very soon after by a higher amount failure.
This commit is contained in:
parent
6a36ed44f8
commit
1a6b28553a
@ -50,6 +50,11 @@ const (
|
|||||||
// DefaultAprioriWeight is the default a priori weight. See
|
// DefaultAprioriWeight is the default a priori weight. See
|
||||||
// MissionControlConfig for further explanation.
|
// MissionControlConfig for further explanation.
|
||||||
DefaultAprioriWeight = 0.5
|
DefaultAprioriWeight = 0.5
|
||||||
|
|
||||||
|
// DefaultMinFailureRelaxInterval is the default minimum time that must
|
||||||
|
// have passed since the previously recorded failure before the failure
|
||||||
|
// amount may be raised.
|
||||||
|
DefaultMinFailureRelaxInterval = time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodeResults contains previous results from a node to its peers.
|
// NodeResults contains previous results from a node to its peers.
|
||||||
@ -113,6 +118,11 @@ type MissionControlConfig struct {
|
|||||||
// results, unless there are none available.
|
// results, unless there are none available.
|
||||||
AprioriWeight float64
|
AprioriWeight float64
|
||||||
|
|
||||||
|
// MinFailureRelaxInterval is the minimum time that must have passed
|
||||||
|
// since the previously recorded failure before the failure amount may
|
||||||
|
// be raised.
|
||||||
|
MinFailureRelaxInterval time.Duration
|
||||||
|
|
||||||
// SelfNode is our own pubkey.
|
// SelfNode is our own pubkey.
|
||||||
SelfNode route.Vertex
|
SelfNode route.Vertex
|
||||||
}
|
}
|
||||||
@ -188,7 +198,7 @@ func NewMissionControl(db kvdb.Backend, cfg *MissionControlConfig) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
mc := &MissionControl{
|
mc := &MissionControl{
|
||||||
state: newMissionControlState(),
|
state: newMissionControlState(cfg.MinFailureRelaxInterval),
|
||||||
now: time.Now,
|
now: time.Now,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
store: store,
|
store: store,
|
||||||
|
@ -19,13 +19,21 @@ type missionControlState struct {
|
|||||||
// lastSecondChance tracks the last time a second chance was granted for
|
// lastSecondChance tracks the last time a second chance was granted for
|
||||||
// a directed node pair.
|
// a directed node pair.
|
||||||
lastSecondChance map[DirectedNodePair]time.Time
|
lastSecondChance map[DirectedNodePair]time.Time
|
||||||
|
|
||||||
|
// minFailureRelaxInterval is the minimum time that must have passed
|
||||||
|
// since the previously recorded failure before the failure amount may
|
||||||
|
// be raised.
|
||||||
|
minFailureRelaxInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMissionControlState instantiates a new mission control state object.
|
// newMissionControlState instantiates a new mission control state object.
|
||||||
func newMissionControlState() *missionControlState {
|
func newMissionControlState(
|
||||||
|
minFailureRelaxInterval time.Duration) *missionControlState {
|
||||||
|
|
||||||
return &missionControlState{
|
return &missionControlState{
|
||||||
lastPairResult: make(map[route.Vertex]NodeResults),
|
lastPairResult: make(map[route.Vertex]NodeResults),
|
||||||
lastSecondChance: make(map[DirectedNodePair]time.Time),
|
lastSecondChance: make(map[DirectedNodePair]time.Time),
|
||||||
|
minFailureRelaxInterval: minFailureRelaxInterval,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +95,22 @@ func (m *missionControlState) setLastPairResult(fromNode, toNode route.Vertex,
|
|||||||
// condition) to be revived as if it just happened.
|
// condition) to be revived as if it just happened.
|
||||||
failAmt := result.amt
|
failAmt := result.amt
|
||||||
|
|
||||||
|
// Drop result if it would increase the failure amount too soon
|
||||||
|
// after a previous failure. This can happen if htlc results
|
||||||
|
// come in out of order. This check makes it easier for payment
|
||||||
|
// processes to converge to a final state.
|
||||||
|
failInterval := timestamp.Sub(current.FailTime)
|
||||||
|
if failAmt > current.FailAmt &&
|
||||||
|
failInterval < m.minFailureRelaxInterval {
|
||||||
|
|
||||||
|
log.Debugf("Ignoring higher amount failure within min "+
|
||||||
|
"failure relaxation interval: prev_fail_amt=%v, "+
|
||||||
|
"fail_amt=%v, interval=%v",
|
||||||
|
current.FailAmt, failAmt, failInterval)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
current.FailTime = timestamp
|
current.FailTime = timestamp
|
||||||
current.FailAmt = failAmt
|
current.FailAmt = failAmt
|
||||||
|
|
||||||
|
47
routing/missioncontrol_state_test.go
Normal file
47
routing/missioncontrol_state_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestMissionControlStateFailureResult tests setting failure results on the
|
||||||
|
// mission control state.
|
||||||
|
func TestMissionControlStateFailureResult(t *testing.T) {
|
||||||
|
const minFailureRelaxInterval = time.Minute
|
||||||
|
state := newMissionControlState(minFailureRelaxInterval)
|
||||||
|
|
||||||
|
var (
|
||||||
|
from = route.Vertex{1}
|
||||||
|
to = route.Vertex{2}
|
||||||
|
timestamp = testTime
|
||||||
|
)
|
||||||
|
|
||||||
|
// Report a 1000 sat failure.
|
||||||
|
state.setLastPairResult(from, to, timestamp, &pairResult{amt: 1000})
|
||||||
|
result, _ := state.getLastPairResult(from)
|
||||||
|
if result[to].FailAmt != 1000 {
|
||||||
|
t.Fatalf("unexpected fail amount %v", result[to].FailAmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report an 1100 sat failure one hour later. It is expected to
|
||||||
|
// overwrite the previous failure.
|
||||||
|
timestamp = timestamp.Add(time.Hour)
|
||||||
|
state.setLastPairResult(from, to, timestamp, &pairResult{amt: 1100})
|
||||||
|
result, _ = state.getLastPairResult(from)
|
||||||
|
if result[to].FailAmt != 1100 {
|
||||||
|
t.Fatalf("unexpected fail amount %v", result[to].FailAmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report a 1200 sat failure one second later. Because this increase of
|
||||||
|
// the failure amount is too soon after the previous failure, the result
|
||||||
|
// is not applied.
|
||||||
|
timestamp = timestamp.Add(time.Second)
|
||||||
|
state.setLastPairResult(from, to, timestamp, &pairResult{amt: 1200})
|
||||||
|
result, _ = state.getLastPairResult(from)
|
||||||
|
if result[to].FailAmt != 1100 {
|
||||||
|
t.Fatalf("unexpected fail amount %v", result[to].FailAmt)
|
||||||
|
}
|
||||||
|
}
|
@ -715,6 +715,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
|
|||||||
MaxMcHistory: routingConfig.MaxMcHistory,
|
MaxMcHistory: routingConfig.MaxMcHistory,
|
||||||
AprioriWeight: routingConfig.AprioriWeight,
|
AprioriWeight: routingConfig.AprioriWeight,
|
||||||
SelfNode: selfNode.PubKeyBytes,
|
SelfNode: selfNode.PubKeyBytes,
|
||||||
|
MinFailureRelaxInterval: routing.DefaultMinFailureRelaxInterval,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user