docs(api): align cache docs with explicit limits

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 08:36:59 +00:00
parent 5e4cf1fde8
commit 4725b39049
2 changed files with 39 additions and 3 deletions

View file

@ -106,6 +106,36 @@ func TestWithCache_Good_CachesGETResponse(t *testing.T) {
}
}
func TestWithCacheLimits_Good_CachesGETResponse(t *testing.T) {
gin.SetMode(gin.TestMode)
grp := &cacheCounterGroup{}
e, _ := api.New(api.WithCacheLimits(5*time.Second, 1, 0))
e.Register(grp)
h := e.Handler()
w1 := httptest.NewRecorder()
req1, _ := http.NewRequest(http.MethodGet, "/cache/counter", nil)
h.ServeHTTP(w1, req1)
if w1.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w1.Code)
}
w2 := httptest.NewRecorder()
req2, _ := http.NewRequest(http.MethodGet, "/cache/counter", nil)
h.ServeHTTP(w2, req2)
if w2.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w2.Code)
}
if got := w2.Header().Get("X-Cache"); got != "HIT" {
t.Fatalf("expected X-Cache=HIT, got %q", got)
}
if grp.counter.Load() != 1 {
t.Fatalf("expected counter=1 (cached), got %d", grp.counter.Load())
}
}
func TestWithCache_Good_POSTNotCached(t *testing.T) {
gin.SetMode(gin.TestMode)
grp := &cacheCounterGroup{}

View file

@ -176,7 +176,8 @@ They execute after `gin.Recovery()` but before any route handler. The `Option` t
| `WithBrotli(level...)` | Brotli response compression | Writer pool for efficiency; default compression if level omitted |
| `WithSlog(logger)` | Structured request logging | Falls back to `slog.Default()` if nil |
| `WithTimeout(d)` | Per-request deadline | 504 with standard error envelope on timeout |
| `WithCache(ttl)` | In-memory GET response caching | `X-Cache: HIT` header on cache hits; 2xx only |
| `WithCache(ttl)` | In-memory GET response caching | Compatibility wrapper for `WithCacheLimits(ttl, 0, 0)`; `X-Cache: HIT` header on cache hits; 2xx only |
| `WithCacheLimits(ttl, maxEntries, maxBytes)` | In-memory GET response caching with explicit bounds | Clearer cache configuration when eviction policy should be self-documenting |
| `WithSessions(name, secret)` | Cookie-backed server sessions | gin-contrib/sessions with cookie store |
| `WithAuthz(enforcer)` | Casbin policy-based authorisation | Subject from HTTP Basic Auth; 403 on deny |
| `WithHTTPSign(secrets, opts...)` | HTTP Signatures verification | draft-cavage-http-signatures; 401/400 on failure |
@ -383,14 +384,19 @@ redirects and introspection). The GraphQL handler is created via gqlgen's
## 8. Response Caching
`WithCache(ttl)` installs a URL-keyed in-memory response cache scoped to GET requests:
`WithCacheLimits(ttl, maxEntries, maxBytes)` installs a URL-keyed in-memory response cache scoped to GET requests:
```go
engine, _ := api.New(api.WithCacheLimits(5*time.Minute, 100, 10<<20))
```
- Only successful 2xx responses are cached.
- Non-GET methods pass through uncached.
- Cached responses are served with an `X-Cache: HIT` header.
- Expired entries are evicted lazily on the next access for the same key.
- The cache is not shared across `Engine` instances.
- There is no size limit on the cache.
- `WithCache(ttl)` remains available as a compatibility wrapper for callers that do not need to spell out the bounds.
- Passing non-positive values to `WithCacheLimits` leaves that limit unbounded.
The implementation uses a `cacheWriter` that wraps `gin.ResponseWriter` to intercept and
capture the response body and status code for storage.