chanfitness: record timestamped flap count for peers

In preparation for storing our flap count on disk, we start tracking
flap count per-peer.
This commit is contained in:
carla 2020-09-08 13:47:19 +02:00
parent e05b4a8e2e
commit 8b09b2d716
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91
3 changed files with 63 additions and 10 deletions

@ -46,6 +46,14 @@ type peerLog struct {
// onlineEvents is a log of timestamped events observed for the peer. // onlineEvents is a log of timestamped events observed for the peer.
onlineEvents []*event onlineEvents []*event
// flapCount is the number of times this peer has been observed as
// going offline.
flapCount int
// lastFlap is the timestamp of the last flap we recorded for the peer.
// This value will be nil if we have never recorded a flap for the peer.
lastFlap *time.Time
// clock allows creation of deterministic unit tests. // clock allows creation of deterministic unit tests.
clock clock.Clock clock clock.Clock
@ -76,8 +84,15 @@ func newChannelInfo(openedAt time.Time) *channelInfo {
} }
} }
// onlineEvent records a peer online or offline event in the log. // onlineEvent records a peer online or offline event in the log and increments
// the peer's flap count.
func (p *peerLog) onlineEvent(online bool) { func (p *peerLog) onlineEvent(online bool) {
eventTime := p.clock.Now()
// Record flap count information and online state regardless of whether
// we have any channels open with this peer.
p.flapCount++
p.lastFlap = &eventTime
p.online = online p.online = online
// If we have no channels currently open with the peer, we do not want // If we have no channels currently open with the peer, we do not want
@ -87,7 +102,7 @@ func (p *peerLog) onlineEvent(online bool) {
return return
} }
p.addEvent(online, p.clock.Now()) p.addEvent(online, eventTime)
} }
// addEvent records an online or offline event in our event log. // addEvent records an online or offline event in our event log.
@ -176,6 +191,12 @@ func (p *peerLog) channelUptime(channelPoint wire.OutPoint) (time.Duration,
return now.Sub(channel.openedAt), uptime, nil return now.Sub(channel.openedAt), uptime, nil
} }
// getFlapCount returns the peer's flap count and the timestamp that we last
// recorded a flap.
func (p *peerLog) getFlapCount() (int, *time.Time) {
return p.flapCount, p.lastFlap
}
// onlinePeriod represents a period of time over which a peer was online. // onlinePeriod represents a period of time over which a peer was online.
type onlinePeriod struct { type onlinePeriod struct {
start, end time.Time start, end time.Time

@ -14,29 +14,53 @@ func TestPeerLog(t *testing.T) {
clock := clock.NewTestClock(testNow) clock := clock.NewTestClock(testNow)
peerLog := newPeerLog(clock) peerLog := newPeerLog(clock)
// assertFlapCount is a helper that asserts that our peer's flap count
// and timestamp is set to expected values.
assertFlapCount := func(expectedCount int, expectedTs *time.Time) {
flapCount, flapTs := peerLog.getFlapCount()
require.Equal(t, expectedCount, flapCount)
require.Equal(t, expectedTs, flapTs)
}
require.Zero(t, peerLog.channelCount()) require.Zero(t, peerLog.channelCount())
require.False(t, peerLog.online) require.False(t, peerLog.online)
assertFlapCount(0, nil)
// Test that looking up an unknown channel fails. // Test that looking up an unknown channel fails.
_, _, err := peerLog.channelUptime(wire.OutPoint{Index: 1}) _, _, err := peerLog.channelUptime(wire.OutPoint{Index: 1})
require.Error(t, err) require.Error(t, err)
lastFlap := clock.Now()
// Add an offline event, since we have no channels, we do not expect // Add an offline event, since we have no channels, we do not expect
// to have any online periods recorded for our peer. // to have any online periods recorded for our peer. However, we should
// increment our flap count for the peer.
peerLog.onlineEvent(false) peerLog.onlineEvent(false)
require.Len(t, peerLog.getOnlinePeriods(), 0) require.Len(t, peerLog.getOnlinePeriods(), 0)
assertFlapCount(1, &lastFlap)
// Bump our test clock's time by an hour so that we can create an online
// event with a distinct time.
lastFlap = testNow.Add(time.Hour)
clock.SetTime(lastFlap)
// Likewise, if we have an online event, nothing beyond the online state // Likewise, if we have an online event, nothing beyond the online state
// of our peer log should change. // of our peer log should change, but our flap count should change.
peerLog.onlineEvent(true) peerLog.onlineEvent(true)
require.Len(t, peerLog.getOnlinePeriods(), 0) require.Len(t, peerLog.getOnlinePeriods(), 0)
assertFlapCount(2, &lastFlap)
// Add a channel and assert that we have one channel listed. // Add a channel and assert that we have one channel listed. Since this
// is the first channel we track for the peer, we expect an online
// event to be added, however, our flap count should not change because
// this is not a new online event, we are just copying one into our log
// for our purposes.
chan1 := wire.OutPoint{ chan1 := wire.OutPoint{
Index: 1, Index: 1,
} }
require.NoError(t, peerLog.addChannel(chan1)) require.NoError(t, peerLog.addChannel(chan1))
require.Equal(t, 1, peerLog.channelCount()) require.Equal(t, 1, peerLog.channelCount())
assertFlapCount(2, &lastFlap)
// Assert that we can now successfully get our added channel. // Assert that we can now successfully get our added channel.
_, _, err = peerLog.channelUptime(chan1) _, _, err = peerLog.channelUptime(chan1)
@ -44,8 +68,8 @@ func TestPeerLog(t *testing.T) {
// Bump our test clock's time so that our current time is different to // Bump our test clock's time so that our current time is different to
// channel open time. // channel open time.
now := testNow.Add(time.Hour) lastFlap = clock.Now().Add(time.Hour)
clock.SetTime(now) clock.SetTime(lastFlap)
// Now that we have added a channel and an hour has passed, we expect // Now that we have added a channel and an hour has passed, we expect
// our uptime and lifetime to both equal an hour. // our uptime and lifetime to both equal an hour.
@ -54,8 +78,10 @@ func TestPeerLog(t *testing.T) {
require.Equal(t, time.Hour, lifetime) require.Equal(t, time.Hour, lifetime)
require.Equal(t, time.Hour, uptime) require.Equal(t, time.Hour, uptime)
// Add an offline event for our peer. // Add an offline event for our peer and assert that our flap count is
// incremented.
peerLog.onlineEvent(false) peerLog.onlineEvent(false)
assertFlapCount(3, &lastFlap)
// Now we add another channel to our store and assert that we now report // Now we add another channel to our store and assert that we now report
// two channels for this peer. // two channels for this peer.
@ -67,7 +93,7 @@ func TestPeerLog(t *testing.T) {
// Progress our time again, so that our peer has now been offline for // Progress our time again, so that our peer has now been offline for
// two hours. // two hours.
now = now.Add(time.Hour * 2) now := lastFlap.Add(time.Hour * 2)
clock.SetTime(now) clock.SetTime(now)
// Our first channel should report as having been monitored for three // Our first channel should report as having been monitored for three
@ -91,10 +117,11 @@ func TestPeerLog(t *testing.T) {
require.Equal(t, time.Duration(0), uptime) require.Equal(t, time.Duration(0), uptime)
// Finally, remove our second channel and assert that our peer cleans // Finally, remove our second channel and assert that our peer cleans
// up its in memory set of events. // up its in memory set of events but keeps its flap count record.
require.NoError(t, peerLog.removeChannel(chan2)) require.NoError(t, peerLog.removeChannel(chan2))
require.Equal(t, 0, peerLog.channelCount()) require.Equal(t, 0, peerLog.channelCount())
require.Len(t, peerLog.onlineEvents, 0) require.Len(t, peerLog.onlineEvents, 0)
assertFlapCount(3, &lastFlap)
} }
// TestGetOnlinePeriod tests the getOnlinePeriod function. It tests the case // TestGetOnlinePeriod tests the getOnlinePeriod function. It tests the case

@ -26,4 +26,9 @@ type peerMonitor interface {
// the channel has been monitored for and its uptime over this period. // the channel has been monitored for and its uptime over this period.
channelUptime(channelPoint wire.OutPoint) (time.Duration, channelUptime(channelPoint wire.OutPoint) (time.Duration,
time.Duration, error) time.Duration, error)
// getFlapCount returns the peer's flap count and the timestamp that we
// last recorded a flap, which may be nil if we have never recorded a
// flap for this peer.
getFlapCount() (int, *time.Time)
} }