github.com/netdata/go.d.plugin@v0.58.1/agent/discovery/sd/pipeline/accumulator.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package pipeline
     4  
     5  import (
     6  	"context"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/netdata/go.d.plugin/agent/discovery/sd/model"
    11  	"github.com/netdata/go.d.plugin/logger"
    12  )
    13  
    14  func newAccumulator() *accumulator {
    15  	return &accumulator{
    16  		send:      make(chan struct{}, 1),
    17  		sendEvery: time.Second * 3,
    18  		mux:       &sync.Mutex{},
    19  		tggs:      make(map[string]model.TargetGroup),
    20  	}
    21  }
    22  
    23  type (
    24  	accumulator struct {
    25  		*logger.Logger
    26  		discoverers []model.Discoverer
    27  		send        chan struct{}
    28  		sendEvery   time.Duration
    29  		mux         *sync.Mutex
    30  		tggs        map[string]model.TargetGroup
    31  	}
    32  )
    33  
    34  func (a *accumulator) run(ctx context.Context, in chan []model.TargetGroup) {
    35  	updates := make(chan []model.TargetGroup)
    36  
    37  	var wg sync.WaitGroup
    38  	for _, d := range a.discoverers {
    39  		wg.Add(1)
    40  		d := d
    41  		go func() { defer wg.Done(); a.runDiscoverer(ctx, d, updates) }()
    42  	}
    43  
    44  	done := make(chan struct{})
    45  	go func() { defer close(done); wg.Wait() }()
    46  
    47  	tk := time.NewTicker(a.sendEvery)
    48  	defer tk.Stop()
    49  
    50  	for {
    51  		select {
    52  		case <-ctx.Done():
    53  			select {
    54  			case <-done:
    55  				a.Info("all discoverers exited")
    56  			case <-time.After(time.Second * 5):
    57  				a.Warning("not all discoverers exited")
    58  			}
    59  			return
    60  		case <-done:
    61  			a.Info("all discoverers exited")
    62  			a.trySend(in)
    63  			return
    64  		case <-tk.C:
    65  			select {
    66  			case <-a.send:
    67  				a.trySend(in)
    68  			default:
    69  			}
    70  		}
    71  	}
    72  }
    73  
    74  func (a *accumulator) runDiscoverer(ctx context.Context, d model.Discoverer, updates chan []model.TargetGroup) {
    75  	done := make(chan struct{})
    76  	go func() { defer close(done); d.Discover(ctx, updates) }()
    77  
    78  	for {
    79  		select {
    80  		case <-ctx.Done():
    81  			select {
    82  			case <-done:
    83  			case <-time.After(time.Second * 5):
    84  			}
    85  			return
    86  		case <-done:
    87  			return
    88  		case tggs := <-updates:
    89  			a.mux.Lock()
    90  			a.groupsUpdate(tggs)
    91  			a.mux.Unlock()
    92  			a.triggerSend()
    93  		}
    94  	}
    95  }
    96  
    97  func (a *accumulator) trySend(in chan<- []model.TargetGroup) {
    98  	a.mux.Lock()
    99  	defer a.mux.Unlock()
   100  
   101  	select {
   102  	case in <- a.groupsList():
   103  		a.groupsReset()
   104  	default:
   105  		a.triggerSend()
   106  	}
   107  }
   108  
   109  func (a *accumulator) triggerSend() {
   110  	select {
   111  	case a.send <- struct{}{}:
   112  	default:
   113  	}
   114  }
   115  
   116  func (a *accumulator) groupsUpdate(tggs []model.TargetGroup) {
   117  	for _, tgg := range tggs {
   118  		a.tggs[tgg.Source()] = tgg
   119  	}
   120  }
   121  
   122  func (a *accumulator) groupsReset() {
   123  	for key := range a.tggs {
   124  		delete(a.tggs, key)
   125  	}
   126  }
   127  
   128  func (a *accumulator) groupsList() []model.TargetGroup {
   129  	tggs := make([]model.TargetGroup, 0, len(a.tggs))
   130  	for _, tgg := range a.tggs {
   131  		if tgg != nil {
   132  			tggs = append(tggs, tgg)
   133  		}
   134  	}
   135  	return tggs
   136  }