@@ -15,6 +15,7 @@ import (
15
15
"path/filepath"
16
16
"regexp"
17
17
"strings"
18
+ "sync"
18
19
"time"
19
20
"unicode"
20
21
@@ -63,7 +64,17 @@ type Package struct {
63
64
// The key is the `Package.Raw` string.
64
65
// This cannot be a field on the Package struct, because that struct
65
66
// 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
+ }
67
78
68
79
// PackageFromStrings constructs Package from the list of package names provided.
69
80
// These names correspond to devbox packages from the devbox.json config.
@@ -550,7 +561,9 @@ func (p *Package) IsInBinaryCache() (bool, error) {
550
561
}
551
562
552
563
// 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 ()
554
567
if ! ok {
555
568
return false , errors .Errorf ("narInfo cache miss: %v. call XYZ before invoking IsInBinaryCache" , p .Raw )
556
569
}
@@ -577,22 +590,34 @@ func (p *Package) fillNarInfoCache() error {
577
590
)
578
591
}
579
592
580
- httpClient := & http.Client {
581
- Timeout : time .Second * 5 ,
582
- }
583
593
pathParts := newStorePathParts (sysInfo .StorePath )
584
594
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 ()
586
611
if err != nil {
587
612
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
589
614
return nil
590
615
}
591
616
return err
592
617
}
593
618
fmt .Printf ("res status code %d\n " , res .StatusCode )
594
619
595
- isNarInfoInCache [p .Raw ] = res .StatusCode == 200
620
+ isNarInfoInCache . status [p .Raw ] = res .StatusCode == 200
596
621
return nil
597
622
}
598
623
@@ -687,13 +712,14 @@ func FillNarInfoCache(ctx context.Context, packages ...*Package) error {
687
712
group , _ := errgroup .WithContext (ctx )
688
713
for _ , p := range packages {
689
714
// 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 {
691
719
continue
692
720
}
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 )
697
723
}
698
724
return group .Wait ()
699
725
}
0 commit comments