A second download source, for when the first one refuses

Cloudflare rate-limits by volume, and the block it handed out while this was
being built lasted over twenty minutes — too fragile to be the only thing behind
a button. So the download phase tries its sources in order: the anycast endpoint
first, then a plain 100 MB file on Linode's Seattle mirror, which has no opinion
about how much has been pulled today.

Only a refusal moves to the next one; anything else would fail the same way
twice. Every result names the source it used, and the page says "from
linode-seattle" rather than "via SEA" when it was the fallback — that mirror is
in one fixed place, so somewhere else it reads low, and a figure that quietly
came from a different continent would be a lie.

Upload stays on Cloudflare, which has never been the one to refuse.

Also: the too-short-window guard was refusing to divide 512 MiB by ten
milliseconds on a link fast enough to hit the runaway budget before the first
tick. It now guards against dividing by zero and nothing more.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-27 20:15:33 -07:00
co-authored by Claude Opus 5
parent 2f59a54678
commit 48d3887514
5 changed files with 146 additions and 32 deletions
+76 -14
View File
@@ -8,7 +8,6 @@ import (
"net/http"
"net/http/httptest"
"os"
"strings"
"sync/atomic"
"testing"
"time"
@@ -180,16 +179,16 @@ func TestPhaseMeasuresTheWindow(t *testing.T) {
}))
defer srv.Close()
restore := func(u, up string, w, d, mw time.Duration, m int64) {
downURL, upURL, warmUp, downFor, minWindow, measureBytes = u, up, w, d, mw, m
}
defer restore(downURL, upURL, warmUp, downFor, minWindow, measureBytes)
downURL, upURL = srv.URL+"/down?bytes=%d", srv.URL+"/up"
defer func(up string, w, d, mw time.Duration, m int64) {
upURL, warmUp, downFor, minWindow, measureBytes = up, w, d, mw, m
}(upURL, warmUp, downFor, minWindow, measureBytes)
upURL = srv.URL + "/up"
warmUp, downFor, minWindow, measureBytes = 100*time.Millisecond, 3*time.Second, 200*time.Millisecond, 8<<20
here := Source{"local", func(int) string { return srv.URL + "/down" }}
tr := NewTester(nil)
began := time.Now()
mbps, moved, note, err := tr.phase(context.Background(), srv.Client(), "download", 2, downFor, 0, 1)
mbps, moved, note, err := tr.phase(context.Background(), srv.Client(), "download", 2, downFor, 0, 1, here)
took := time.Since(began)
if err != nil {
t.Fatalf("phase: %v", err)
@@ -216,11 +215,12 @@ func TestPhaseMeasuresTheWindow(t *testing.T) {
// A phase whose server refuses outright fails, and says what it was told.
bad := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(429) }))
defer bad.Close()
downURL = bad.URL + "/down?bytes=%d"
if _, _, _, err := tr.phase(context.Background(), bad.Client(), "download", 2, 300*time.Millisecond, 0, 1); err == nil {
refuser := Source{"refuser", func(int) string { return bad.URL + "/down" }}
_, _, _, err = tr.phase(context.Background(), bad.Client(), "download", 2, 300*time.Millisecond, 0, 1, refuser)
if err == nil {
t.Error("a phase against a server that refuses everything should fail")
} else if !strings.Contains(err.Error(), "rate-limiting") {
t.Errorf("error = %q, want it to name the rate limit", err)
} else if !refused(err) {
t.Errorf("error = %q, want the refusal that makes the fallback kick in", err)
}
}
@@ -241,15 +241,77 @@ func TestCancelIsReportedAsCancelled(t *testing.T) {
}
}))
defer srv.Close()
defer func(u string, w, d time.Duration) { downURL, warmUp, downFor = u, w, d }(downURL, warmUp, downFor)
downURL, warmUp, downFor = srv.URL+"/down?bytes=%d", 10*time.Millisecond, 10*time.Second
defer func(w, d time.Duration) { warmUp, downFor = w, d }(warmUp, downFor)
warmUp, downFor = 10*time.Millisecond, 10*time.Second
here := Source{"local", func(int) string { return srv.URL + "/down" }}
tr := NewTester(nil)
ctx, cancel := context.WithCancel(context.Background())
go func() { time.Sleep(200 * time.Millisecond); cancel() }()
_, _, _, err := tr.phase(ctx, srv.Client(), "download", 1, downFor, 0, 1)
_, _, _, err := tr.phase(ctx, srv.Client(), "download", 1, downFor, 0, 1, here)
// Cancelled after bytes moved: the figure stands, and the phase does not claim an error.
if err != nil && !errors.Is(err, context.Canceled) {
t.Errorf("err = %v, want nil or a cancellation", err)
}
}
// When the first source refuses, the download is taken from the next one and the result says
// so. This is the whole reason there is more than one.
func TestDownloadFallsBackWhenRefused(t *testing.T) {
serving := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/up" {
_, _ = io.Copy(io.Discard, r.Body)
w.WriteHeader(204)
return
}
// paced, so the phase is governed by its window and not by a loopback moving a
// gibibyte before the first tick
buf := make([]byte, 1<<20)
fl, _ := w.(http.Flusher)
for i := 0; i < 64; i++ {
if _, err := w.Write(buf); err != nil {
return
}
if fl != nil {
fl.Flush()
}
select {
case <-time.After(2 * time.Millisecond):
case <-r.Context().Done():
return
}
}
}))
defer serving.Close()
refusing := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(429) }))
defer refusing.Close()
defer func(srcs []Source, up string, w, d, mw time.Duration, m int64) {
downSources, upURL, warmUp, downFor, minWindow, measureBytes = srcs, up, w, d, mw, m
}(downSources, upURL, warmUp, downFor, minWindow, measureBytes)
downSources = []Source{
{"refuser", func(int) string { return refusing.URL + "/down" }},
{"the-other-one", func(int) string { return serving.URL + "/down" }},
}
upURL = serving.URL + "/up"
warmUp, downFor, upFor, minWindow, measureBytes = 50*time.Millisecond, time.Second, 300*time.Millisecond, 100*time.Millisecond, 4<<20
tr := NewTester(nil)
if _, err := tr.Start(); err != nil {
t.Fatal(err)
}
deadline := time.Now().Add(30 * time.Second)
for tr.Running() && time.Now().Before(deadline) {
time.Sleep(50 * time.Millisecond)
}
res := tr.State().Result
if res == nil {
t.Fatal("no result")
}
if res.Source != "the-other-one" {
t.Errorf("source = %q, want the second one — the first refused", res.Source)
}
if res.DownMbps <= 0 {
t.Errorf("download = %v, want a figure from the source that did serve: %+v", res.DownMbps, res)
}
}