The sentence promised a direct fallback whenever sum.golang.org is
unreachable. That's only true once the cached tree has gone stale; a fresh
one keeps the proxy answering. configuration.md has the details.
Also the cases around a silent or slow database: a fresh cached tree still
gets a yes on supported, a stale one does not, a late answer from the
database clears the remembered failure, a fallback to the cached tree is
not mistaken for an answer, and an answer still counts when the cache
write fails afterwards. The damaged tile test guards the size check on the
cached copy of a complete tile: it shortens the stored blob and expects
the tile to be fetched again, also past the TTL from a database that sends
an ETag.
A block page in place of a lookup must not replace the cached record, not
even one that starts like a record. The lookup fixtures now look like real
records, since lookups are checked too.
Two problems with the check behind supported. The fetch it waits on is
shared with other callers and only bounded by the upstream timeout, which
defaults to 30 seconds. A firewall that silently drops packets to
sum.golang.org would stall the first go command every minute for that long,
and the go command itself has no timeout on this request. Now we wait five
seconds at most; the fetch carries on in the background, records how it
went and fills the cache for the next check. A timeout only counts as a
failure if the fetch hasn't succeeded in the meantime.
The fetch reports no error when it falls back to the cached tree after the
database failed, so the metadata result now says when it did, and the
check treats that as the database not answering. A fresh cached tree
(within metadata_ttl) still lets a timed-out check say yes, since the
fetch only falls back once upstream has failed, which a silent database
delays past our timeout. An older tree does not count: once the proxy has
lost the database, clients go there directly, as they would without the
proxy.
And the check believed any 200. Egress proxies tend to answer blocked hosts
with a 200 HTML page, which we would have cached as the signed tree and taken
as proof that the database is reachable. latest now has to start with
"go.sum database tree", or it is refused like a bad tile.
Lookups had the same gap. A block page cached as a lookup would also
replace the good copy that is served during an outage, so a lookup now has
to be a record followed by a signed tree, the way the go command reads it.
Complete tiles are never fetched again once cached, so a bad response would
stick around until someone deletes it by hand. That could be an error page
from a proxy in between that comes back with 200.
A hash tile is always W*32 bytes, so anything else is refused now and not
stored, and the same check runs when a cached tile is served. Data tiles
have no fixed size and only must not be empty.
A cached tile that fails the check is fetched again without asking the
database whether it changed. Past the TTL the cache would ask, and a
database that sends an ETag would answer 304 and hand back the damaged
copy every time.
The fake database in the tests now guards its maps with the mutex, since the
tests change them between requests.
Once a proxy answers 200 on supported, the go command sends all checksum
requests there and never goes to the database directly. A proxy that cannot
reach sum.golang.org would therefore break every build that needs a lookup.
supported now fetches latest first, through the cache, and answers 404 if
that fails and nothing is cached, so clients fall back like before. A failed
check is remembered for a minute so an unreachable database doesn't make
each go command wait for an upstream timeout.
Everything under /go/sumdb/ was passed on to the module upstream. But
proxy.golang.org doesn't serve the checksum database, so those requests
always came back 404 and every client had to talk to sum.golang.org itself.
The Go handler now serves /sumdb/{name}/ for the databases in
upstream.go_sumdbs, which defaults to sum.golang.org. Lookups, latest and
tiles come from the database through the metadata cache. Complete tiles
never change, so once they're cached we don't ask again. Unknown names and
paths that aren't part of the protocol get a 404 without going upstream.
If the database answers 404 we pass on up to 4 KiB of its text, because the
go command prints it and it tells you why a version is missing. For that the
not-found error from the metadata fetch now carries the start of the body.
Other handlers still just see ErrUpstreamNotFound.
The first matching pattern names the package, so an upstream's own
patterns now take precedence over overlapping built-ins and keep the
package names, cache entries and rules already keyed on them.
Downloads are routed as /{channel}/{arch}/{filename}, so the stored name is always {channel}/{name}. A key like pkg:conda/conda-forge/label/rc/numpy passed validation and matched nothing; it's now refused.
Refs #306
parseTarballFilename split at the last hyphen before a digit, so amqp_client-4.1.5-1.tar was stored as package amqp_client-4.1.5. Hex names can't contain a hyphen, so it now splits at the first one. The docs caveat is gone.
Refs #306
The handler stores packages as {channel}/{name}, which purl writes as pkg:conda/conda-forge%2Fnumpy. Package rules name the channel as the namespace (pkg:conda/conda-forge/numpy) and get mapped to that form, with the package name lowercased the way conda treats it. A key without a channel or with an empty part could never match and is refused.
The mirror can't fetch conda packages today, so there's no mirror test.
Refs #306
Tarballs are stored under the name from the tarball filename, and purl keeps hex names as they are, so package rules use the default mapping with the name as published.
The filename parser splits versions like 4.1.5-1 at the wrong hyphen and files them under a package like amqp_client-4.1.5, so package rules don't reach them; the docs row says so until the parser is fixed.
Refs #306
The mirror passed ids through as written, so pkg:nuget/Newtonsoft.Json@13.0.3 was stored under a copy no client is served while the handler kept serving its lowercased one. GetOrFetchArtifact now lowercases nuget names, and the docs no longer ask for lowercase ids in mirror lists.
Refs #306
parseGemFilename split at the last hyphen before a digit, so sorbet-static-0.5.11002-universal-darwin-22.gem was stored as package sorbet-static-0.5.11002-universal-darwin. It now splits at the first one, which only misreads gem names with a -<digit> segment. The docs caveat is gone.
Refs #306
The history is process-wide, and tests cleared it only after running, and
not all of them. Records from earlier tests or an earlier -count repetition
reached later assertions: TestRecordUpstreamOperations left a record for
TestRecentFailuresNewestFirst, and the server tests never cleared it.
- Export ResetRecentFailures so tests outside the metrics package can use it
- Clear the history before and after every test that records or reads
failures directly, in metrics, server and handler
- Assert exactly one failure in the handler tests instead of looking only
at the newest
Source archives are stored under the registry identity, pkg:generic/swift-registry/{scope}.{name}, lowercased. Package rules write the scope as the namespace (pkg:swift/apple/swift-nio) or use the stored form, in any case. A source repository key like pkg:swift/github.com/apple/swift-nio doesn't name a registry identity and is refused.
The mirror can't fetch swift packages, so there's no mirror test.
Refs #306
Since #431 the handler stores Conan archives under pkg:conan/{name} with the version as the version and the revisions and any user/channel in the filename. purl keeps conan names as they are, so package rules use the default mapping, and pkg:conan/zlib covers every version, revision and user/channel of the recipe.
The mirror can't fetch conan packages, so there's no mirror test.
Refs #306
Archives are stored under pkg:composer/{vendor}/{package}, which purl lowercases, so case in a package rule doesn't matter. A key without a vendor could never match and is refused. Packages that git-pkgs wrote as "packagist" fall under the composer rules too.
The mirror can't fetch composer packages today (its resolver has no composer support, and #415 would file them under packagist), so there's no mirror test.
Refs #306
Crates are stored under the name they were published with, and purl keeps cargo names as they are. Package rules have to match that name exactly, so pkg:cargo/inflector doesn't cover Inflector.
Refs #306
Generic upstreams pointed at archive.apache.org, nodejs.org or
dl.google.com/go now cache their versioned downloads as artifacts without
any generic_artifacts patterns. Configured patterns add to the built-in ones.
- Start from the schema before migration 011 on SQLite and PostgreSQL, with
npm and Composer metadata cached, and check the rows and stored bytes
survive the migration unchanged
- After the upgrade, check two replicas serve the cached document with
matching ETag and Last-Modified and answer conditional requests with 304
- Change the upstream document and check neither replica answers the old
validators with 304, and both agree on the new ones
- Give the PostgreSQL handler test a database of its own, so it shares no
tables with the database package tests on the same server
The handler stores NuGet ids lowercased, but purl keeps nuget names as written, so package rules get lowercased before they're matched. Case in a rule doesn't matter.
The mirror stores ids as given, so a mixed-case PURL ends up as a copy clients never get. Until that's fixed, the docs ask for lowercase ids in mirror lists.
Refs #306
Packages are stored as pkg:rpm/{name}, without a vendor and with the name's case kept. Package rules may name a vendor the way PURLs usually do (pkg:rpm/fedora/curl); it's dropped. The docs row warns that Fedora and EPEL update repositories only keep the latest build.
The mirror can't fetch rpm packages, so there's no mirror test.
Refs #306
Packages are stored under the name from the download path, and purl keeps pub names as they are, so package rules use the default mapping with the name as published.
Refs #306
Gems are stored under the name from the gem filename, and purl keeps gem names as they are, so package rules use the default mapping and match the name as published, case included. Packages that git-pkgs wrote as "rubygems" fall under the gem rules too.
The filename parser splits platforms that end in a number (universal-darwin-22) at the wrong hyphen and files those gems under a package like sorbet-static-0.5.11002-universal-darwin, so package rules don't reach them; the docs row says so until the parser is fixed.
The mirror can't fetch gems today (it asks the resolver for "rubygems", see #415), so there's no mirror test.
Refs #306
Julia takes no package rules, since its PURLs need a uuid qualifier the cache doesn't store; configuring one already fails validation. The julia rule and the default cover package tarballs, artifacts and registry snapshots alike. Everything there is content-addressed, so an evicted tarball comes back unchanged.
Refs #306
Helm takes no package rules: charts are cached under the configured repository's name and the chart digest, not under the chart name, so a key like pkg:helm/nginx would name a repository. The helm rule and the default cover charts from every HTTP repository; charts pulled over OCI go through the container handler and fall under oci.
Refs #306
Dates were kept in each process's rewrite cache, so replicas sharing a
database and storage dated independently: one dated a document, upstream
changed in that second, and another gave the new document the same date,
so a client holding it got 304 and kept the old document.
- Date from metadata_dates instead of process memory: a package's date is
the second it started serving its current representation, recorded once
for every replica
- Switch a package to another representation only in a second later than
its recorded date, and serve it undated until then
- Date nothing in the second the process started, so dates clients hold
from before this change, or from the process it replaced, stay older
- Serve no date ahead of the local clock, recorded by a replica whose clock
is ahead
- Date rewrites without the rewrite cache too; the cache keeps only ETags
- Add two-replica, pre-fix date and clock skew regression tests
Replicas sharing a database need one place to agree on the Last-Modified
of rewritten metadata, which process memory cannot give them.
- Add metadata_dates, keyed by ecosystem, proxy URL and package, holding the
ETag being dated and its date in Unix seconds, so no zone is involved
- Insert a package's first date only if no replica recorded one first
- Swap a date only if it is still the one read, so of two replicas
switching a package at once only one succeeds
- Create it in both schemas and in migration 011