Skip to content

Commit 1afca73

Browse files
committed
use lock for isNarInfoInCache; and other suggested changes
1 parent 97efeac commit 1afca73

File tree

1 file changed

+39
-13
lines changed

1 file changed

+39
-13
lines changed

internal/devpkg/package.go

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"path/filepath"
1616
"regexp"
1717
"strings"
18+
"sync"
1819
"time"
1920
"unicode"
2021

@@ -63,7 +64,17 @@ type Package struct {
6364
// The key is the `Package.Raw` string.
6465
// This cannot be a field on the Package struct, because that struct
6566
// is constructed multiple times in a request (TODO: we could fix that).
66-
var isNarInfoInCache = map[string]bool{}
67+
var isNarInfoInCache = struct {
68+
status map[string]bool
69+
lock sync.RWMutex
70+
// re-use httpClient to re-use the connection
71+
httpClient http.Client
72+
}{
73+
status: map[string]bool{},
74+
httpClient: http.Client{
75+
Timeout: time.Second * 5,
76+
},
77+
}
6778

6879
// PackageFromStrings constructs Package from the list of package names provided.
6980
// These names correspond to devbox packages from the devbox.json config.
@@ -550,7 +561,9 @@ func (p *Package) IsInBinaryCache() (bool, error) {
550561
}
551562

552563
// Check if the narinfo is present in the binary cache
553-
exists, ok := isNarInfoInCache[p.Raw]
564+
isNarInfoInCache.lock.RLock()
565+
exists, ok := isNarInfoInCache.status[p.Raw]
566+
defer isNarInfoInCache.lock.RUnlock()
554567
if !ok {
555568
return false, errors.Errorf("narInfo cache miss: %v. call XYZ before invoking IsInBinaryCache", p.Raw)
556569
}
@@ -577,22 +590,34 @@ func (p *Package) fillNarInfoCache() error {
577590
)
578591
}
579592

580-
httpClient := &http.Client{
581-
Timeout: time.Second * 5,
582-
}
583593
pathParts := newStorePathParts(sysInfo.StorePath)
584594
reqURL := BinaryCache + "/" + pathParts.hash + ".narinfo"
585-
res, err := httpClient.Head(reqURL)
595+
res, err := isNarInfoInCache.httpClient.Head(reqURL)
596+
ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
597+
req, err := http.NewRequestWithContext(ctx, "HEAD", reqURL, nil)
598+
if err != nil {
599+
return err
600+
}
601+
res, err = isNarInfoInCache.httpClient.Do(req)
602+
if err != nil {
603+
return err
604+
}
605+
// read the body fully, and close it to ensure the connection is reused.
606+
io.Copy(io.Discard, res.Body)
607+
defer res.Body.Close()
608+
609+
isNarInfoInCache.lock.Lock()
610+
defer isNarInfoInCache.lock.Unlock()
586611
if err != nil {
587612
if os.IsTimeout(err) {
588-
isNarInfoInCache[p.Raw] = false // set this to avoid re-tries
613+
isNarInfoInCache.status[p.Raw] = false // set this to avoid re-tries
589614
return nil
590615
}
591616
return err
592617
}
593618
fmt.Printf("res status code %d\n", res.StatusCode)
594619

595-
isNarInfoInCache[p.Raw] = res.StatusCode == 200
620+
isNarInfoInCache.status[p.Raw] = res.StatusCode == 200
596621
return nil
597622
}
598623

@@ -687,13 +712,14 @@ func FillNarInfoCache(ctx context.Context, packages ...*Package) error {
687712
group, _ := errgroup.WithContext(ctx)
688713
for _, p := range packages {
689714
// If the package's NarInfo status is already known, skip it
690-
if _, ok := isNarInfoInCache[p.Raw]; ok {
715+
isNarInfoInCache.lock.RLock()
716+
_, ok := isNarInfoInCache.status[p.Raw]
717+
isNarInfoInCache.lock.RUnlock()
718+
if ok {
691719
continue
692720
}
693-
pkg := p
694-
group.Go(func() error {
695-
return pkg.fillNarInfoCache()
696-
})
721+
pkg := p // copy the loop variable since its used in a closure below
722+
group.Go(pkg.fillNarInfoCache)
697723
}
698724
return group.Wait()
699725
}

0 commit comments

Comments
 (0)