Skip to content

Commit 97af95b

Browse files
author
Test User
committed
feat: parallelize file processing in walk method
1 parent cbbd154 commit 97af95b

File tree

1 file changed

+96
-40
lines changed

1 file changed

+96
-40
lines changed

main.go

Lines changed: 96 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import (
77
"log"
88
"os"
99
"os/exec"
10-
1110
"path/filepath"
1211
"strings"
12+
"sync"
1313
"time"
1414

1515
"github.com/chime/mani-diffy/pkg/helm"
@@ -44,9 +44,33 @@ type Walker struct {
4444
ignoreSuffix string
4545
}
4646

47+
// Thread-safe visited map
48+
type VisitedMap struct {
49+
sync.RWMutex
50+
visited map[string]bool
51+
}
52+
53+
func NewVisitedMap() *VisitedMap {
54+
return &VisitedMap{
55+
visited: make(map[string]bool),
56+
}
57+
}
58+
59+
func (vm *VisitedMap) Set(path string) {
60+
vm.Lock()
61+
defer vm.Unlock()
62+
vm.visited[path] = true
63+
}
64+
65+
func (vm *VisitedMap) Get(path string) bool {
66+
vm.RLock()
67+
defer vm.RUnlock()
68+
return vm.visited[path]
69+
}
70+
4771
// Walk walks a directory tree looking for Argo applications and renders them
4872
func (w *Walker) Walk(inputPath, outputPath string, maxDepth int, hashes HashStore) error {
49-
visited := make(map[string]bool)
73+
visited := NewVisitedMap()
5074

5175
if err := w.walk(inputPath, outputPath, 0, maxDepth, visited, hashes); err != nil {
5276
return err
@@ -63,7 +87,7 @@ func (w *Walker) Walk(inputPath, outputPath string, maxDepth int, hashes HashSto
6387
return nil
6488
}
6589

66-
func pruneUnvisited(visited map[string]bool, outputPath string) error {
90+
func pruneUnvisited(visited *VisitedMap, outputPath string) error {
6791
files, err := os.ReadDir(outputPath)
6892
if err != nil {
6993
return err
@@ -75,7 +99,7 @@ func pruneUnvisited(visited map[string]bool, outputPath string) error {
7599
}
76100

77101
path := filepath.Join(outputPath, f.Name())
78-
if visited[path] {
102+
if visited.Get(path) {
79103
continue
80104
}
81105
if err := os.RemoveAll(path); err != nil {
@@ -86,7 +110,7 @@ func pruneUnvisited(visited map[string]bool, outputPath string) error {
86110
return nil
87111
}
88112

89-
func (w *Walker) walk(inputPath, outputPath string, depth, maxDepth int, visited map[string]bool, hashes HashStore) error {
113+
func (w *Walker) walk(inputPath, outputPath string, depth, maxDepth int, visited *VisitedMap, hashes HashStore) error {
90114
if maxDepth != InfiniteDepth {
91115
// If we've reached the max depth, stop walking
92116
if depth > maxDepth {
@@ -100,65 +124,97 @@ func (w *Walker) walk(inputPath, outputPath string, depth, maxDepth int, visited
100124
if err != nil {
101125
return err
102126
}
127+
128+
var wg sync.WaitGroup
129+
errChan := make(chan error, len(fi))
130+
semaphore := make(chan struct{}, 10) // Limit concurrent goroutines
131+
103132
for _, file := range fi {
104133
if !strings.Contains(file.Name(), ".yaml") {
105134
continue
106135
}
107136

108-
crds, err := helm.Read(filepath.Join(inputPath, file.Name()))
109-
if err != nil {
110-
return err
111-
}
112-
for _, crd := range crds {
113-
if crd.Kind != "Application" {
114-
continue
115-
}
116-
117-
if strings.HasSuffix(crd.ObjectMeta.Name, w.ignoreSuffix) {
118-
continue
119-
}
137+
wg.Add(1)
138+
semaphore <- struct{}{} // Acquire semaphore
120139

121-
path := filepath.Join(outputPath, crd.ObjectMeta.Name)
122-
visited[path] = true
140+
go func(file os.DirEntry) {
141+
defer wg.Done()
142+
defer func() { <-semaphore }() // Release semaphore
123143

124-
hash, err := hashes.Get(crd.ObjectMeta.Name)
125-
// COMPARE HASHES HERE. STEP INTO RENDER IF NO MATCH
144+
crds, err := helm.Read(filepath.Join(inputPath, file.Name()))
126145
if err != nil {
127-
return err
146+
errChan <- err
147+
return
128148
}
129149

130-
hashGenerated, err := w.GenerateHash(crd)
131-
if err != nil {
132-
if errors.Is(err, kustomize.ErrNotSupported) {
150+
for _, crd := range crds {
151+
if crd.Kind != "Application" {
133152
continue
134153
}
135-
return err
136-
}
137154

138-
emptyManifest, err := helm.EmptyManifest(filepath.Join(path, "manifest.yaml"))
139-
if err != nil {
140-
return err
141-
}
155+
if strings.HasSuffix(crd.ObjectMeta.Name, w.ignoreSuffix) {
156+
continue
157+
}
158+
159+
path := filepath.Join(outputPath, crd.ObjectMeta.Name)
160+
visited.Set(path)
142161

143-
if hashGenerated != hash || emptyManifest {
144-
log.Printf("No match detected. Render: %s\n", crd.ObjectMeta.Name)
145-
if err := w.Render(crd, path); err != nil {
162+
hash, err := hashes.Get(crd.ObjectMeta.Name)
163+
if err != nil {
164+
errChan <- err
165+
return
166+
}
167+
168+
hashGenerated, err := w.GenerateHash(crd)
169+
if err != nil {
146170
if errors.Is(err, kustomize.ErrNotSupported) {
147171
continue
148172
}
149-
return err
173+
errChan <- err
174+
return
150175
}
151176

152-
if err := hashes.Add(crd.ObjectMeta.Name, hashGenerated); err != nil {
153-
return err
177+
emptyManifest, err := helm.EmptyManifest(filepath.Join(path, "manifest.yaml"))
178+
if err != nil {
179+
errChan <- err
180+
return
154181
}
155-
}
156182

157-
if err := w.walk(path, outputPath, depth+1, maxDepth, visited, hashes); err != nil {
158-
return err
183+
if hashGenerated != hash || emptyManifest {
184+
log.Printf("No match detected. Render: %s\n", crd.ObjectMeta.Name)
185+
if err := w.Render(crd, path); err != nil {
186+
if errors.Is(err, kustomize.ErrNotSupported) {
187+
continue
188+
}
189+
errChan <- err
190+
return
191+
}
192+
193+
if err := hashes.Add(crd.ObjectMeta.Name, hashGenerated); err != nil {
194+
errChan <- err
195+
return
196+
}
197+
}
198+
199+
if err := w.walk(path, outputPath, depth+1, maxDepth, visited, hashes); err != nil {
200+
errChan <- err
201+
return
202+
}
159203
}
204+
}(file)
205+
}
206+
207+
// Wait for all goroutines to complete
208+
wg.Wait()
209+
close(errChan)
210+
211+
// Check for any errors
212+
for err := range errChan {
213+
if err != nil {
214+
return err
160215
}
161216
}
217+
162218
return nil
163219
}
164220

0 commit comments

Comments
 (0)