Skip to content

Commit 041ebd5

Browse files
author
Carlos M. Lopez
authored
fix(temporal-service): makes the search for possible temporal paths parallel. (aaif-goose#3062)
The serialized nature of it meant that even a slightly larger than expected delay could quickly add up to enough to trigger timeouts consistently. Since these operations are highly IO bound there's no reason not to run them all asynchronously.
1 parent 39cf42d commit 041ebd5

2 files changed

Lines changed: 61 additions & 26 deletions

File tree

temporal-service/main.go

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"runtime"
1414
"strconv"
1515
"strings"
16+
"sync"
1617
"syscall"
1718
"time"
1819

@@ -176,45 +177,79 @@ func findTemporalCLI() (string, error) {
176177

177178
// If not found in PATH, try different possible locations for the temporal CLI
178179
log.Println("Checking bundled/local locations for temporal CLI...")
179-
possiblePaths := []string{
180-
"./temporal", // Current directory
180+
currentPaths := []string{
181+
"./temporal",
182+
"./temporal.exe",
183+
}
184+
if path, err := getExistingTemporalCLIFrom(currentPaths); err == nil {
185+
return path, nil
186+
} else {
187+
log.Printf("Attempt to find in local directory failed: %s.", err)
181188
}
182189

183190
// Also try relative to the current executable (most important for bundled apps)
184-
if exePath, err := os.Executable(); err == nil {
185-
exeDir := filepath.Dir(exePath)
186-
log.Printf("Executable directory: %s", exeDir)
187-
additionalPaths := []string{
188-
filepath.Join(exeDir, "temporal"),
189-
filepath.Join(exeDir, "temporal.exe"), // Windows
190-
// Also try one level up (for development)
191-
filepath.Join(exeDir, "..", "temporal"),
192-
filepath.Join(exeDir, "..", "temporal.exe"),
193-
}
194-
possiblePaths = append(possiblePaths, additionalPaths...)
195-
log.Printf("Will check these additional paths: %v", additionalPaths)
196-
} else {
191+
exePath, err := os.Executable()
192+
if err != nil {
197193
log.Printf("Failed to get executable path: %v", err)
198194
}
195+
exeDir := filepath.Dir(exePath)
196+
log.Printf("Executable directory: %s", exeDir)
197+
additionalPaths := []string{
198+
filepath.Join(exeDir, "temporal"),
199+
filepath.Join(exeDir, "temporal.exe"), // Windows
200+
// Also try one level up (for development)
201+
filepath.Join(exeDir, "..", "temporal"),
202+
filepath.Join(exeDir, "..", "temporal.exe"),
203+
}
204+
log.Printf("Will check these additional paths: %v", additionalPaths)
205+
return getExistingTemporalCLIFrom(additionalPaths)
206+
}
199207

208+
// getExistingTemporalCLIFrom gets a list of paths and returns one of those that is an existing and working Temporal CLI binary
209+
func getExistingTemporalCLIFrom(possiblePaths []string) (string, error) {
200210
log.Printf("Checking %d possible paths for temporal CLI", len(possiblePaths))
201211

202-
// Test each possible path
212+
// Check all possible paths in parallel, pick the first one that works.
213+
pathFound := make(chan string)
214+
var wg sync.WaitGroup
215+
// This allows us to cancel whatever remaining work is done when we find a valid path.
216+
psCtx, psCancel := context.WithCancel(context.Background())
203217
for i, path := range possiblePaths {
204-
log.Printf("Checking path %d/%d: %s", i+1, len(possiblePaths), path)
205-
if _, err := os.Stat(path); err == nil {
218+
wg.Add(1)
219+
go func() {
220+
defer wg.Done()
221+
log.Printf("Checking path %d/%d: %s", i+1, len(possiblePaths), path)
222+
if _, err := os.Stat(path); err != nil {
223+
log.Printf("File does not exist at %s: %v", path, err)
224+
return
225+
}
206226
log.Printf("File exists at: %s", path)
207227
// File exists, test if it's executable and the right binary
208-
cmd := exec.Command(path, "--version")
209-
if err := cmd.Run(); err == nil {
210-
log.Printf("Successfully verified temporal CLI at: %s", path)
211-
return path, nil
212-
} else {
228+
cmd := exec.CommandContext(psCtx, path, "--version")
229+
if err := cmd.Run(); err != nil {
213230
log.Printf("Failed to verify temporal CLI at %s: %v", path, err)
231+
return
214232
}
215-
} else {
216-
log.Printf("File does not exist at %s: %v", path, err)
217-
}
233+
select {
234+
case pathFound <- path:
235+
log.Printf("Successfully verified temporal CLI at: %s", path)
236+
case <-psCtx.Done():
237+
// No need to report the path not chosen.
238+
}
239+
}()
240+
}
241+
// We transform the workgroup wait into a channel so we can wait for either this or pathFound
242+
pathNotFound := make(chan bool)
243+
go func() {
244+
wg.Wait()
245+
pathNotFound <- true
246+
}()
247+
select {
248+
case path := <-pathFound:
249+
psCancel() // Cancel the remaining search functions otherwise they'll just exist eternally.
250+
return path, nil
251+
case <-pathNotFound:
252+
// No need to do anything, this just says that none of the functions were able to do it and there's nothing left to cleanup
218253
}
219254

220255
return "", fmt.Errorf("temporal CLI not found in PATH or any of the expected locations: %v", possiblePaths)

temporal-service/temporal-service

33 KB
Binary file not shown.

0 commit comments

Comments
 (0)