Merge pull request #469 from unpoller/parallize-inputs
Faster initialization & Collection for large number of sites
This commit is contained in:
		
						commit
						9a13fdce4d
					
				
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							|  | @ -6,6 +6,7 @@ require ( | ||||||
| 	github.com/DataDog/datadog-go v4.8.3+incompatible | 	github.com/DataDog/datadog-go v4.8.3+incompatible | ||||||
| 	github.com/gorilla/mux v1.8.0 | 	github.com/gorilla/mux v1.8.0 | ||||||
| 	github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab | 	github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab | ||||||
|  | 	github.com/pkg/errors v0.9.1 | ||||||
| 	github.com/prometheus/client_golang v1.14.0 | 	github.com/prometheus/client_golang v1.14.0 | ||||||
| 	github.com/prometheus/common v0.38.0 | 	github.com/prometheus/common v0.38.0 | ||||||
| 	github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c | 	github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c | ||||||
|  | @ -21,7 +22,6 @@ require ( | ||||||
| 	github.com/cespare/xxhash/v2 v2.1.2 // indirect | 	github.com/cespare/xxhash/v2 v2.1.2 // indirect | ||||||
| 	github.com/deepmap/oapi-codegen v1.8.2 // indirect | 	github.com/deepmap/oapi-codegen v1.8.2 // indirect | ||||||
| 	github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect | 	github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect | ||||||
| 	github.com/pkg/errors v0.9.1 // indirect |  | ||||||
| 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect | 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect | ||||||
| 	golang.org/x/net v0.3.0 // indirect | 	golang.org/x/net v0.3.0 // indirect | ||||||
| 	golang.org/x/tools v0.1.12 // indirect | 	golang.org/x/tools v0.1.12 // indirect | ||||||
|  |  | ||||||
|  | @ -1,9 +1,12 @@ | ||||||
| package poller | package poller | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/pkg/errors" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  | @ -61,17 +64,53 @@ func (u *UnifiPoller) InitializeInputs() error { | ||||||
| 	inputSync.RLock() | 	inputSync.RLock() | ||||||
| 	defer inputSync.RUnlock() | 	defer inputSync.RUnlock() | ||||||
| 
 | 
 | ||||||
|  | 	errChan := make(chan error, len(inputs)) | ||||||
|  | 	wg := &sync.WaitGroup{} | ||||||
|  | 
 | ||||||
|  | 	// parallelize startup
 | ||||||
|  | 	u.LogDebugf("initializing %d inputs", len(inputs)) | ||||||
| 	for _, input := range inputs { | 	for _, input := range inputs { | ||||||
|  | 		wg.Add(1) | ||||||
|  | 		go func(input *InputPlugin) { | ||||||
|  | 			defer wg.Done() | ||||||
| 			// This must return, or the app locks up here.
 | 			// This must return, or the app locks up here.
 | ||||||
| 			u.LogDebugf("inititalizing input... %s", input.Name) | 			u.LogDebugf("inititalizing input... %s", input.Name) | ||||||
| 			if err := input.Initialize(u); err != nil { | 			if err := input.Initialize(u); err != nil { | ||||||
| 				u.LogDebugf("error initializing input ... %s", input.Name) | 				u.LogDebugf("error initializing input ... %s", input.Name) | ||||||
| 			return err | 				errChan <- err | ||||||
|  | 				return | ||||||
| 			} | 			} | ||||||
| 			u.LogDebugf("input successfully initialized ... %s", input.Name) | 			u.LogDebugf("input successfully initialized ... %s", input.Name) | ||||||
|  | 			errChan <- nil | ||||||
|  | 		}(input) | ||||||
|  | 	} | ||||||
|  | 	wg.Wait() | ||||||
|  | 	close(errChan) | ||||||
|  | 	u.LogDebugf("collecting input errors...") | ||||||
|  | 
 | ||||||
|  | 	// collect errors if any.
 | ||||||
|  | 	errs := make([]error, 0) | ||||||
|  | 	for err := range errChan { | ||||||
|  | 		if err != nil { | ||||||
|  | 			errs = append(errs, err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	var err error | ||||||
|  | 	if len(errs) > 0 { | ||||||
|  | 		err = fmt.Errorf("error initializing inputs") | ||||||
|  | 		for _, e := range errs { | ||||||
|  | 			err = errors.Wrap(err, e.Error()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	u.LogDebugf("returning error: %w", err) | ||||||
|  | 
 | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type eventInputResult struct { | ||||||
|  | 	logs []any | ||||||
|  | 	err  error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Events aggregates log messages (events) from one or more sources.
 | // Events aggregates log messages (events) from one or more sources.
 | ||||||
|  | @ -79,25 +118,56 @@ func (u *UnifiPoller) Events(filter *Filter) (*Events, error) { | ||||||
| 	inputSync.RLock() | 	inputSync.RLock() | ||||||
| 	defer inputSync.RUnlock() | 	defer inputSync.RUnlock() | ||||||
| 
 | 
 | ||||||
| 	events := Events{} | 	resultChan := make(chan eventInputResult, len(inputs)) | ||||||
|  | 	wg := &sync.WaitGroup{} | ||||||
| 
 | 
 | ||||||
| 	for _, input := range inputs { | 	for _, input := range inputs { | ||||||
|  | 		wg.Add(1) | ||||||
|  | 		go func(input *InputPlugin) { | ||||||
|  | 			defer wg.Done() | ||||||
| 			if filter != nil && | 			if filter != nil && | ||||||
| 				filter.Name != "" && | 				filter.Name != "" && | ||||||
| 				!strings.EqualFold(input.Name, filter.Name) { | 				!strings.EqualFold(input.Name, filter.Name) { | ||||||
| 			continue | 				resultChan <- eventInputResult{} | ||||||
|  | 				return | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			e, err := input.Events(filter) | 			e, err := input.Events(filter) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 			return &events, err | 				resultChan <- eventInputResult{err: err} | ||||||
|  | 				return | ||||||
| 			} | 			} | ||||||
|  | 			resultChan <- eventInputResult{logs: e.Logs} | ||||||
| 
 | 
 | ||||||
|  | 		}(input) | ||||||
|  | 	} | ||||||
|  | 	wg.Wait() | ||||||
|  | 	close(resultChan) | ||||||
|  | 
 | ||||||
|  | 	events := Events{} | ||||||
|  | 	errs := make([]error, 0) | ||||||
|  | 	for result := range resultChan { | ||||||
|  | 		if result.err != nil { | ||||||
|  | 			errs = append(errs, result.err) | ||||||
|  | 		} else if result.logs != nil { | ||||||
| 			// Logs is the only member to extend at this time.
 | 			// Logs is the only member to extend at this time.
 | ||||||
| 		events.Logs = append(events.Logs, e.Logs...) | 			events.Logs = append(events.Logs, result.logs...) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	var err error | ||||||
|  | 	if len(errs) > 0 { | ||||||
|  | 		err = fmt.Errorf("error initializing inputs") | ||||||
|  | 		for _, e := range errs { | ||||||
|  | 			err = errors.Wrap(err, e.Error()) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &events, nil | 	return &events, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type metricInputResult struct { | ||||||
|  | 	metric *Metrics | ||||||
|  | 	err    error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Metrics aggregates all the measurements from filtered inputs and returns them.
 | // Metrics aggregates all the measurements from filtered inputs and returns them.
 | ||||||
|  | @ -106,24 +176,46 @@ func (u *UnifiPoller) Metrics(filter *Filter) (*Metrics, error) { | ||||||
| 	inputSync.RLock() | 	inputSync.RLock() | ||||||
| 	defer inputSync.RUnlock() | 	defer inputSync.RUnlock() | ||||||
| 
 | 
 | ||||||
| 	metrics := &Metrics{} | 	resultChan := make(chan metricInputResult, len(inputs)) | ||||||
|  | 	wg := &sync.WaitGroup{} | ||||||
| 
 | 
 | ||||||
| 	for _, input := range inputs { | 	for _, input := range inputs { | ||||||
|  | 		wg.Add(1) | ||||||
|  | 		go func(input *InputPlugin) { | ||||||
|  | 			defer wg.Done() | ||||||
| 			if filter != nil && | 			if filter != nil && | ||||||
| 				filter.Name != "" && | 				filter.Name != "" && | ||||||
| 				!strings.EqualFold(input.Name, filter.Name) { | 				!strings.EqualFold(input.Name, filter.Name) { | ||||||
| 			continue | 				resultChan <- metricInputResult{} | ||||||
|  | 				return | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			m, err := input.Metrics(filter) | 			m, err := input.Metrics(filter) | ||||||
| 		if err != nil { | 			resultChan <- metricInputResult{metric: m, err: err} | ||||||
|  | 		}(input) | ||||||
|  | 	} | ||||||
|  | 	wg.Wait() | ||||||
|  | 	close(resultChan) | ||||||
|  | 
 | ||||||
|  | 	errs := make([]error, 0) | ||||||
|  | 	metrics := &Metrics{} | ||||||
|  | 	for result := range resultChan { | ||||||
|  | 		if result.err != nil { | ||||||
|  | 			errs = append(errs, result.err) | ||||||
|  | 		} else if result.metric != nil { | ||||||
|  | 			metrics = AppendMetrics(metrics, result.metric) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var err error | ||||||
|  | 	if len(errs) > 0 { | ||||||
|  | 		err = fmt.Errorf("error initializing inputs") | ||||||
|  | 		for _, e := range errs { | ||||||
|  | 			err = errors.Wrap(err, e.Error()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return metrics, err | 	return metrics, err | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		metrics = AppendMetrics(metrics, m) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return metrics, nil |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AppendMetrics combines the metrics from two sources.
 | // AppendMetrics combines the metrics from two sources.
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue