From 906d005edf411ce1cf888aef288b30d67dc72113 Mon Sep 17 00:00:00 2001 From: Ryan McGuire Date: Sun, 4 Jan 2026 13:49:28 -0500 Subject: [PATCH] improves concurrent GetAll operations --- pkg/edgeos/api.go | 167 ++++++++++++++++++++++++-------------------- pkg/edgeos/types.go | 2 +- 2 files changed, 93 insertions(+), 76 deletions(-) diff --git a/pkg/edgeos/api.go b/pkg/edgeos/api.go index 1be0e0c..0902eae 100644 --- a/pkg/edgeos/api.go +++ b/pkg/edgeos/api.go @@ -2,6 +2,7 @@ package edgeos import ( "context" + "errors" "fmt" "sync" ) @@ -24,36 +25,28 @@ func (c *Client) GetInterfaces(ctx context.Context, host string) ([]Interface, e // GetAllInterfaces retrieves interfaces for all devices. func (c *Client) GetAllInterfaces(ctx context.Context) (map[string][]Interface, error) { results := make(map[string][]Interface) - var mu sync.Mutex - var wg sync.WaitGroup - - // Use a buffered channel or just loop? - // Since we return error if any fails? Or partial results? - // Usually partial results + error or composite error. - // I will return partial results and the last error for now, or just stop on error? - // "methods to get ... for either all device" - // I will implement parallel fetch. + var ( + mu sync.Mutex + wg sync.WaitGroup + errs error + ) for host := range c.devices { - wg.Add(1) - go func(h string) { - defer wg.Done() - res, err := c.GetInterfaces(ctx, h) + wg.Go(func() { + res, err := c.GetInterfaces(ctx, host) if err != nil { - // For now, log error or ignore? - // We should probably return an error map or just return what we have? - // I will just skip failed ones for this implementation or log? - // I'll return what succeeds. - // The prompt doesn't specify error handling strategy for "all". + mu.Lock() + errs = errors.Join(errs, err) + mu.Unlock() return } mu.Lock() - results[h] = res + results[host] = res mu.Unlock() - }(host) + }) } wg.Wait() - return results, nil + return results, errs } // GetDevice retrieves the device info for a specific device. @@ -74,24 +67,28 @@ func (c *Client) GetDevice(ctx context.Context, host string) (*Device, error) { // GetAllDevices retrieves device info for all devices. func (c *Client) GetAllDevices(ctx context.Context) (map[string]*Device, error) { results := make(map[string]*Device) - var mu sync.Mutex - var wg sync.WaitGroup + var ( + mu sync.Mutex + wg sync.WaitGroup + errs error + ) for host := range c.devices { - wg.Add(1) - go func(h string) { - defer wg.Done() - res, err := c.GetDevice(ctx, h) + wg.Go(func() { + res, err := c.GetDevice(ctx, host) if err != nil { + mu.Lock() + errs = errors.Join(errs, err) + mu.Unlock() return } mu.Lock() - results[h] = res + results[host] = res mu.Unlock() - }(host) + }) } wg.Wait() - return results, nil + return results, errs } // GetSystem retrieves the system info for a specific device. @@ -112,24 +109,28 @@ func (c *Client) GetSystem(ctx context.Context, host string) (*System, error) { // GetAllSystems retrieves system info for all devices. func (c *Client) GetAllSystems(ctx context.Context) (map[string]*System, error) { results := make(map[string]*System) - var mu sync.Mutex - var wg sync.WaitGroup + var ( + mu sync.Mutex + wg sync.WaitGroup + errs error + ) for host := range c.devices { - wg.Add(1) - go func(h string) { - defer wg.Done() - res, err := c.GetSystem(ctx, h) + wg.Go(func() { + res, err := c.GetSystem(ctx, host) if err != nil { + mu.Lock() + errs = errors.Join(errs, err) + mu.Unlock() return } mu.Lock() - results[h] = res + results[host] = res mu.Unlock() - }(host) + }) } wg.Wait() - return results, nil + return results, errs } // GetVLANs retrieves the VLANs for a specific device. @@ -150,24 +151,28 @@ func (c *Client) GetVLANs(ctx context.Context, host string) (*VLANs, error) { // GetAllVLANs retrieves VLANs for all devices. func (c *Client) GetAllVLANs(ctx context.Context) (map[string]*VLANs, error) { results := make(map[string]*VLANs) - var mu sync.Mutex - var wg sync.WaitGroup + var ( + mu sync.Mutex + wg sync.WaitGroup + errs error + ) for host := range c.devices { - wg.Add(1) - go func(h string) { - defer wg.Done() - res, err := c.GetVLANs(ctx, h) + wg.Go(func() { + res, err := c.GetVLANs(ctx, host) if err != nil { + mu.Lock() + errs = errors.Join(errs, err) + mu.Unlock() return } mu.Lock() - results[h] = res + results[host] = res mu.Unlock() - }(host) + }) } wg.Wait() - return results, nil + return results, errs } // GetServices retrieves the services for a specific device. @@ -188,24 +193,28 @@ func (c *Client) GetServices(ctx context.Context, host string) (*Services, error // GetAllServices retrieves services for all devices. func (c *Client) GetAllServices(ctx context.Context) (map[string]*Services, error) { results := make(map[string]*Services) - var mu sync.Mutex - var wg sync.WaitGroup + var ( + mu sync.Mutex + wg sync.WaitGroup + errs error + ) for host := range c.devices { - wg.Add(1) - go func(h string) { - defer wg.Done() - res, err := c.GetServices(ctx, h) + wg.Go(func() { + res, err := c.GetServices(ctx, host) if err != nil { + mu.Lock() + errs = errors.Join(errs, err) + mu.Unlock() return } mu.Lock() - results[h] = res + results[host] = res mu.Unlock() - }(host) + }) } wg.Wait() - return results, nil + return results, errs } // GetStatistics retrieves the statistics for a specific device. @@ -226,24 +235,28 @@ func (c *Client) GetStatistics(ctx context.Context, host string) ([]Statistics, // GetAllStatistics retrieves statistics for all devices. func (c *Client) GetAllStatistics(ctx context.Context) (map[string][]Statistics, error) { results := make(map[string][]Statistics) - var mu sync.Mutex - var wg sync.WaitGroup + var ( + mu sync.Mutex + wg sync.WaitGroup + errs error + ) for host := range c.devices { - wg.Add(1) - go func(h string) { - defer wg.Done() - res, err := c.GetStatistics(ctx, h) + wg.Go(func() { + res, err := c.GetStatistics(ctx, host) if err != nil { + mu.Lock() + errs = errors.Join(errs, err) + mu.Unlock() return } mu.Lock() - results[h] = res + results[host] = res mu.Unlock() - }(host) + }) } wg.Wait() - return results, nil + return results, errs } // GetNeighbors retrieves the neighbors for a specific device. @@ -264,22 +277,26 @@ func (c *Client) GetNeighbors(ctx context.Context, host string) ([]Neighbor, err // GetAllNeighbors retrieves neighbors for all devices. func (c *Client) GetAllNeighbors(ctx context.Context) (map[string][]Neighbor, error) { results := make(map[string][]Neighbor) - var mu sync.Mutex - var wg sync.WaitGroup + var ( + mu sync.Mutex + wg sync.WaitGroup + errs error + ) for host := range c.devices { - wg.Add(1) - go func(h string) { - defer wg.Done() - res, err := c.GetNeighbors(ctx, h) + wg.Go(func() { + res, err := c.GetNeighbors(ctx, host) if err != nil { + mu.Lock() + errs = errors.Join(errs, err) + mu.Unlock() return } mu.Lock() - results[h] = res + results[host] = res mu.Unlock() - }(host) + }) } wg.Wait() - return results, nil + return results, errs } diff --git a/pkg/edgeos/types.go b/pkg/edgeos/types.go index 91890e5..dcfa72c 100644 --- a/pkg/edgeos/types.go +++ b/pkg/edgeos/types.go @@ -361,4 +361,4 @@ type Neighbor struct { IP string `json:"ip"` ZoneID string `json:"zoneID"` Addresses []NeighborAddress `json:"addresses"` -} \ No newline at end of file +}