Skip to content

Commit 801dd34

Browse files
authored
Avoid unnecessary HTTP requests for latest/last_rc (#641)
* Avoid unnecessary HTTP requests for latest/last_rc The refactoring in #631 introduced a severe performance regression: With latest and last_rc the code traversed *all* GCS buckets for *every* existing Bazel version. Since we send one HTTP request per bucket, this behavior led to a significant increase in HTTP requests ( >140 instead of 2-3 requests). This commit restores the previous, correct behavior: Traversal will be stopped as soon as a matching version has been found. Moreover, this commit adds a test to prevent similar regressions in the future. Fixes #640 Drive-by fix: Replaced \"%s\" with %q. * Apply minimal indentation principle
1 parent e812979 commit 801dd34

File tree

7 files changed

+65
-6
lines changed

7 files changed

+65
-6
lines changed

bazelisk_version_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,58 @@ func TestResolveLatestVersion_ShouldOnlyReturnStableReleases(t *testing.T) {
164164
}
165165
}
166166

167+
func TestResolveLatestAvoidsUnnecessaryRequests(t *testing.T) {
168+
tests := []struct{
169+
name string
170+
specifiedVersion string
171+
wantVersion string
172+
} {
173+
{
174+
name: "Release",
175+
specifiedVersion: "latest",
176+
wantVersion: "7.0.0",
177+
},
178+
{
179+
name: "Candidate",
180+
specifiedVersion: "last_rc",
181+
wantVersion: "7.0.0rc1",
182+
},
183+
}
184+
185+
for _, test := range tests {
186+
t.Run(test.name, func(t *testing.T){
187+
s := setUp(t)
188+
s.AddVersion("4.0.0", true, []int{1}, nil)
189+
s.AddVersion("5.0.0", true, []int{1}, nil)
190+
s.AddVersion("6.0.0", true, []int{1}, nil)
191+
s.AddVersion("7.0.0", true, []int{1}, nil)
192+
s.AddVersion("8.0.0", false, nil, nil)
193+
s.Finish()
194+
195+
gcs := &repositories.GCSRepo{}
196+
repos := core.CreateRepositories(gcs, nil, nil, nil, false)
197+
gotVersion, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, test.specifiedVersion, config.Null())
198+
199+
if err != nil {
200+
t.Fatalf("Version resolution failed unexpectedly: %v", err)
201+
}
202+
if gotVersion != test.wantVersion {
203+
t.Errorf("Expected version %s, but got %s", test.wantVersion, gotVersion)
204+
}
205+
/*
206+
We expect three requests:
207+
- One to get a list of all Bazel version tracks
208+
- One to check for the existence of the 8.* release (candidates), which returns no results
209+
- One to check for the existence of the 7.* release (candidates), which returns a match
210+
*/
211+
wantRequests := 3
212+
if gotRequests := len(s.Transport.RequestedURLs); gotRequests != wantRequests {
213+
t.Errorf("Expected exactly %d requests (one for the top-level, one for 8.0.0, one for 7.0.0), but got %d:\n%s", wantRequests, gotRequests, strings.Join(s.Transport.RequestedURLs, "\n"))
214+
}
215+
})
216+
}
217+
}
218+
167219
func TestResolveLatestVersion_ShouldFailIfNotEnoughReleases(t *testing.T) {
168220
s := setUp(t)
169221
s.AddVersion("3.0.0", true, nil, nil)

core/core.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ func parseBazelForkAndVersion(bazelForkAndVersion string) (string, string, error
332332
} else if len(versionInfo) == 2 {
333333
bazelFork, bazelVersion = versionInfo[0], versionInfo[1]
334334
} else {
335-
return "", "", fmt.Errorf("invalid version \"%s\", could not parse version with more than one slash", bazelForkAndVersion)
335+
return "", "", fmt.Errorf("invalid version %q, could not parse version with more than one slash", bazelForkAndVersion)
336336
}
337337

338338
return bazelFork, bazelVersion, nil

core/repositories.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func resolvePotentiallyRelativeVersion(bazeliskHome string, lister listVersionsF
196196

197197
index := len(available) - 1 - vi.LatestOffset
198198
if index < 0 {
199-
return "", fmt.Errorf("cannot resolve version \"%s\": There are not enough matching Bazel releases (%d)", vi.Value, len(available))
199+
return "", fmt.Errorf("cannot resolve version %q: There are not enough matching Bazel releases (%d)", vi.Value, len(available))
200200
}
201201
sorted := versions.GetInAscendingOrder(available)
202202
return sorted[index], nil

httputil/fake.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
// FakeTransport represents a fake http.Transport that returns prerecorded responses.
1010
type FakeTransport struct {
1111
responses map[string]*responseCollection
12+
13+
RequestedURLs []string
1214
}
1315

1416
// NewFakeTransport creates a new FakeTransport instance without any responses.
@@ -38,6 +40,7 @@ func (ft *FakeTransport) AddError(url string, err error) {
3840

3941
// RoundTrip returns a prerecorded response to the given request, if one exists. Otherwise its response indicates 404 - not found.
4042
func (ft *FakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
43+
ft.RequestedURLs = append(ft.RequestedURLs, req.URL.String())
4144
if responses, ok := ft.responses[req.URL.String()]; ok {
4245
return responses.Next()
4346
}

platforms/platforms.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func DetermineArchitecture(osName, version string) (string, error) {
6464
case "arm64":
6565
machineName = "arm64"
6666
default:
67-
return "", fmt.Errorf("unsupported machine architecture \"%s\", must be arm64 or x86_64", runtime.GOARCH)
67+
return "", fmt.Errorf("unsupported machine architecture %q, must be arm64 or x86_64", runtime.GOARCH)
6868
}
6969

7070
if osName == "darwin" {
@@ -80,7 +80,7 @@ func DetermineOperatingSystem() (string, error) {
8080
case "darwin", "linux", "windows":
8181
return runtime.GOOS, nil
8282
default:
83-
return "", fmt.Errorf("unsupported operating system \"%s\", must be Linux, macOS or Windows", runtime.GOOS)
83+
return "", fmt.Errorf("unsupported operating system %q, must be Linux, macOS or Windows", runtime.GOOS)
8484
}
8585
}
8686

repositories/gcs.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,16 @@ func (gcs *GCSRepo) matchingVersions(history []string, opts *core.FilterOpts) ([
157157

158158
// Ascending list of rc versions, followed by the release version (if it exists) and a rolling identifier (if there are rolling releases).
159159
versions := getVersionsFromGCSPrefixes(prefixes)
160-
for vpos := len(versions) - 1; vpos >= 0 && len(descendingMatches) < opts.MaxResults; vpos-- {
160+
for vpos := len(versions) - 1; vpos >= 0; vpos-- {
161161
curr := versions[vpos]
162162
if strings.Contains(curr, "rolling") || !opts.Filter(curr) {
163163
continue
164164
}
165+
165166
descendingMatches = append(descendingMatches, curr)
167+
if len(descendingMatches) == opts.MaxResults {
168+
return descendingMatches, nil
169+
}
166170
}
167171
}
168172
return descendingMatches, nil

versions/versions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func Parse(fork, version string) (*Info, error) {
6161
if m[1] != "" {
6262
offset, err := strconv.Atoi(m[1])
6363
if err != nil {
64-
return nil, fmt.Errorf("invalid version \"%s\", could not parse offset: %v", version, err)
64+
return nil, fmt.Errorf("invalid version %q, could not parse offset: %v", version, err)
6565
}
6666
vi.LatestOffset = offset
6767
}

0 commit comments

Comments
 (0)