-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #63 from mariomac/capacity-limiter
NETOBSERV-613: drop messages when they accumulate in the exporter
- Loading branch information
Showing
6 changed files
with
174 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package flow | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
const initialLogPeriod = time.Minute | ||
const maxLogPeriod = time.Hour | ||
|
||
var cllog = logrus.WithField("component", "capacity.Limiter") | ||
|
||
// CapacityLimiter forwards the flows between two nodes but checks the status of the destination | ||
// node's buffered channel. If it is already full, it drops the incoming flow and periodically will | ||
// log a message about the number of lost flows. | ||
type CapacityLimiter struct { | ||
droppedFlows int | ||
} | ||
|
||
func (c *CapacityLimiter) Limit(in <-chan []*Record, out chan<- []*Record) { | ||
go c.logDroppedFlows() | ||
for i := range in { | ||
if len(out) < cap(out) { | ||
out <- i | ||
} else { | ||
c.droppedFlows += len(i) | ||
} | ||
} | ||
} | ||
|
||
func (c *CapacityLimiter) logDroppedFlows() { | ||
logPeriod := initialLogPeriod | ||
debugging := logrus.IsLevelEnabled(logrus.DebugLevel) | ||
for { | ||
time.Sleep(logPeriod) | ||
|
||
// a race condition might happen in this counter but it's not important as it's just for | ||
// logging purposes | ||
df := c.droppedFlows | ||
if df > 0 { | ||
c.droppedFlows = 0 | ||
cllog.Warnf("%d flows were dropped during the last %s because the agent is forwarding "+ | ||
"more flows than the remote ingestor is able to process. You might "+ | ||
"want to increase the CACHE_MAX_FLOWS and CACHE_ACTIVE_TIMEOUT property", | ||
df, logPeriod) | ||
|
||
// if not debug logs, backoff to avoid flooding the log with warning messages | ||
if !debugging && logPeriod < maxLogPeriod { | ||
logPeriod *= 2 | ||
if logPeriod > maxLogPeriod { | ||
logPeriod = maxLogPeriod | ||
} | ||
} | ||
} else { | ||
logPeriod = initialLogPeriod | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package flow | ||
|
||
import ( | ||
"strconv" | ||
"testing" | ||
|
||
"github.com/netobserv/gopipes/pkg/node" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
const limiterLen = 50 | ||
|
||
func TestCapacityLimiter_NoDrop(t *testing.T) { | ||
// GIVEN a limiter-enabled pipeline | ||
pipeIn, pipeOut := capacityLimiterPipe() | ||
|
||
// WHEN it buffers less elements than it's maximum capacity | ||
for i := 0; i < 33; i++ { | ||
pipeIn <- []*Record{{Interface: strconv.Itoa(i)}} | ||
} | ||
|
||
// THEN it is able to retrieve all the buffered elements | ||
for i := 0; i < 33; i++ { | ||
elem := <-pipeOut | ||
require.Len(t, elem, 1) | ||
assert.Equal(t, strconv.Itoa(i), elem[0].Interface) | ||
} | ||
|
||
// AND not a single extra element | ||
select { | ||
case elem := <-pipeOut: | ||
assert.Failf(t, "unexpected element", "%#v", elem) | ||
default: | ||
// ok! | ||
} | ||
} | ||
|
||
func TestCapacityLimiter_Drop(t *testing.T) { | ||
// GIVEN a limiter-enabled pipeline | ||
pipeIn, pipeOut := capacityLimiterPipe() | ||
|
||
// WHEN it receives more elements than its maximum capacity | ||
// (it's not blocking) | ||
for i := 0; i < limiterLen*2; i++ { | ||
pipeIn <- []*Record{{Interface: strconv.Itoa(i)}} | ||
} | ||
|
||
// THEN it is only able to retrieve all the nth first buffered elements | ||
// (plus the single element that is buffered in the output channel) | ||
for i := 0; i < limiterLen+1; i++ { | ||
elem := <-pipeOut | ||
require.Len(t, elem, 1) | ||
assert.Equal(t, strconv.Itoa(i), elem[0].Interface) | ||
} | ||
|
||
// BUT not a single extra element | ||
select { | ||
case elem := <-pipeOut: | ||
assert.Failf(t, "unexpected element", "%#v", elem) | ||
default: | ||
// ok! | ||
} | ||
} | ||
|
||
func capacityLimiterPipe() (in chan<- []*Record, out <-chan []*Record) { | ||
inCh, outCh := make(chan []*Record), make(chan []*Record) | ||
|
||
init := node.AsInit(func(initOut chan<- []*Record) { | ||
for i := range inCh { | ||
initOut <- i | ||
} | ||
}) | ||
limiter := node.AsMiddle((&CapacityLimiter{}).Limit) | ||
term := node.AsTerminal(func(termIn <-chan []*Record) { | ||
for i := range termIn { | ||
outCh <- i | ||
} | ||
}, node.ChannelBufferLen(limiterLen)) | ||
|
||
init.SendsTo(limiter) | ||
limiter.SendsTo(term) | ||
|
||
init.Start() | ||
|
||
return inCh, outCh | ||
} |