routing: change representation of pair results in mc

This commit changes the in-memory structure of the mission control
state. It prepares for calculation of a node probability. For this we
need to be able to efficiently look up the last results for all channels
of a node.
This commit is contained in:
Joost Jager 2019-09-03 17:36:32 +02:00
parent aefbee78d6
commit a3f7dbc633
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7

@ -49,6 +49,9 @@ const (
prevSuccessProbability = 0.95 prevSuccessProbability = 0.95
) )
// NodeResults contains previous results from a node to its peers.
type NodeResults map[route.Vertex]timedPairResult
// MissionControl contains state which summarizes the past attempts of HTLC // MissionControl contains state which summarizes the past attempts of HTLC
// routing by external callers when sending payments throughout the network. It // routing by external callers when sending payments throughout the network. It
// acts as a shared memory during routing attempts with the goal to optimize the // acts as a shared memory during routing attempts with the goal to optimize the
@ -59,8 +62,11 @@ const (
// since the last failure is used to estimate a success probability that is fed // since the last failure is used to estimate a success probability that is fed
// into the path finding process for subsequent payment attempts. // into the path finding process for subsequent payment attempts.
type MissionControl struct { type MissionControl struct {
// lastPairResult tracks the last payment result per node pair. // lastPairResult tracks the last payment result (on a pair basis) for
lastPairResult map[DirectedNodePair]timedPairResult // each transited node. This is a multi-layer map that allows us to look
// up the failure history of all connected channels (node pairs) for a
// particular node.
lastPairResult map[route.Vertex]NodeResults
// lastNodeFailure tracks the last node level failure per node. // lastNodeFailure tracks the last node level failure per node.
lastNodeFailure map[route.Vertex]time.Time lastNodeFailure map[route.Vertex]time.Time
@ -180,7 +186,7 @@ func NewMissionControl(db *bbolt.DB, cfg *MissionControlConfig) (
} }
mc := &MissionControl{ mc := &MissionControl{
lastPairResult: make(map[DirectedNodePair]timedPairResult), lastPairResult: make(map[route.Vertex]NodeResults),
lastNodeFailure: make(map[route.Vertex]time.Time), lastNodeFailure: make(map[route.Vertex]time.Time),
lastSecondChance: make(map[DirectedNodePair]time.Time), lastSecondChance: make(map[DirectedNodePair]time.Time),
now: time.Now, now: time.Now,
@ -226,7 +232,7 @@ func (m *MissionControl) ResetHistory() error {
return err return err
} }
m.lastPairResult = make(map[DirectedNodePair]timedPairResult) m.lastPairResult = make(map[route.Vertex]NodeResults)
m.lastNodeFailure = make(map[route.Vertex]time.Time) m.lastNodeFailure = make(map[route.Vertex]time.Time)
m.lastSecondChance = make(map[DirectedNodePair]time.Time) m.lastSecondChance = make(map[DirectedNodePair]time.Time)
@ -264,6 +270,36 @@ func (m *MissionControl) getProbAfterFail(lastFailure time.Time) float64 {
return probability return probability
} }
// getLastPairResult gets the last recorded result for a node pair.
func (m *MissionControl) getLastPairResult(fromNode,
toNode route.Vertex) *timedPairResult {
nodePairs, ok := m.lastPairResult[fromNode]
if !ok {
return nil
}
lastResult, ok := nodePairs[toNode]
if !ok {
return nil
}
return &lastResult
}
// setLastPairResult stores a result for a node pair.
func (m *MissionControl) setLastPairResult(fromNode,
toNode route.Vertex, result *timedPairResult) {
nodePairs, ok := m.lastPairResult[fromNode]
if !ok {
nodePairs = make(NodeResults)
m.lastPairResult[fromNode] = nodePairs
}
nodePairs[toNode] = *result
}
// getPairProbability estimates the probability of successfully // getPairProbability estimates the probability of successfully
// traversing from fromNode to toNode based on historical payment outcomes. // traversing from fromNode to toNode based on historical payment outcomes.
func (m *MissionControl) getPairProbability(fromNode, func (m *MissionControl) getPairProbability(fromNode,
@ -276,13 +312,12 @@ func (m *MissionControl) getPairProbability(fromNode,
lastFail := m.lastNodeFailure[fromNode] lastFail := m.lastNodeFailure[fromNode]
// Retrieve the last pair outcome. // Retrieve the last pair outcome.
pair := NewDirectedNodePair(fromNode, toNode) lastPairResult := m.getLastPairResult(fromNode, toNode)
lastPairResult, ok := m.lastPairResult[pair]
// Only look at the last pair outcome if it happened after the last node // Only look at the last pair outcome if it happened after the last node
// level failure. Otherwise the node level failure is the most recent // level failure. Otherwise the node level failure is the most recent
// and used as the basis for calculation of the probability. // and used as the basis for calculation of the probability.
if ok && lastPairResult.timestamp.After(lastFail) { if lastPairResult != nil && lastPairResult.timestamp.After(lastFail) {
if lastPairResult.success { if lastPairResult.success {
return prevSuccessProbability return prevSuccessProbability
} }
@ -355,20 +390,26 @@ func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
pairs := make([]MissionControlPairSnapshot, 0, len(m.lastPairResult)) pairs := make([]MissionControlPairSnapshot, 0, len(m.lastPairResult))
for v, h := range m.lastPairResult { for fromNode, fromPairs := range m.lastPairResult {
for toNode, result := range fromPairs {
// Show probability assuming amount meets min // Show probability assuming amount meets min
// penalization amount. // penalization amount.
prob := m.getPairProbability(v.From, v.To, h.minPenalizeAmt) prob := m.getPairProbability(
fromNode, toNode, result.minPenalizeAmt,
)
pair := MissionControlPairSnapshot{ pair := NewDirectedNodePair(fromNode, toNode)
Pair: v,
MinPenalizeAmt: h.minPenalizeAmt, pairSnapshot := MissionControlPairSnapshot{
Timestamp: h.timestamp, Pair: pair,
MinPenalizeAmt: result.minPenalizeAmt,
Timestamp: result.timestamp,
SuccessProb: prob, SuccessProb: prob,
LastAttemptSuccessful: h.success, LastAttemptSuccessful: result.success,
} }
pairs = append(pairs, pair) pairs = append(pairs, pairSnapshot)
}
} }
snapshot := MissionControlSnapshot{ snapshot := MissionControlSnapshot{
@ -480,10 +521,10 @@ func (m *MissionControl) applyPaymentResult(
pair, pairResult.minPenalizeAmt) pair, pairResult.minPenalizeAmt)
} }
m.lastPairResult[pair] = timedPairResult{ m.setLastPairResult(pair.From, pair.To, &timedPairResult{
timestamp: result.timeReply, timestamp: result.timeReply,
pairResult: pairResult, pairResult: pairResult,
} })
} }
return i.finalFailureReason return i.finalFailureReason