Skip to content

Commit d298b69

Browse files
committed
More docs
Signed-off-by: Matej Gera <[email protected]>
1 parent 318fd6c commit d298b69

File tree

1 file changed

+41
-5
lines changed

1 file changed

+41
-5
lines changed

cmd/otel-allocator/allocation/per_node.go

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,15 @@ var _ Allocator = &perNodeAllocator{}
2929
const (
3030
perNodeStrategyName = "per-node"
3131

32-
nodeNameLabel model.LabelName = "__meta_kubernetes_pod_node_name"
32+
podNodeNameLabel model.LabelName = "__meta_kubernetes_pod_node_name"
3333
)
3434

35+
// perNodeAllocator makes decisions to distribute work among
36+
// a number of OpenTelemetry collectors based on the node on which
37+
// the collector is running. This allocator should be used only when
38+
// collectors are running as daemon set (agent) on each node.
39+
// Users need to call SetTargets when they have new targets in their
40+
// clusters and call SetCollectors when the collectors have changed.
3541
type perNodeAllocator struct {
3642
// m protects collectors and targetItems for concurrent use.
3743
m sync.RWMutex
@@ -48,6 +54,8 @@ type perNodeAllocator struct {
4854
filter Filter
4955
}
5056

57+
// SetCollectors sets the set of collectors with key=collectorName, value=Collector object.
58+
// This method is called when Collectors are added or removed.
5159
func (allocator *perNodeAllocator) SetCollectors(collectors map[string]*Collector) {
5260
timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetCollectors", perNodeStrategyName))
5361
defer timer.ObserveDuration()
@@ -68,6 +76,8 @@ func (allocator *perNodeAllocator) SetCollectors(collectors map[string]*Collecto
6876
}
6977
}
7078

79+
// handleCollectors receives the new and removed collectors and reconciles the current state.
80+
// Any removals are removed from the allocator's collectors. New collectors are added to the allocator's collector map.
7181
func (allocator *perNodeAllocator) handleCollectors(diff diff.Changes[*Collector]) {
7282
// Clear removed collectors
7383
for _, k := range diff.Removals() {
@@ -82,6 +92,9 @@ func (allocator *perNodeAllocator) handleCollectors(diff diff.Changes[*Collector
8292
}
8393
}
8494

95+
// SetTargets accepts a list of targets that will be used to make
96+
// load balancing decisions. This method should be called when there are
97+
// new targets discovered or existing targets are shutdown.
8598
func (allocator *perNodeAllocator) SetTargets(targets map[string]*target.Item) {
8699
timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetTargets", perNodeStrategyName))
87100
defer timer.ObserveDuration()
@@ -138,12 +151,19 @@ func (allocator *perNodeAllocator) SetTargets(targets map[string]*target.Item) {
138151
allocator.handleTargets(targetsDiff)
139152
}
140153
}
154+
155+
// handleTargets receives the new and removed targets and reconciles the current state.
156+
// Any removals are removed from the allocator's targetItems and unassigned from the corresponding collector.
157+
// Any net-new additions are assigned to the collector on the same node as the target.
141158
func (allocator *perNodeAllocator) handleTargets(diff diff.Changes[*target.Item]) {
142159
// Check for removals
143160
for k, item := range allocator.targetItems {
144161
// if the current item is in the removals list
145162
if _, ok := diff.Removals()[k]; ok {
146-
c := allocator.collectors[item.CollectorName]
163+
c, ok := allocator.collectors[item.CollectorName]
164+
if !ok {
165+
continue
166+
}
147167
c.NumTargets--
148168
delete(allocator.targetItems, k)
149169
delete(allocator.targetItemsPerJobPerCollector[item.CollectorName][item.JobName], item.Hash())
@@ -163,9 +183,15 @@ func (allocator *perNodeAllocator) handleTargets(diff diff.Changes[*target.Item]
163183
}
164184
}
165185

186+
// addTargetToTargetItems assigns a target to the collector and adds it to the allocator's targetItems
187+
// This method is called from within SetTargets and SetCollectors, which acquire the needed lock.
188+
// This is only called after the collectors are cleared or when a new target has been found in the tempTargetMap.
189+
// INVARIANT: allocator.collectors must have at least 1 collector set.
190+
// NOTE: by not creating a new target item, there is the potential for a race condition where we modify this target
191+
// item while it's being encoded by the server JSON handler.
192+
// Also, any targets that cannot be assigned to a collector due to no matching node name will be dropped.
166193
func (allocator *perNodeAllocator) addTargetToTargetItems(tg *target.Item) {
167194
chosenCollector := allocator.findCollector(tg.Labels)
168-
// TODO: How to handle this edge case? Can we have items without a collector?
169195
if chosenCollector == nil {
170196
allocator.log.V(2).Info("Couldn't find a collector for the target item", "item", tg, "collectors", allocator.collectors)
171197
return
@@ -177,11 +203,15 @@ func (allocator *perNodeAllocator) addTargetToTargetItems(tg *target.Item) {
177203
TargetsPerCollector.WithLabelValues(chosenCollector.Name, leastWeightedStrategyName).Set(float64(chosenCollector.NumTargets))
178204
}
179205

206+
// findCollector finds the collector that matches the node of the target, on the basis of the
207+
// pod node label.
208+
// This method is called from within SetTargets and SetCollectors, whose caller
209+
// acquires the needed lock. This method assumes there are is at least 1 collector set.
180210
func (allocator *perNodeAllocator) findCollector(labels model.LabelSet) *Collector {
181211
var col *Collector
182212
for _, v := range allocator.collectors {
183-
if nodeNameLabelValue, ok := labels[nodeNameLabel]; ok {
184-
if v.Node == string(nodeNameLabelValue) {
213+
if podNodeNameLabelValue, ok := labels[podNodeNameLabel]; ok {
214+
if v.Node == string(podNodeNameLabelValue) {
185215
col = v
186216
break
187217
}
@@ -191,6 +221,9 @@ func (allocator *perNodeAllocator) findCollector(labels model.LabelSet) *Collect
191221
return col
192222
}
193223

224+
// addCollectorTargetItemMapping keeps track of which collector has which jobs and targets
225+
// this allows the allocator to respond without any extra allocations to http calls. The caller of this method
226+
// has to acquire a lock.
194227
func (allocator *perNodeAllocator) addCollectorTargetItemMapping(tg *target.Item) {
195228
if allocator.targetItemsPerJobPerCollector[tg.CollectorName] == nil {
196229
allocator.targetItemsPerJobPerCollector[tg.CollectorName] = make(map[string]map[string]bool)
@@ -201,6 +234,7 @@ func (allocator *perNodeAllocator) addCollectorTargetItemMapping(tg *target.Item
201234
allocator.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName][tg.Hash()] = true
202235
}
203236

237+
// TargetItems returns a shallow copy of the targetItems map.
204238
func (allocator *perNodeAllocator) TargetItems() map[string]*target.Item {
205239
allocator.m.RLock()
206240
defer allocator.m.RUnlock()
@@ -211,6 +245,7 @@ func (allocator *perNodeAllocator) TargetItems() map[string]*target.Item {
211245
return targetItemsCopy
212246
}
213247

248+
// Collectors returns a shallow copy of the collectors map.
214249
func (allocator *perNodeAllocator) Collectors() map[string]*Collector {
215250
allocator.m.RLock()
216251
defer allocator.m.RUnlock()
@@ -239,6 +274,7 @@ func (allocator *perNodeAllocator) GetTargetsForCollectorAndJob(collector string
239274
return targetItemsCopy
240275
}
241276

277+
// SetFilter sets the filtering hook to use.
242278
func (allocator *perNodeAllocator) SetFilter(filter Filter) {
243279
allocator.filter = filter
244280
}

0 commit comments

Comments
 (0)