Skip to content

Keep the stored metadata on an upstream 304 - #495

Open
simonchrz wants to merge 1 commit into
git-pkgs:mainfrom
simonchrz:perf/metadata-not-modified
Open

simonchrz wants to merge 1 commit into
git-pkgs:mainfrom
simonchrz:perf/metadata-not-modified

Conversation

@simonchrz

Copy link
Copy Markdown
Contributor

When upstream answers a metadata revalidation with 304, fetchUpstreamMetadata reads the cached document back from storage, and fetchMetadataFromUpstream then hands it to cacheMetadataBlob, which uploads it again and hashes it with SHA-256, for bytes that are already stored under the same digest. For large npm packuments that is several MB of storage reads and writes per package every metadata_ttl, and on S3/GCS an extra PUT each time.

Now a 304 only moves the cache row's fetched_at on (RefreshMetadataCache), keeping the row's digest, so storedRewrite and the rewrite cache keep working off it. The update only applies while the row still records the digest of the document that was read. If a concurrent fetch (say, one with another Accept or Accept-Encoding, which isn't coalesced with this one) has replaced the document in the meantime, it isn't overwritten. In that case, and for rows written before digests were recorded, the document is stored as before, and the row gets its digest.

The document is still read on a 304, since callers need the body. Avoiding that read too would mean going back through storedRewrite after revalidation, which I've left for a separate change.

Tests

  • TestMetadataNotModifiedKeepsStoredDocument: two revalidations answered with 304 serve the document with its original digest, move fetched_at on, and store it once in total (three times before).
  • TestMetadataNotModifiedStoresRowWithoutDigest: a 304 for a row without a digest stores the document and records its digest.
  • TestRefreshMetadataCache: the refresh changes only fetched_at, and doesn't apply when the digest differs or the row is missing.

The first fails on main, and the database test fails if the digest condition is dropped. go test -race ./... and golangci-lint pass.

When upstream answered a revalidation with 304, the proxy read the cached
document back from storage and then stored it again: a full upload and a
SHA-256 over identical bytes, for every popular package once per TTL.

A 304 now only moves the cache row's fetched_at on, keeping its digest.
The update is conditional on the row still recording the digest of the
document that was read, so a concurrent fetch that replaced it is not
overwritten; in that case, and for rows written before digests were
recorded, the document is stored as before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant