Test / test (push) Successful in 7m5s
Release / gates (push) Successful in 7m28s
Release / build (amd64, freebsd) (push) Successful in 2m52s
Release / build (amd64, linux) (push) Successful in 2m46s
Release / build (arm64, freebsd) (push) Successful in 2m22s
Release / build (arm64, linux) (push) Successful in 2m38s
Release / build (loong64, linux) (push) Successful in 2m7s
Release / build (riscv64, linux) (push) Successful in 2m17s
Release / release (push) Successful in 1m0s
Assisted-by: GLM 5.3
99 lines
2.7 KiB
Go
99 lines
2.7 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
|
|
|
|
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"sourcedock.dev/petrbalvin/volumen/internal/config"
|
|
"sourcedock.dev/petrbalvin/volumen/internal/store"
|
|
)
|
|
|
|
// BenchmarkServer drives the wired handler over real HTTP: a list, a
|
|
// single post, a tag feed and the sitemap, against a corpus the size of a
|
|
// site that has been running for a few years. It is also the workload the
|
|
// PGO profile is recorded from, which is why it exercises the whole chain
|
|
// rather than one function.
|
|
func BenchmarkServer(b *testing.B) {
|
|
const posts = 500
|
|
dir := b.TempDir()
|
|
content := filepath.Join(dir, "posts")
|
|
if err := os.MkdirAll(content, 0o755); err != nil {
|
|
b.Fatalf("mkdir: %v", err)
|
|
}
|
|
for i := range posts {
|
|
body := fmt.Sprintf(`+++
|
|
title = "Post %d"
|
|
slug = "post-%d"
|
|
date = 2026-01-%02d
|
|
lang = "en"
|
|
tags = ["go", "bench"]
|
|
series = "Bench"
|
|
series_order = %d
|
|
+++
|
|
|
|
## Section
|
|
|
|
A paragraph with **markup**, a [link](https://example.com) and enough
|
|
words to make the renderer do real work: %s
|
|
|
|
- one
|
|
- two
|
|
- three
|
|
`, i, i, i%28+1, i, strings.Repeat("lorem ipsum dolor sit amet ", 40))
|
|
path := filepath.Join(content, fmt.Sprintf("post-%d.md", i))
|
|
if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
|
|
b.Fatalf("write: %v", err)
|
|
}
|
|
}
|
|
cfg := config.Defaults()
|
|
cfg.ContentDir = content
|
|
cfg.UsersFile = filepath.Join(dir, "users.toml")
|
|
cfg.Admin.SessionKey = strings.Repeat("k", 64)
|
|
cfg.Site.BaseURL = "https://site.example"
|
|
cfg.API.RateLimit = 0
|
|
srv, err := New(cfg, store.New(store.Options{ContentDir: content, DefaultLang: "en", RevisionLimit: 10}))
|
|
if err != nil {
|
|
b.Fatalf("New: %v", err)
|
|
}
|
|
// The access line each request writes is noise inside a benchmark.
|
|
previous := slog.Default()
|
|
slog.SetDefault(slog.New(slog.NewTextHandler(io.Discard, nil)))
|
|
b.Cleanup(func() { slog.SetDefault(previous) })
|
|
|
|
server := httptest.NewServer(srv.Handler())
|
|
defer server.Close()
|
|
|
|
targets := []string{
|
|
"/api/volumen/posts?limit=20",
|
|
"/api/volumen/posts/post-250",
|
|
"/api/volumen/tags",
|
|
"/api/volumen/tags/go/feed.json",
|
|
"/api/volumen/sitemap.xml",
|
|
}
|
|
client := server.Client()
|
|
b.ResetTimer()
|
|
for i := 0; b.Loop(); i++ {
|
|
resp, err := client.Get(server.URL + targets[i%len(targets)])
|
|
if err != nil {
|
|
b.Fatalf("get: %v", err)
|
|
}
|
|
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
|
|
b.Fatalf("read: %v", err)
|
|
}
|
|
resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
b.Fatalf("%s: status %d", targets[i%len(targets)], resp.StatusCode)
|
|
}
|
|
}
|
|
}
|