Skip to content

refactor(responsecache): remove legacy Middleware() path#476

Merged
SantiagoDePolonia merged 1 commit into
mainfrom
refactor/responsecache-drop-legacy-middleware
Jul 4, 2026
Merged

refactor(responsecache): remove legacy Middleware() path#476
SantiagoDePolonia merged 1 commit into
mainfrom
refactor/responsecache-drop-legacy-middleware

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes possible-refactoring item #4, the follow-up deferred from #474. Production traffic has gone exclusively through HandleRequest / HandleInternalRequest since the exchange seam landed; the echo middleware wrapper was referenced only by its own tests.

Removed:

  • ResponseCacheMiddleware.Middleware() and simpleCacheMiddleware.Middleware()
  • The three helpers only that path used: requestBodyForCache, shouldSkipCacheForWorkflow, shouldSkipCache — production equivalents live in handle() (Cache-Control skip via shouldSkipCacheControl) or at the call sites, which pass the patched body explicitly and gate on workflow.CacheEnabled() (translated_inference_service.go, internal_chat_completion_executor.go)

Test migration (per that doc's guidance)

middleware_test.goexact_cache_test.go. Preserved by converting to production HandleRequest drivers: exact hit/miss + X-Cache header/status/body assertions, different-body keying, streaming-vs-JSON separate entries with verbatim SSE replay, Cache-Control: no-cache bypass, close-drains-pending-writes, and bounded write concurrency. Pure-helper tests (hashRequest ×4, isStreamingRequest + benchmarks) kept verbatim.

Dropped, with reasons: workflow-gating bypass tests (that gate was legacy-middleware-only; callers gate before invoking the cache), snapshot-body precedence and body-read-error tests (callers supply the body; the helper is gone), and the non-cacheable-path test (exact-layer path gating was legacy-only; the semantic layer still enforces cacheablePaths internally).

User-visible impact

None. The removed API had no production callers.

Testing

Full go build ./... and go test ./... pass; go test -race -count=2 on responsecache; golangci-lint 0 issues; pre-commit make test-race on the commit.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor

    • Removed an outdated middleware entrypoint and simplified the response-caching path.
    • Streamlined cache handling so requests now flow through a single, updated execution path.
  • Documentation

    • Marked one refactoring task as completed in the developer notes.
  • Tests

    • Added broader coverage for cache hits, request matching, streaming behavior, cache bypass rules, shutdown handling, and concurrent write limits.

Production traffic has gone exclusively through HandleRequest (translated
inference service) and HandleInternalRequest (guardrail executor) since the
exchange seam landed; the echo middleware wrapper was test-only. Remove
ResponseCacheMiddleware.Middleware(), simpleCacheMiddleware.Middleware(), and
the three helpers only that path used (requestBodyForCache,
shouldSkipCacheForWorkflow, shouldSkipCache) — their production equivalents
live in handle() or at the call sites, which pass the patched body explicitly
and gate on workflow.CacheEnabled().

Test migration (possible-refactoring #4): middleware_test.go becomes
exact_cache_test.go. Hit/miss, key separation, streaming-vs-JSON replay,
Cache-Control skip, close-drains-writes, and write-concurrency tests now
drive the production HandleRequest entry. Dropped with the legacy path, as
their behavior no longer exists in this package: workflow-gating bypass tests
(caller-gated since the HandleRequest migration), snapshot-body and
body-read-error tests (callers supply the body), and the non-cacheable-path
test (exact-layer path gating was legacy-only; the semantic layer still
enforces cacheablePaths and callers only invoke the cache for inference
endpoints).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Removes the legacy ResponseCacheMiddleware.Middleware() Echo adapter and simpleCacheMiddleware.Middleware() along with cache-skip helper functions, deletes middleware_test.go, adds a new exact_cache_test.go exercising HandleRequest directly, and marks the corresponding refactoring doc step as done.

Changes

Legacy Middleware Removal and Test Coverage Migration

Layer / File(s) Summary
Remove legacy Middleware() entrypoint and helpers
internal/responsecache/responsecache.go, internal/responsecache/simple.go, docs/dev/possible-refactoring.md
Deletes ResponseCacheMiddleware.Middleware(), simpleCacheMiddleware.Middleware(), and shouldSkipCacheForWorkflow/requestBodyForCache/shouldSkipCache; removes unused io import; marks refactoring step 4 done.
Delete old middleware tests
internal/responsecache/middleware_test.go
Removes all prior test/benchmark coverage tied to the deleted middleware path (872 lines removed).
New test helpers and exact-hit coverage
internal/responsecache/exact_cache_test.go
Adds concurrentTrackingStore, resolvedWorkflow, driveHandleRequest helpers and tests for exact cache hits and body-based key differences via HandleRequest.
Cache key derivation and streaming detection tests
internal/responsecache/exact_cache_test.go
Adds tests verifying hashRequest keys change with model/provider, mode, and streaming fields; adds TestIsStreamingRequest and stdlib/gjson benchmarks.
Bypass, close, and concurrency-limit tests
internal/responsecache/exact_cache_test.go
Adds tests for Cache-Control: no-cache bypass, Close() draining pending writes, and capping concurrent cache writes at cacheWriteWorkerCount.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • ENTERPILOT/GoModel#200: Both PRs touch internal/responsecache middleware/request handling around streaming cache behavior, directly connected in the same responsecache flow.
  • ENTERPILOT/GoModel#202: The new exact_cache_test.go assertions for streaming vs non-streaming exact-cache keying and SSE replay directly target the same behavior introduced in #202.

Poem

A middleware path, now laid to rest,
New tests hop in to pass the test,
Exact hits cached, SSE replayed true,
Concurrent writers capped at a few.
With whiskers twitching, I approve —
🐇 this refactor's a smooth little groove.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main refactor: removing the legacy responsecache Middleware() path.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/responsecache-drop-legacy-middleware

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mintlify

mintlify Bot commented Jul 4, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
gomodel 🟢 Ready View Preview Jul 4, 2026, 9:51 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/responsecache/exact_cache_test.go`:
- Around line 106-142: The response cache tests are leaking the middleware’s
background write workers because they only close the store and never shut down
the ResponseCacheMiddleware. Update TestHandleRequest_ExactCacheHit and the
other affected tests to defer mw.Close() after creating the middleware from
NewResponseCacheMiddlewareWithStore, so the worker pool and any in-flight writes
are drained before the store is closed. Keep the existing store.Close() defer,
but ensure mw.Close() runs first to terminate the cache-write goroutines
cleanly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6fb4171c-9cc5-4ca5-927c-21d0f2328fbf

📥 Commits

Reviewing files that changed from the base of the PR and between 49f895e and f0984a5.

📒 Files selected for processing (5)
  • docs/dev/possible-refactoring.md
  • internal/responsecache/exact_cache_test.go
  • internal/responsecache/middleware_test.go
  • internal/responsecache/responsecache.go
  • internal/responsecache/simple.go
💤 Files with no reviewable changes (3)
  • internal/responsecache/middleware_test.go
  • internal/responsecache/responsecache.go
  • internal/responsecache/simple.go

Comment on lines +106 to +142
func TestHandleRequest_ExactCacheHit(t *testing.T) {
store := cache.NewMapStore()
defer store.Close()
mw := NewResponseCacheMiddlewareWithStore(store, time.Hour)
workflow := resolvedWorkflow("openai", "gpt-4")
body := []byte(`{"model":"gpt-4","messages":[{"role":"user","content":"hi"}]}`)
callCount := 0
next := func(c *echo.Context) error {
callCount++
return c.JSON(http.StatusOK, map[string]string{"result": "cached"})
}

rec := driveHandleRequest(t, mw, workflow, body, nil, next)
if rec.Code != http.StatusOK {
t.Fatalf("first request: got status %d", rec.Code)
}
if rec.Header().Get("X-Cache") != "" {
t.Fatalf("first request should not have X-Cache: %s", rec.Header().Get("X-Cache"))
}

// Wait for the tracked background write to complete before the second request.
mw.simple.wg.Wait()

rec2 := driveHandleRequest(t, mw, workflow, body, nil, next)
if rec2.Code != http.StatusOK {
t.Fatalf("second request: got status %d", rec2.Code)
}
if rec2.Header().Get("X-Cache") != "HIT (exact)" {
t.Fatalf("second request should have X-Cache=HIT (exact), got %s", rec2.Header().Get("X-Cache"))
}
if !bytes.Contains(rec2.Body.Bytes(), []byte("cached")) {
t.Fatalf("cached response body missing expected content: %s", rec2.Body.String())
}
if callCount != 1 {
t.Fatalf("exact hit should not call next again, callCount=%d", callCount)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Missing mw.Close() leaks the cache-write worker pool across tests.

TestHandleRequest_ExactCacheHit, TestHandleRequest_DifferentBodyDifferentKey, TestHandleRequest_SeparatesStreamingAndNonStreamingEntries, and TestHandleRequest_SkipsNoCache only defer store.Close() and never call mw.Close(). Given TestClose_WaitsForPendingWrites's own comment that "Close must drain any in-flight write before closing the store" and that Close is needed to avoid racing the store's Set against its Close, this implies ResponseCacheMiddleware spawns a persistent cacheWriteWorkerCount-sized worker pool that only terminates via Close(). Without it, each of these tests leaks worker goroutines that outlive the test.

♻️ Proposed fix
 func TestHandleRequest_ExactCacheHit(t *testing.T) {
 	store := cache.NewMapStore()
 	defer store.Close()
 	mw := NewResponseCacheMiddlewareWithStore(store, time.Hour)
+	defer mw.Close()
 	workflow := resolvedWorkflow("openai", "gpt-4")

(apply similarly to the other three tests)

Also applies to: 144-166, 242-319, 382-404

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/responsecache/exact_cache_test.go` around lines 106 - 142, The
response cache tests are leaking the middleware’s background write workers
because they only close the store and never shut down the
ResponseCacheMiddleware. Update TestHandleRequest_ExactCacheHit and the other
affected tests to defer mw.Close() after creating the middleware from
NewResponseCacheMiddlewareWithStore, so the worker pool and any in-flight writes
are drained before the store is closed. Keep the existing store.Close() defer,
but ensure mw.Close() runs first to terminate the cache-write goroutines
cleanly.

@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Safe to merge; the removed API had no production callers and the replacement tests drive the same behaviors through the live code path.

The change is a pure deletion of a confirmed-dead code path with a one-to-one test migration. The three helper functions removed are confirmed absent from the rest of the codebase. Cache-control bypass logic is already handled by the pre-existing shouldSkipAllCacheHeaders in handle(). Workflow-gating checks are enforced at the two call sites via workflow.CacheEnabled(). The cacheablePaths map stays because semantic.go still references it. New tests exercise hit/miss keying, streaming vs. non-streaming separation, Cache-Control bypass, graceful close under in-flight writes, and bounded write concurrency — all against the production HandleRequest entrypoint.

No files require special attention.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Caller as translated_inference_service
    participant RCM as ResponseCacheMiddleware
    participant Simple as simpleCacheMiddleware
    participant Semantic as semanticCacheMiddleware
    participant LLM as LLM Provider

    Note over Caller,LLM: New path (this PR) — legacy Middleware() removed

    Caller->>Caller: workflow.CacheEnabled() gate
    Caller->>RCM: HandleRequest(c, body, next)
    RCM->>RCM: handle(echoExchange, body, next)
    RCM->>RCM: shouldSkipAllCacheHeaders?

    alt Cache-Control: no-cache/no-store
        RCM->>LLM: next() directly
        LLM-->>Caller: response
    else Exact-cache check
        RCM->>Simple: TryHit(ex, body)
        Simple->>Simple: hashRequest(path, body, workflow)
        alt Exact hit
            Simple-->>Caller: replay cached response + X-Cache: HIT (exact)
        else Exact miss
            RCM->>Semantic: Handle(ex, body, innerNext)
            alt Semantic hit
                Semantic-->>Caller: replay + X-Cache: HIT (semantic)
            else Full miss
                Semantic->>LLM: next() via innerNext
                LLM-->>Simple: response captured
                Simple->>Simple: enqueueWrite (async, worker pool)
                Simple-->>Caller: response
            end
        end
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Caller as translated_inference_service
    participant RCM as ResponseCacheMiddleware
    participant Simple as simpleCacheMiddleware
    participant Semantic as semanticCacheMiddleware
    participant LLM as LLM Provider

    Note over Caller,LLM: New path (this PR) — legacy Middleware() removed

    Caller->>Caller: workflow.CacheEnabled() gate
    Caller->>RCM: HandleRequest(c, body, next)
    RCM->>RCM: handle(echoExchange, body, next)
    RCM->>RCM: shouldSkipAllCacheHeaders?

    alt Cache-Control: no-cache/no-store
        RCM->>LLM: next() directly
        LLM-->>Caller: response
    else Exact-cache check
        RCM->>Simple: TryHit(ex, body)
        Simple->>Simple: hashRequest(path, body, workflow)
        alt Exact hit
            Simple-->>Caller: replay cached response + X-Cache: HIT (exact)
        else Exact miss
            RCM->>Semantic: Handle(ex, body, innerNext)
            alt Semantic hit
                Semantic-->>Caller: replay + X-Cache: HIT (semantic)
            else Full miss
                Semantic->>LLM: next() via innerNext
                LLM-->>Simple: response captured
                Simple->>Simple: enqueueWrite (async, worker pool)
                Simple-->>Caller: response
            end
        end
    end
Loading

Reviews (1): Last reviewed commit: "refactor(responsecache): remove legacy M..." | Re-trigger Greptile

@SantiagoDePolonia SantiagoDePolonia merged commit 0f9952d into main Jul 4, 2026
21 checks passed
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.

2 participants