drop metrics outside of the aggregators period

This commit is contained in:
Cameron Sparr 2016-10-10 13:43:47 +01:00
parent 8ca4a50c18
commit a84ce5d5cb
4 changed files with 106 additions and 15 deletions

View File

@ -358,6 +358,17 @@ func (a *Agent) Run(shutdown chan struct{}) error {
} }
}() }()
wg.Add(len(a.Config.Aggregators))
for _, aggregator := range a.Config.Aggregators {
go func(agg *models.RunningAggregator) {
defer wg.Done()
acc := NewAccumulator(agg, metricC)
acc.SetPrecision(a.Config.Agent.Precision.Duration,
a.Config.Agent.Interval.Duration)
agg.Run(acc, shutdown)
}(aggregator)
}
wg.Add(len(a.Config.Inputs)) wg.Add(len(a.Config.Inputs))
for _, input := range a.Config.Inputs { for _, input := range a.Config.Inputs {
interval := a.Config.Agent.Interval.Duration interval := a.Config.Agent.Interval.Duration
@ -371,17 +382,6 @@ func (a *Agent) Run(shutdown chan struct{}) error {
}(input, interval) }(input, interval)
} }
wg.Add(len(a.Config.Aggregators))
for _, aggregator := range a.Config.Aggregators {
go func(agg *models.RunningAggregator) {
defer wg.Done()
acc := NewAccumulator(agg, metricC)
acc.SetPrecision(a.Config.Agent.Precision.Duration,
a.Config.Agent.Interval.Duration)
agg.Run(acc, shutdown)
}(aggregator)
}
wg.Wait() wg.Wait()
return nil return nil
} }

View File

@ -87,7 +87,9 @@ There are no generic configuration options available for all outputs.
The following config parameters are available for all aggregators: The following config parameters are available for all aggregators:
* **period**: The period on which to flush & clear each aggregator. * **period**: The period on which to flush & clear each aggregator. All metrics
that are sent with timestamps outside of this period will be ignored by the
aggregator.
* **delay**: The delay before each aggregator is flushed. This is to control * **delay**: The delay before each aggregator is flushed. This is to control
how long for aggregators to wait before receiving metrics from input plugins, how long for aggregators to wait before receiving metrics from input plugins,
in the case that aggregators are flushing and inputs are gathering on the in the case that aggregators are flushing and inputs are gathering on the

View File

@ -11,6 +11,9 @@ type RunningAggregator struct {
Config *AggregatorConfig Config *AggregatorConfig
metrics chan telegraf.Metric metrics chan telegraf.Metric
periodStart time.Time
periodEnd time.Time
} }
func NewRunningAggregator( func NewRunningAggregator(
@ -105,10 +108,32 @@ func (r *RunningAggregator) reset() {
r.a.Reset() r.a.Reset()
} }
// Run runs the running aggregator, listens for incoming metrics, and waits
// for period ticks to tell it when to push and reset the aggregator.
func (r *RunningAggregator) Run( func (r *RunningAggregator) Run(
acc telegraf.Accumulator, acc telegraf.Accumulator,
shutdown chan struct{}, shutdown chan struct{},
) { ) {
// The start of the period is truncated to the nearest second.
//
// Every metric then gets it's timestamp checked and is dropped if it
// is not within:
//
// start < t < end + truncation + delay
//
// So if we start at now = 00:00.2 with a 10s period and 0.3s delay:
// now = 00:00.2
// start = 00:00
// truncation = 00:00.2
// end = 00:10
// 1st interval: 00:00 - 00:10.5
// 2nd interval: 00:10 - 00:20.5
// etc.
//
now := time.Now()
r.periodStart = now.Truncate(time.Second)
truncation := now.Sub(r.periodStart)
r.periodEnd = r.periodStart.Add(r.Config.Period)
time.Sleep(r.Config.Delay) time.Sleep(r.Config.Delay)
periodT := time.NewTicker(r.Config.Period) periodT := time.NewTicker(r.Config.Period)
defer periodT.Stop() defer periodT.Stop()
@ -122,8 +147,16 @@ func (r *RunningAggregator) Run(
} }
return return
case m := <-r.metrics: case m := <-r.metrics:
if m.Time().Before(r.periodStart) ||
m.Time().After(r.periodEnd.Add(truncation).Add(r.Config.Delay)) {
// the metric is outside the current aggregation period, so
// skip it.
continue
}
r.add(m) r.add(m)
case <-periodT.C: case <-periodT.C:
r.periodStart = r.periodEnd
r.periodEnd = r.periodStart.Add(r.Config.Period)
r.push(acc) r.push(acc)
r.reset() r.reset()
} }

View File

@ -31,11 +31,64 @@ func TestAdd(t *testing.T) {
map[string]interface{}{"value": int(101)}, map[string]interface{}{"value": int(101)},
map[string]string{}, map[string]string{},
telegraf.Untyped, telegraf.Untyped,
time.Now(), time.Now().Add(time.Millisecond*150),
) )
assert.False(t, ra.Add(m)) assert.False(t, ra.Add(m))
for { for {
time.Sleep(time.Millisecond)
if atomic.LoadInt64(&a.sum) > 0 {
break
}
}
assert.Equal(t, int64(101), atomic.LoadInt64(&a.sum))
}
func TestAddMetricsOutsideCurrentPeriod(t *testing.T) {
a := &TestAggregator{}
ra := NewRunningAggregator(a, &AggregatorConfig{
Name: "TestRunningAggregator",
Filter: Filter{
NamePass: []string{"*"},
},
Period: time.Millisecond * 500,
})
assert.NoError(t, ra.Config.Filter.Compile())
acc := testutil.Accumulator{}
go ra.Run(&acc, make(chan struct{}))
// metric before current period
m := ra.MakeMetric(
"RITest",
map[string]interface{}{"value": int(101)},
map[string]string{},
telegraf.Untyped,
time.Now().Add(-time.Hour),
)
assert.False(t, ra.Add(m))
// metric after current period
m = ra.MakeMetric(
"RITest",
map[string]interface{}{"value": int(101)},
map[string]string{},
telegraf.Untyped,
time.Now().Add(time.Hour),
)
assert.False(t, ra.Add(m))
// "now" metric
m = ra.MakeMetric(
"RITest",
map[string]interface{}{"value": int(101)},
map[string]string{},
telegraf.Untyped,
time.Now().Add(time.Millisecond*50),
)
assert.False(t, ra.Add(m))
for {
time.Sleep(time.Millisecond)
if atomic.LoadInt64(&a.sum) > 0 { if atomic.LoadInt64(&a.sum) > 0 {
break break
} }
@ -68,11 +121,12 @@ func TestAddAndPushOnePeriod(t *testing.T) {
map[string]interface{}{"value": int(101)}, map[string]interface{}{"value": int(101)},
map[string]string{}, map[string]string{},
telegraf.Untyped, telegraf.Untyped,
time.Now(), time.Now().Add(time.Millisecond*100),
) )
assert.False(t, ra.Add(m)) assert.False(t, ra.Add(m))
for { for {
time.Sleep(time.Millisecond)
if acc.NMetrics() > 0 { if acc.NMetrics() > 0 {
break break
} }
@ -182,7 +236,9 @@ type TestAggregator struct {
func (t *TestAggregator) Description() string { return "" } func (t *TestAggregator) Description() string { return "" }
func (t *TestAggregator) SampleConfig() string { return "" } func (t *TestAggregator) SampleConfig() string { return "" }
func (t *TestAggregator) Reset() {} func (t *TestAggregator) Reset() {
atomic.StoreInt64(&t.sum, 0)
}
func (t *TestAggregator) Push(acc telegraf.Accumulator) { func (t *TestAggregator) Push(acc telegraf.Accumulator) {
acc.AddFields("TestMetric", acc.AddFields("TestMetric",