Skip to content

Commit 30bbd9a

Browse files
committed
feat(accumulator): add high priority accumulator to get response from output plugins immediately
1 parent 160548d commit 30bbd9a

File tree

12 files changed

+161
-5
lines changed

12 files changed

+161
-5
lines changed

accumulator.go

+9
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ type Accumulator interface {
5252
// Upgrade to a TrackingAccumulator with space for maxTracked
5353
// metrics/batches.
5454
WithTracking(maxTracked int) TrackingAccumulator
55+
56+
// Upgrade to a HighPriorityAccumulator
57+
ToHighPriority() HighPriorityAccumulator
5558
}
5659

5760
// TrackingID uniquely identifies a tracked metric group
@@ -91,3 +94,9 @@ type TrackingAccumulator interface {
9194
// Delivered returns a channel that will contain the tracking results.
9295
Delivered() <-chan DeliveryInfo
9396
}
97+
98+
type HighPriorityAccumulator interface {
99+
Accumulator
100+
101+
AddMetricHighPriority(Metric) error
102+
}

agent/accumulator.go

+22
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ func (ac *accumulator) WithTracking(maxTracked int) telegraf.TrackingAccumulator
126126
}
127127
}
128128

129+
func (ac *accumulator) ToHighPriority() telegraf.HighPriorityAccumulator {
130+
return &highPriorityAccumulator{
131+
ac,
132+
}
133+
}
134+
129135
type trackingAccumulator struct {
130136
telegraf.Accumulator
131137
delivered chan telegraf.DeliveryInfo
@@ -158,3 +164,19 @@ func (a *trackingAccumulator) onDelivery(info telegraf.DeliveryInfo) {
158164
panic("channel is full")
159165
}
160166
}
167+
168+
type highPriorityAccumulator struct {
169+
*accumulator
170+
}
171+
172+
func (ac *highPriorityAccumulator) AddMetricHighPriority(m telegraf.Metric) error {
173+
m.SetTime(m.Time().Round(ac.precision))
174+
if m := ac.maker.MakeMetric(m); m != nil {
175+
errCh := make(chan error, 1)
176+
ac.metrics <- m.ToHighPriority(errCh)
177+
178+
err := <-errCh
179+
return err
180+
}
181+
return nil
182+
}

agent/agent.go

+4
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ func (a *Agent) startInputs(
354354
acc := NewAccumulator(input, dst)
355355
acc.SetPrecision(getPrecision(precision, interval))
356356

357+
if input.Config.HighPriorityIO {
358+
acc = acc.ToHighPriority()
359+
}
360+
357361
if err := input.Start(acc); err != nil {
358362
// If the model tells us to remove the plugin we do so without error
359363
var fatalErr *internal.FatalError

config/config.go

+11
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,13 @@ func (c *Config) addInput(name string, table *ast.Table) error {
13231323
return err
13241324
}
13251325

1326+
if pluginConfig.HighPriorityIO {
1327+
if _, ok = input.(telegraf.HighPriorityInput); !ok {
1328+
return fmt.Errorf("input plugin %s is not high priority input plugin", pluginConfig.Name)
1329+
}
1330+
log.Printf("I! [agent] Input plugin %s is high-priority-IO\n", pluginConfig.Name)
1331+
}
1332+
13261333
if err := c.toml.UnmarshalTable(table, input); err != nil {
13271334
return err
13281335
}
@@ -1531,6 +1538,7 @@ func (c *Config) buildInput(name string, tbl *ast.Table) (*models.InputConfig, e
15311538
cp.NameOverride = c.getFieldString(tbl, "name_override")
15321539
cp.Alias = c.getFieldString(tbl, "alias")
15331540
cp.LogLevel = c.getFieldString(tbl, "log_level")
1541+
cp.HighPriorityIO = c.getFieldBool(tbl, "high_priority_io")
15341542

15351543
cp.Tags = make(map[string]string)
15361544
if node, ok := tbl.Fields["tags"]; ok {
@@ -1621,6 +1629,9 @@ func (c *Config) missingTomlField(_ reflect.Type, key string) error {
16211629
// Parser and serializer options to ignore
16221630
case "data_type", "influx_parser_type":
16231631

1632+
// Customized options to ignore
1633+
case "high_priority_io":
1634+
16241635
default:
16251636
c.unusedFieldsMutex.Lock()
16261637
c.UnusedFields[key] = true

input.go

+8
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,11 @@ type ServiceInput interface {
2121
// to the accumulator before returning.
2222
Stop()
2323
}
24+
25+
type HighPriorityInput interface {
26+
Input
27+
28+
// MarkHighPriority does nothing but tell us this input plugin may be a high
29+
// priority input plugin.
30+
MarkHighPriority()
31+
}

metric.go

+9
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@ type Metric interface {
125125
// Drop marks the metric as processed successfully without being written
126126
// to any output.
127127
Drop()
128+
129+
// ToHighPriority upgrade the metric to a HighPriorityMetric.
130+
ToHighPriority(chan<- error) HighPriorityMetric
131+
}
132+
133+
type HighPriorityMetric interface {
134+
Metric
135+
136+
ErrorCh() chan<- error
128137
}
129138

130139
// TemplateMetric is an interface to use in templates (e.g text/template)

metric/metric.go

+16
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,22 @@ func (m *metric) Reject() {
286286
func (m *metric) Drop() {
287287
}
288288

289+
func (m *metric) ToHighPriority(errorCh chan<- error) telegraf.HighPriorityMetric {
290+
return &highPriorityMetric{
291+
metric: m,
292+
errorCh: errorCh,
293+
}
294+
}
295+
296+
type highPriorityMetric struct {
297+
*metric
298+
errorCh chan<- error
299+
}
300+
301+
func (m *highPriorityMetric) ErrorCh() chan<- error {
302+
return m.errorCh
303+
}
304+
289305
// Convert field to a supported type or nil if inconvertible
290306
func convertField(v interface{}) interface{} {
291307
switch v := v.(type) {

models/running_input.go

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ type InputConfig struct {
9090
StartupErrorBehavior string
9191
LogLevel string
9292

93+
HighPriorityIO bool
94+
9395
NameOverride string
9496
MeasurementPrefix string
9597
MeasurementSuffix string

models/running_output.go

+9
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,15 @@ func (r *RunningOutput) AddMetric(metric telegraf.Metric) {
242242
metric.AddSuffix(r.Config.NameSuffix)
243243
}
244244

245+
if mt, ok := metric.(telegraf.HighPriorityMetric); ok {
246+
r.log.Tracef("calling output plugin %s directly - before\n", r.Config.Name)
247+
err := r.Output.Write([]telegraf.Metric{mt})
248+
r.log.Tracef("calling output plugin %s directly - after\n", r.Config.Name)
249+
mt.ErrorCh() <- err
250+
r.log.Tracef("calling output plugin %s directly - responded\n", r.Config.Name)
251+
return
252+
}
253+
245254
dropped := r.buffer.Add(metric)
246255
atomic.AddInt64(&r.droppedMetrics, int64(dropped))
247256

plugins/inputs/http_listener_v2/http_listener_v2.go

+45-3
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,35 @@ func (h *HTTPListenerV2) serveWrite(res http.ResponseWriter, req *http.Request)
293293
})
294294
}
295295

296+
switch acc := h.acc.(type) {
297+
case telegraf.HighPriorityAccumulator:
298+
h.writeToHighPriorityAccumulator(req, res, acc, metrics)
299+
default:
300+
for _, m := range metrics {
301+
for headerName, measurementName := range h.HTTPHeaderTags {
302+
headerValues := req.Header.Get(headerName)
303+
if len(headerValues) > 0 {
304+
m.AddTag(measurementName, headerValues)
305+
}
306+
}
307+
308+
if h.PathTag {
309+
m.AddTag(pathTag, req.URL.Path)
310+
}
311+
312+
h.acc.AddMetric(m)
313+
}
314+
}
315+
316+
res.WriteHeader(h.SuccessCode)
317+
}
318+
319+
func (h *HTTPListenerV2) writeToHighPriorityAccumulator(
320+
req *http.Request,
321+
res http.ResponseWriter,
322+
acc telegraf.HighPriorityAccumulator,
323+
metrics []telegraf.Metric,
324+
) {
296325
for _, m := range metrics {
297326
for headerName, measurementName := range h.HTTPHeaderTags {
298327
headerValues := req.Header.Get(headerName)
@@ -305,10 +334,19 @@ func (h *HTTPListenerV2) serveWrite(res http.ResponseWriter, req *http.Request)
305334
m.AddTag(pathTag, req.URL.Path)
306335
}
307336

308-
h.acc.AddMetric(m)
309-
}
337+
if err := acc.AddMetricHighPriority(m); err != nil {
338+
acc.AddError(fmt.Errorf("writing data to output failed: %w", err))
339+
h.Log.Debugf("got error from high-priority-IO")
310340

311-
res.WriteHeader(h.SuccessCode)
341+
res.WriteHeader(http.StatusInternalServerError)
342+
_, err = res.Write([]byte(fmt.Sprintf(`{"error":"%v"}`, err)))
343+
if err != nil {
344+
acc.AddError(fmt.Errorf("send htp response failed: %w", err))
345+
h.Log.Tracef("send http response failed: %v", err)
346+
}
347+
return
348+
}
349+
}
312350
}
313351

314352
func (h *HTTPListenerV2) collectBody(res http.ResponseWriter, req *http.Request) ([]byte, bool) {
@@ -419,6 +457,10 @@ func (h *HTTPListenerV2) authenticateIfSet(handler http.HandlerFunc, res http.Re
419457
}
420458
}
421459

460+
func (h *HTTPListenerV2) MarkHighPriority() {
461+
// Do nothing
462+
}
463+
422464
func init() {
423465
inputs.Add("http_listener_v2", func() telegraf.Input {
424466
return &HTTPListenerV2{

plugins/inputs/socket_listener/socket_listener.go

+17-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package socket_listener
44

55
import (
66
_ "embed"
7+
"fmt"
78
"net"
89
"sync"
910

@@ -63,8 +64,18 @@ func (sl *SocketListener) Start(acc telegraf.Accumulator) error {
6364
sl.Log.Debug(internal.NoMetricsCreatedMsg)
6465
})
6566
}
66-
for _, m := range metrics {
67-
acc.AddMetric(m)
67+
switch ac := acc.(type) {
68+
case telegraf.HighPriorityAccumulator:
69+
for _, m := range metrics {
70+
if err = ac.AddMetricHighPriority(m); err != nil {
71+
sl.Log.Tracef("input socket_listener got error from high-priority-IO")
72+
acc.AddError(fmt.Errorf("writing data to output failed: %w", err))
73+
}
74+
}
75+
default:
76+
for _, m := range metrics {
77+
ac.AddMetric(m)
78+
}
6879
}
6980
}
7081
onError := func(err error) {
@@ -88,6 +99,10 @@ func (sl *SocketListener) Stop() {
8899
}
89100
}
90101

102+
func (sl *SocketListener) MarkHighPriority() {
103+
// Do nothing
104+
}
105+
91106
func init() {
92107
inputs.Add("socket_listener", func() telegraf.Input {
93108
return &SocketListener{}

testutil/accumulator.go

+9
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ func (a *Accumulator) AddMetrics(metrics []telegraf.Metric) {
176176
}
177177
}
178178

179+
func (a *Accumulator) AddMetricHighPriority(m telegraf.Metric) error {
180+
a.addMeasurement(m.Name(), m.Tags(), m.Fields(), m.Type(), m.Time())
181+
return nil
182+
}
183+
179184
func (a *Accumulator) AddSummary(
180185
measurement string,
181186
fields map[string]interface{},
@@ -222,6 +227,10 @@ func (a *Accumulator) WithTracking(maxTracked int) telegraf.TrackingAccumulator
222227
return a
223228
}
224229

230+
func (a *Accumulator) ToHighPriority() telegraf.HighPriorityAccumulator {
231+
return a
232+
}
233+
225234
func (a *Accumulator) AddTrackingMetric(m telegraf.Metric) telegraf.TrackingID {
226235
dm, id := metric.WithTracking(m, a.onDelivery)
227236
a.AddMetric(dm)

0 commit comments

Comments
 (0)