-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
Copy pathqueue_sender.go
186 lines (160 loc) · 5.92 KB
/
queue_sender.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package internal // import "go.opentelemetry.io/collector/exporter/exporterhelper/internal"
import (
"context"
"errors"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/exporter"
"go.opentelemetry.io/collector/exporter/exporterbatcher"
"go.opentelemetry.io/collector/exporter/exporterqueue"
"go.opentelemetry.io/collector/exporter/internal"
"go.opentelemetry.io/collector/exporter/internal/queue"
)
// QueueConfig defines configuration for queueing batches before sending to the consumerSender.
type QueueConfig struct {
// Enabled indicates whether to not enqueue batches before sending to the consumerSender.
Enabled bool `mapstructure:"enabled"`
// NumConsumers is the number of consumers from the queue. Defaults to 10.
// If batching is enabled, a combined batch cannot contain more requests than the number of consumers.
// So it's recommended to set higher number of consumers if batching is enabled.
NumConsumers int `mapstructure:"num_consumers"`
// QueueSize is the maximum number of batches allowed in queue at a given time.
QueueSize int `mapstructure:"queue_size"`
// Blocking controls the queue behavior when full.
// If true it blocks until enough space to add the new request to the queue.
Blocking bool `mapstructure:"blocking"`
// StorageID if not empty, enables the persistent storage and uses the component specified
// as a storage extension for the persistent queue
StorageID *component.ID `mapstructure:"storage"`
}
// NewDefaultQueueConfig returns the default config for QueueConfig.
func NewDefaultQueueConfig() QueueConfig {
return QueueConfig{
Enabled: true,
NumConsumers: 10,
// By default, batches are 8192 spans, for a total of up to 8 million spans in the queue
// This can be estimated at 1-4 GB worth of maximum memory usage
// This default is probably still too high, and may be adjusted further down in a future release
QueueSize: 1_000,
Blocking: false,
}
}
// Validate checks if the QueueConfig configuration is valid
func (qCfg *QueueConfig) Validate() error {
if !qCfg.Enabled {
return nil
}
if qCfg.QueueSize <= 0 {
return errors.New("`queue_size` must be positive")
}
if qCfg.NumConsumers <= 0 {
return errors.New("`num_consumers` must be positive")
}
return nil
}
type QueueSender struct {
BaseSender[internal.Request]
queue exporterqueue.Queue[internal.Request]
numConsumers int
batcher queue.Batcher
consumers *queue.Consumers[internal.Request]
obsrep *ObsReport
exporterID component.ID
logger *zap.Logger
}
func NewQueueSender(
q exporterqueue.Queue[internal.Request],
set exporter.Settings,
numConsumers int,
exportFailureMessage string,
obsrep *ObsReport,
batcherCfg exporterbatcher.Config,
) *QueueSender {
qs := &QueueSender{
queue: q,
numConsumers: numConsumers,
obsrep: obsrep,
exporterID: set.ID,
logger: set.Logger,
}
exportFunc := func(ctx context.Context, req internal.Request) error {
err := qs.NextSender.Send(ctx, req)
if err != nil {
set.Logger.Error("Exporting failed. Dropping data."+exportFailureMessage,
zap.Error(err), zap.Int("dropped_items", req.ItemsCount()))
}
return err
}
if usePullingBasedExporterQueueBatcher.IsEnabled() {
qs.batcher, _ = queue.NewBatcher(batcherCfg, q, exportFunc, numConsumers)
} else {
qs.consumers = queue.NewQueueConsumers[internal.Request](q, numConsumers, exportFunc)
}
return qs
}
// Start is invoked during service startup.
func (qs *QueueSender) Start(ctx context.Context, host component.Host) error {
if err := qs.queue.Start(ctx, host); err != nil {
return err
}
if usePullingBasedExporterQueueBatcher.IsEnabled() {
if err := qs.batcher.Start(ctx, host); err != nil {
return err
}
} else {
if err := qs.consumers.Start(ctx, host); err != nil {
return err
}
}
exporterAttr := attribute.String(ExporterKey, qs.exporterID.String())
dataTypeAttr := attribute.String(DataTypeKey, qs.obsrep.Signal.String())
return errors.Join(
qs.obsrep.TelemetryBuilder.RegisterExporterQueueSizeCallback(func(_ context.Context, o metric.Int64Observer) error {
o.Observe(qs.queue.Size(), metric.WithAttributeSet(attribute.NewSet(exporterAttr, dataTypeAttr)))
return nil
}),
qs.obsrep.TelemetryBuilder.RegisterExporterQueueCapacityCallback(func(_ context.Context, o metric.Int64Observer) error {
o.Observe(qs.queue.Capacity(), metric.WithAttributeSet(attribute.NewSet(exporterAttr)))
return nil
}))
}
// Shutdown is invoked during service shutdown.
func (qs *QueueSender) Shutdown(ctx context.Context) error {
// Stop the queue and consumers, this will drain the queue and will call the retry (which is stopped) that will only
// try once every request.
// At the end, make sure metrics are un-registered since we want to free this object.
defer qs.obsrep.TelemetryBuilder.Shutdown()
if err := qs.queue.Shutdown(ctx); err != nil {
return err
}
if usePullingBasedExporterQueueBatcher.IsEnabled() {
return qs.batcher.Shutdown(ctx)
}
err := qs.consumers.Shutdown(ctx)
return err
}
// send implements the requestSender interface. It puts the request in the queue.
func (qs *QueueSender) Send(ctx context.Context, req internal.Request) error {
// Prevent cancellation and deadline to propagate to the context stored in the queue.
// The grpc/http based receivers will cancel the request context after this function returns.
c := context.WithoutCancel(ctx)
span := trace.SpanFromContext(c)
if err := qs.queue.Offer(c, req); err != nil {
span.AddEvent("Failed to enqueue item.")
return err
}
span.AddEvent("Enqueued item.")
return nil
}
type MockHost struct {
component.Host
Ext map[component.ID]component.Component
}
func (nh *MockHost) GetExtensions() map[component.ID]component.Component {
return nh.Ext
}