chanfitness: refactor to store channels by peer
When dealing with online events, we actually need to track our events by peer, not by channel. All we need to track channels is to have a set of online events for a peer which at least contain those events. This change refactors chanfitness to track by peer.
This commit is contained in:
6 files changed
+358
-344
No files matched your search
+130
-57
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
type eventType int
|
||||
@@ -29,74 +28,152 @@ func (e eventType) String() string {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// channelEvent is a a timestamped event which is observed on a per channel
|
||||
// basis.
|
||||
type channelEvent struct {
|
||||
type event struct {
|
||||
timestamp time.Time
|
||||
eventType eventType
|
||||
}
|
||||
|
||||
// chanEventLog stores all events that have occurred over a channel's lifetime.
|
||||
type chanEventLog struct {
|
||||
// channelPoint is the outpoint for the channel's funding transaction.
|
||||
channelPoint wire.OutPoint
|
||||
// peerLog tracks events for a peer and its channels. If we currently have no
|
||||
// channels with the peer, it will simply track its current online state. If we
|
||||
// do have channels open with the peer, it will track the peer's online and
|
||||
// offline events so that we can calculate uptime for our channels. A single
|
||||
// event log is used for these online and offline events, and uptime for a
|
||||
// channel is calculated by examining a subsection of this log.
|
||||
type peerLog struct {
|
||||
// online stores whether the peer is currently online.
|
||||
online bool
|
||||
|
||||
// peer is the compressed public key of the peer being monitored.
|
||||
peer route.Vertex
|
||||
|
||||
// events is a log of timestamped events observed for the channel.
|
||||
events []*channelEvent
|
||||
// onlineEvents is a log of timestamped events observed for the peer.
|
||||
onlineEvents []*event
|
||||
|
||||
// clock allows creation of deterministic unit tests.
|
||||
clock clock.Clock
|
||||
|
||||
// channels contains a set of currently open channels. Channels will be
|
||||
// added and removed from this map as they are opened and closed.
|
||||
channels map[wire.OutPoint]*channelInfo
|
||||
}
|
||||
|
||||
// newPeerLog creates a log for a peer.
|
||||
func newPeerLog(clock clock.Clock) *peerLog {
|
||||
return &peerLog{
|
||||
clock: clock,
|
||||
channels: make(map[wire.OutPoint]*channelInfo),
|
||||
}
|
||||
}
|
||||
|
||||
// channelInfo contains information about a channel.
|
||||
type channelInfo struct {
|
||||
// openedAt tracks the first time this channel was seen. This is not
|
||||
// necessarily the time that it confirmed on chain because channel
|
||||
// events are not persisted at present.
|
||||
openedAt time.Time
|
||||
|
||||
// closedAt is the time that the channel was closed. If the channel has
|
||||
// not been closed yet, it is zero.
|
||||
closedAt time.Time
|
||||
}
|
||||
|
||||
// newEventLog creates an event log for a channel with the openedAt time set.
|
||||
func newEventLog(channelPoint wire.OutPoint, peer route.Vertex,
|
||||
clock clock.Clock) *chanEventLog {
|
||||
|
||||
eventlog := &chanEventLog{
|
||||
channelPoint: channelPoint,
|
||||
peer: peer,
|
||||
clock: clock,
|
||||
openedAt: clock.Now(),
|
||||
func newChannelInfo(openedAt time.Time) *channelInfo {
|
||||
return &channelInfo{
|
||||
openedAt: openedAt,
|
||||
}
|
||||
|
||||
return eventlog
|
||||
}
|
||||
|
||||
// close sets the closing time for an event log.
|
||||
func (e *chanEventLog) close() {
|
||||
e.closedAt = e.clock.Now()
|
||||
}
|
||||
// onlineEvent records a peer online or offline event in the log.
|
||||
func (p *peerLog) onlineEvent(online bool) {
|
||||
p.online = online
|
||||
|
||||
// add appends an event with the given type and current time to the event log.
|
||||
// The open time for the eventLog will be set to the event's timestamp if it is
|
||||
// not set yet.
|
||||
func (e *chanEventLog) add(eventType eventType) {
|
||||
// If the channel is already closed, return early without adding an
|
||||
// event.
|
||||
if !e.closedAt.IsZero() {
|
||||
// If we have no channels currently open with the peer, we do not want
|
||||
// to commit resources to tracking their online state beyond a simple
|
||||
// online boolean, so we exit early.
|
||||
if p.channelCount() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Add the event to the eventLog with the current timestamp.
|
||||
event := &channelEvent{
|
||||
timestamp: e.clock.Now(),
|
||||
p.addEvent(online, p.clock.Now())
|
||||
}
|
||||
|
||||
// addEvent records an online or offline event in our event log.
|
||||
func (p *peerLog) addEvent(online bool, time time.Time) {
|
||||
eventType := peerOnlineEvent
|
||||
if !online {
|
||||
eventType = peerOfflineEvent
|
||||
}
|
||||
|
||||
event := &event{
|
||||
timestamp: time,
|
||||
eventType: eventType,
|
||||
}
|
||||
e.events = append(e.events, event)
|
||||
|
||||
log.Debugf("Channel %v recording event: %v", e.channelPoint, eventType)
|
||||
p.onlineEvents = append(p.onlineEvents, event)
|
||||
}
|
||||
|
||||
// addChannel adds a channel to our log. If we have not tracked any online
|
||||
// events for our peer yet, we create one with our peer's current online state
|
||||
// so that we know the state that the peer had at channel start, which is
|
||||
// required to calculate uptime over the channel's lifetime.
|
||||
func (p *peerLog) addChannel(channelPoint wire.OutPoint) error {
|
||||
_, ok := p.channels[channelPoint]
|
||||
if ok {
|
||||
return fmt.Errorf("channel: %v already present", channelPoint)
|
||||
}
|
||||
|
||||
openTime := p.clock.Now()
|
||||
p.channels[channelPoint] = newChannelInfo(openTime)
|
||||
|
||||
// If we do not have any online events tracked for our peer (which is
|
||||
// the case when we have no other channels open with the peer), we add
|
||||
// an event with the peer's current online state so that we know that
|
||||
// starting state for this peer when a channel was connected (which
|
||||
// allows us to calculate uptime over the lifetime of the channel).
|
||||
if len(p.onlineEvents) == 0 {
|
||||
p.addEvent(p.online, openTime)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeChannel removes a channel from our log. If we have no more channels
|
||||
// with the peer after removing this one, we clear our list of events.
|
||||
func (p *peerLog) removeChannel(channelPoint wire.OutPoint) error {
|
||||
_, ok := p.channels[channelPoint]
|
||||
if !ok {
|
||||
return fmt.Errorf("channel: %v not present", channelPoint)
|
||||
}
|
||||
|
||||
delete(p.channels, channelPoint)
|
||||
|
||||
// If we have no more channels in our event log, we can discard all of
|
||||
// our online events in memory, since we don't need them anymore.
|
||||
// TODO(carla): this could be done on a per channel basis.
|
||||
if p.channelCount() == 0 {
|
||||
p.onlineEvents = nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// channelCount returns the number of channels that we currently have
|
||||
// with the peer.
|
||||
func (p *peerLog) channelCount() int {
|
||||
return len(p.channels)
|
||||
}
|
||||
|
||||
// channelUptime looks up a channel and returns the amount of time that the
|
||||
// channel has been monitored for and its uptime over this period.
|
||||
func (p *peerLog) channelUptime(channelPoint wire.OutPoint) (time.Duration,
|
||||
time.Duration, error) {
|
||||
|
||||
channel, ok := p.channels[channelPoint]
|
||||
if !ok {
|
||||
return 0, 0, ErrChannelNotFound
|
||||
}
|
||||
|
||||
now := p.clock.Now()
|
||||
|
||||
uptime, err := p.uptime(channel.openedAt, now)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return now.Sub(channel.openedAt), uptime, nil
|
||||
}
|
||||
|
||||
// onlinePeriod represents a period of time over which a peer was online.
|
||||
@@ -112,9 +189,9 @@ type onlinePeriod struct {
|
||||
// calculated until the present. This function expects the event log provided
|
||||
// to be ordered by ascending timestamp, and can tolerate multiple consecutive
|
||||
// online or offline events.
|
||||
func (e *chanEventLog) getOnlinePeriods() []*onlinePeriod {
|
||||
func (p *peerLog) getOnlinePeriods() []*onlinePeriod {
|
||||
// Return early if there are no events, there are no online periods.
|
||||
if len(e.events) == 0 {
|
||||
if len(p.onlineEvents) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -123,7 +200,7 @@ func (e *chanEventLog) getOnlinePeriods() []*onlinePeriod {
|
||||
// a different type to our own. It is used to determine the
|
||||
// start time of our online periods when we experience an
|
||||
// offline event, and to track our last recorded state.
|
||||
lastEvent *channelEvent
|
||||
lastEvent *event
|
||||
onlinePeriods []*onlinePeriod
|
||||
)
|
||||
|
||||
@@ -133,7 +210,7 @@ func (e *chanEventLog) getOnlinePeriods() []*onlinePeriod {
|
||||
// the online event and the present is not tracked. The type of the most
|
||||
// recent event is tracked using the offline bool so that we can add a
|
||||
// final online period if necessary.
|
||||
for _, event := range e.events {
|
||||
for _, event := range p.onlineEvents {
|
||||
switch event.eventType {
|
||||
case peerOnlineEvent:
|
||||
// If our previous event is nil, we just set it and
|
||||
@@ -188,24 +265,20 @@ func (e *chanEventLog) getOnlinePeriods() []*onlinePeriod {
|
||||
}
|
||||
|
||||
// The log ended on an online event, so we need to add a final online
|
||||
// event. If the channel is closed, this period is until channel
|
||||
// closure. It it is still open, we calculate it until the present.
|
||||
// period which terminates at the present.
|
||||
finalEvent := &onlinePeriod{
|
||||
start: lastEvent.timestamp,
|
||||
end: e.closedAt,
|
||||
}
|
||||
if finalEvent.end.IsZero() {
|
||||
finalEvent.end = e.clock.Now()
|
||||
end: p.clock.Now(),
|
||||
}
|
||||
|
||||
// Add the final online period to the set and return.
|
||||
return append(onlinePeriods, finalEvent)
|
||||
}
|
||||
|
||||
// uptime calculates the total uptime we have recorded for a channel over the
|
||||
// uptime calculates the total uptime we have recorded for a peer over the
|
||||
// inclusive range specified. An error is returned if the end of the range is
|
||||
// before the start or a zero end time is returned.
|
||||
func (e *chanEventLog) uptime(start, end time.Time) (time.Duration, error) {
|
||||
func (p *peerLog) uptime(start, end time.Time) (time.Duration, error) {
|
||||
// Error if we are provided with an invalid range to calculate uptime
|
||||
// for.
|
||||
if end.Before(start) {
|
||||
@@ -218,7 +291,7 @@ func (e *chanEventLog) uptime(start, end time.Time) (time.Duration, error) {
|
||||
|
||||
var uptime time.Duration
|
||||
|
||||
for _, p := range e.getOnlinePeriods() {
|
||||
for _, p := range p.getOnlinePeriods() {
|
||||
// The online period ends before the range we're looking at, so
|
||||
// we can skip over it.
|
||||
if p.end.Before(start) {
|
||||
|
||||
Reference in new issue
Block a user