Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-09 15:49:16 -05:00
pkg-proxy/internal/handler/helm_pypi_bench_test.go
Simon Chrzanowski 3afbf2444e Cache Helm and PyPI metadata rewrites
The Helm index.yaml and PyPI simple pages and JSON API documents were
parsed and rewritten on every request, cache hits and conditional
requests included. They now go through the rewrite cache like npm and
Composer, so a repeat request is served from memory with an ETag of
its own, a dated Last-Modified, a gzip copy and 304 when it matches.

PyPI simple pages are keyed by their content type and the versions
filtered out as well as the upstream page. Download hrefs are now
rewritten with a literal prefix search that makes the same changes as
the old regular expression, and the link filter's expressions are
compiled once.

A chart cache miss no longer parses the whole Helm index again while
the stored index is unchanged: the chart downloads it lists are kept
per repository, keyed by the index digest.
2026-10-25 17:32:52 +01:00

184 lines
6.4 KiB
Go

package handler
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
// benchHelmIndex builds an index.yaml shaped like a large public chart
// repository: many charts, each with many releases.
func benchHelmIndex(charts, releases int) string {
var b strings.Builder
b.WriteString("apiVersion: v1\nentries:\n")
for c := range charts {
fmt.Fprintf(&b, " chart-%d:\n", c)
for r := range releases {
fmt.Fprintf(&b, ` - apiVersion: v2
appVersion: 1.%d.0
created: "2021-01-02T03:04:05.123456789Z"
dependencies:
- name: common
repository: oci://registry.example/charts
version: 2.x.x
description: A chart for benchmarking the index rewrite with a realistic amount of text.
digest: %064x
home: https://example.com/chart-%d
keywords: [web, server, benchmark]
maintainers:
- name: Maintainer
url: https://example.com
name: chart-%d
sources:
- https://github.com/example/chart-%d
urls:
- https://charts.example/chart-%d-1.%d.0.tgz
version: 1.%d.0
`, r, c*releases+r, c, c, c, c, r, r)
}
}
b.WriteString("generated: \"2021-01-02T03:04:05Z\"\n")
return b.String()
}
func benchServe(b *testing.B, h http.Handler, target, accept string) {
b.Helper()
b.ReportAllocs()
for b.Loop() {
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, target, nil)
if accept != "" {
req.Header.Set("Accept", accept)
}
h.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
b.Fatalf("status %d: %s", rec.Code, rec.Body.String())
}
}
}
// BenchmarkHelmIndexRequest serves a cached 2.5 MB index repeatedly.
func BenchmarkHelmIndexRequest(b *testing.B) {
index := benchHelmIndex(250, 20)
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/x-yaml")
_, _ = w.Write([]byte(index))
}))
defer upstream.Close()
proxy, _, _, _ := setupTestProxy(b)
proxy.HTTPClient = upstream.Client()
proxy.CacheMetadata = true
proxy.MetadataTTL = time.Hour
proxy.SetMetadataRewriteCacheSize(256 << 20)
h := NewHelmHandler(proxy, "http://proxy.example", map[string]string{"stable": upstream.URL}).Routes()
b.SetBytes(int64(len(index)))
benchServe(b, h, "/stable/index.yaml", "")
}
// benchPyPIProject builds simple HTML, simple JSON and JSON API documents
// for a project with many files, like botocore.
func benchPyPIProject(versions, filesPerVersion int) (simpleHTML, simpleJSON, jsonAPI string) {
var hb, sb, jb strings.Builder
hb.WriteString("<!DOCTYPE html>\n<html>\n <head>\n <meta name=\"pypi:repository-version\" content=\"1.1\">\n <title>Links for big</title>\n </head>\n <body>\n <h1>Links for big</h1>\n")
sb.WriteString(`{"meta":{"api-version":"1.1"},"name":"big","files":[`)
jb.WriteString(`{"info":{"name":"big","version":"1.0.0"},"releases":{`)
n := 0
for v := range versions {
version := fmt.Sprintf("1.%d.0", v)
if v > 0 {
jb.WriteByte(',')
}
fmt.Fprintf(&jb, "%q:[", version)
for f := range filesPerVersion {
filename := fmt.Sprintf("big-%s-py3-none-any%d.whl", version, f)
if f == 1 {
filename = fmt.Sprintf("big-%s.tar.gz", version)
}
url := fmt.Sprintf("https://files.pythonhosted.org/packages/%02x/%02x/%060x/%s", v%256, f, v*filesPerVersion+f, filename)
fmt.Fprintf(&hb, " <a href=\"%s#sha256=%064x\" data-requires-python=\"&gt;= 3.8\" data-dist-info-metadata=\"sha256=%064x\" data-core-metadata=\"sha256=%064x\">%s</a><br />\n", url, n, n, n, filename)
if n > 0 {
sb.WriteByte(',')
}
fmt.Fprintf(&sb, `{"filename":%q,"url":%q,"hashes":{"sha256":"%064x"},"requires-python":">=3.8","core-metadata":{"sha256":"%064x"},"size":12345,"upload-time":"2021-01-02T03:04:05.123456Z","yanked":false}`, filename, url, n, n)
if f > 0 {
jb.WriteByte(',')
}
fmt.Fprintf(&jb, `{"filename":%q,"url":%q,"digests":{"sha256":"%064x","md5":"%032x"},"packagetype":"bdist_wheel","python_version":"py3","size":12345,"upload_time_iso_8601":"2021-01-02T03:04:05.123456Z","yanked":false}`, filename, url, n, n)
n++
}
jb.WriteByte(']')
}
hb.WriteString(" </body>\n</html>\n<!--SERIAL 1-->\n")
sb.WriteString("]}")
jb.WriteString(`},"urls":[]}`)
return hb.String(), sb.String(), jb.String()
}
func benchPyPIHandler(b *testing.B, deny ...string) http.Handler {
b.Helper()
simpleHTML, simpleJSON, jsonAPI := benchPyPIProject(2000, 2)
h, proxy := setupPyPIHandler(b, func(r *http.Request) (*http.Response, error) {
switch {
case r.URL.Path == "/pypi/big/json":
return pypiHTTPResponse(r, "application/json", jsonAPI), nil
case r.Header.Get("Accept") == pypiSimpleJSON:
return pypiHTTPResponse(r, pypiSimpleJSON, simpleJSON), nil
default:
return pypiHTTPResponse(r, "text/html", simpleHTML), nil
}
})
proxy.CacheMetadata = true
proxy.MetadataTTL = time.Hour
proxy.SetMetadataRewriteCacheSize(256 << 20)
if len(deny) > 0 {
setTestDenylist(b, proxy, deny...)
}
return h.Routes()
}
func BenchmarkPyPISimpleHTMLRequest(b *testing.B) {
benchServe(b, benchPyPIHandler(b), "/simple/big/", "")
}
// BenchmarkPyPISimpleHTMLFilteredRequest has a version denied, so the page
// is filtered as well as rewritten.
func BenchmarkPyPISimpleHTMLFilteredRequest(b *testing.B) {
benchServe(b, benchPyPIHandler(b, "pkg:pypi/big@1.5.0"), "/simple/big/", "")
}
func BenchmarkPyPISimpleJSONRequest(b *testing.B) {
benchServe(b, benchPyPIHandler(b), "/simple/big/", pypiSimpleJSON)
}
func BenchmarkPyPIJSONAPIRequest(b *testing.B) {
benchServe(b, benchPyPIHandler(b), "/pypi/big/json", "")
}
// BenchmarkPyPIRewriteSimpleHTML is the rewrite a cache miss pays.
func BenchmarkPyPIRewriteSimpleHTML(b *testing.B) {
simpleHTML, _, _ := benchPyPIProject(2000, 2)
body := []byte(simpleHTML)
h := NewPyPIHandlerWithUpstreams(testProxy(), "http://proxy.example", "", "")
b.SetBytes(int64(len(body)))
b.ReportAllocs()
for b.Loop() {
_ = h.rewriteSimpleHTML(body, nil)
}
}
// BenchmarkPyPIRewriteSimpleHTMLFiltered is the same with a version filtered
// out.
func BenchmarkPyPIRewriteSimpleHTMLFiltered(b *testing.B) {
simpleHTML, _, _ := benchPyPIProject(2000, 2)
body := []byte(simpleHTML)
h := NewPyPIHandlerWithUpstreams(testProxy(), "http://proxy.example", "", "")
filtered := map[string]bool{"1.5.0": true}
b.SetBytes(int64(len(body)))
b.ReportAllocs()
for b.Loop() {
_ = h.rewriteSimpleHTML(h.filterSimpleHTMLLinks(body, filtered), filtered)
}
}