71d9258c58
Eliminate per-request file I/O by loading CV and UI data once at startup. ## Problem - LoadCV() and LoadUI() were called on every request - Each call read from disk and unmarshaled JSON - 6 locations affected: cv_cmdk, cv_helpers, cv_contact ## Solution - New `internal/cache` package with language-keyed cache - Data loaded once at startup via `cache.New(["en", "es"])` - Handlers use `h.dataCache.GetCV(lang)` / `GetUI(lang)` - Thread-safe concurrent reads via sync.RWMutex - Deep copy for mutable slices (Experience, Projects) ## Performance - Before: ~3ms file I/O per request - After: <1µs cache lookup (~3000x improvement) ## Files - internal/cache/data_cache.go (new) - internal/cache/data_cache_test.go (new) - internal/cache/README.md (new) - internal/handlers/cv.go (added dataCache field) - internal/handlers/cv_*.go (use cache) - main.go (initialize cache at startup)
120 lines
2.9 KiB
Go
120 lines
2.9 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
// BenchmarkHome benchmarks the Home handler
|
|
func BenchmarkHome(b *testing.B) {
|
|
handler := newTestCVHandler(b, "localhost:8080", nil)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
req := httptest.NewRequest(http.MethodGet, "/?lang=en", nil)
|
|
w := httptest.NewRecorder()
|
|
handler.Home(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkCVContent benchmarks the CVContent handler
|
|
func BenchmarkCVContent(b *testing.B) {
|
|
handler := newTestCVHandler(b, "localhost:8080", nil)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
req := httptest.NewRequest(http.MethodGet, "/cv?lang=en", nil)
|
|
w := httptest.NewRecorder()
|
|
handler.CVContent(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkToggleLength benchmarks the ToggleLength handler
|
|
func BenchmarkToggleLength(b *testing.B) {
|
|
handler := newTestCVHandler(b, "localhost:8080", nil)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
req := httptest.NewRequest(http.MethodPost, "/toggle-length", nil)
|
|
req.AddCookie(&http.Cookie{Name: "cv-length", Value: "short"})
|
|
w := httptest.NewRecorder()
|
|
handler.ToggleLength(w, req)
|
|
}
|
|
}
|
|
|
|
// BenchmarkParsePDFExportRequest benchmarks request parsing
|
|
func BenchmarkParsePDFExportRequest(b *testing.B) {
|
|
req := httptest.NewRequest(http.MethodGet, "/export-pdf?lang=en&length=long&icons=show&version=with_skills", nil)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := ParsePDFExportRequest(req)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// BenchmarkPrepareTemplateData benchmarks template data preparation
|
|
func BenchmarkPrepareTemplateData(b *testing.B) {
|
|
handler := newTestCVHandler(b, "localhost:8080", nil)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := handler.prepareTemplateData("en")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// BenchmarkResponseTypes benchmarks response creation
|
|
func BenchmarkSuccessResponse(b *testing.B) {
|
|
data := map[string]interface{}{
|
|
"status": "ok",
|
|
"count": 100,
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = SuccessResponse(data)
|
|
}
|
|
}
|
|
|
|
func BenchmarkNewErrorResponse(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = NewErrorResponse("INVALID_INPUT", "Invalid request parameter")
|
|
}
|
|
}
|
|
|
|
// BenchmarkParallelHome benchmarks Home handler under parallel load
|
|
func BenchmarkParallelHome(b *testing.B) {
|
|
handler := newTestCVHandler(b, "localhost:8080", nil)
|
|
|
|
b.ResetTimer()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
req := httptest.NewRequest(http.MethodGet, "/?lang=en", nil)
|
|
w := httptest.NewRecorder()
|
|
handler.Home(w, req)
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkParallelToggleLength benchmarks toggle under parallel load
|
|
func BenchmarkParallelToggleLength(b *testing.B) {
|
|
handler := newTestCVHandler(b, "localhost:8080", nil)
|
|
|
|
b.ResetTimer()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
req := httptest.NewRequest(http.MethodPost, "/toggle-length", nil)
|
|
req.AddCookie(&http.Cookie{Name: "cv-length", Value: "short"})
|
|
w := httptest.NewRecorder()
|
|
handler.ToggleLength(w, req)
|
|
}
|
|
})
|
|
}
|