Skip to content

🧹 chore: Improve cache middleware RFC compliance#3488

Merged
ReneWerner87 merged 7 commits intomainfrom
codex/fix-and-improve-cache-middleware
May 28, 2025
Merged

🧹 chore: Improve cache middleware RFC compliance#3488
ReneWerner87 merged 7 commits intomainfrom
codex/fix-and-improve-cache-middleware

Conversation

@gaby
Copy link
Member

@gaby gaby commented May 28, 2025

Summary

  • Improve tests coverage
  • Add support for Age header

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 28, 2025

Walkthrough

This update enhances the cache middleware to improve HTTP caching compliance. It introduces logic for handling the Age header, respects cache control directives such as no-store and max-age, and adjusts cache expiration accordingly. Documentation and comprehensive tests are added to reflect and verify these changes.

Changes

Files/Paths Change Summary
middleware/cache/cache.go Enhanced cache middleware to set Age header, respect no-store and max-age directives, and adjust expiration logic. Added parseMaxAge helper.
middleware/cache/cache_test.go Added tests for Age header behavior, no-store directive, max-age handling, and parseMaxAge function.
middleware/cache/manager.go Added ttl field to cache item struct; improved logic for storage handling and item retrieval, fixed release condition.
docs/whats_new.md Updated documentation to mention Age header and improved cache directive handling.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant CacheMiddleware
    participant CacheStore
    participant UpstreamHandler

    Client->>CacheMiddleware: HTTP Request
    CacheMiddleware->>CacheStore: Check for cached response
    alt Cache hit
        CacheStore-->>CacheMiddleware: Cached response + metadata
        CacheMiddleware->>CacheMiddleware: Calculate Age, set Age header
        CacheMiddleware->>Client: Serve cached response
    else Cache miss
        CacheMiddleware->>UpstreamHandler: Forward request
        UpstreamHandler-->>CacheMiddleware: Fresh response
        CacheMiddleware->>CacheMiddleware: Check Cache-Control directives
        alt Cacheable
            CacheMiddleware->>CacheStore: Store response with TTL
            CacheMiddleware->>Client: Serve fresh response (Age: 0)
        else Not cacheable (e.g., no-store)
            CacheMiddleware->>Client: Serve fresh response (not cached)
        end
    end
Loading

Suggested reviewers

  • sixcolors
  • efectn
  • ReneWerner87

Poem

In the cache the headers grow,
With Age now ticking as we go.
No-store and max-age, rules in play,
Directives guide what we cache today.
Tests abound to keep us right,
Middleware’s future’s looking bright!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82b7a3e and f925b0e.

📒 Files selected for processing (1)
  • middleware/cache/cache.go (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • middleware/cache/cache.go
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Compare
  • GitHub Check: unit (1.24.x, windows-latest)
  • GitHub Check: repeated
  • GitHub Check: lint
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@gaby gaby changed the title Improve cache middleware 🧹 chore: Improve cache middleware RFC compliance May 28, 2025
@gaby gaby added this to v3 May 28, 2025
@gaby gaby added this to the v3 milestone May 28, 2025
@gaby gaby moved this to In Progress in v3 May 28, 2025
@codecov
Copy link

codecov bot commented May 28, 2025

Codecov Report

❌ Patch coverage is 89.47368% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.88%. Comparing base (557095b) to head (f925b0e).
⚠️ Report is 409 commits behind head on main.

Files with missing lines Patch % Lines
middleware/cache/manager.go 63.63% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3488      +/-   ##
==========================================
+ Coverage   83.75%   83.88%   +0.12%     
==========================================
  Files         120      120              
  Lines       12246    12261      +15     
==========================================
+ Hits        10257    10285      +28     
+ Misses       1564     1553      -11     
+ Partials      425      423       -2     
Flag Coverage Δ
unittests 83.88% <89.47%> (+0.12%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gaby gaby marked this pull request as ready for review May 28, 2025 02:29
Copilot AI review requested due to automatic review settings May 28, 2025 02:29
@gaby gaby requested a review from a team as a code owner May 28, 2025 02:29
@gaby gaby requested review from ReneWerner87, efectn and sixcolors May 28, 2025 02:29
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refines the cache middleware to better align with RFC compliance by adding support for an RFC-compliant Age header and improved handling of Cache-Control directives.

  • Updated cache manager internals (e.g., a new ttl field and revised get/release logic).
  • Added comprehensive tests for Age header behavior, no-store directive, and Cache-Control preservation.
  • Updated documentation to highlight the new caching behaviors.

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
middleware/cache/manager.go Introduces ttl field and revises logic to better handle storage cases.
middleware/cache/cache_test.go Adds tests covering Age header, no-store directive, and max-age parsing.
middleware/cache/cache.go Adjusts cache header setting logic to avoid overwriting and improve compliance.
docs/whats_new.md Documents the changes regarding the new Age header and cache improvements.

@gaby
Copy link
Member Author

gaby commented May 28, 2025

cache.go now has 100% coverage

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
middleware/cache/manager.go (1)

75-93: Improved error handling and clarity in the get method.

The refactored implementation:

  • Returns nil immediately on storage errors or missing data
  • Properly releases items if unmarshaling fails
  • Simplifies the in-memory storage path

Consider adding test coverage for the unmarshal error case (lines 81-82) to ensure this error handling path works correctly.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 81-82: middleware/cache/manager.go#L81-L82
Added lines #L81 - L82 were not covered by tests

middleware/cache/cache_test.go (1)

1002-1020: Improve test naming consistency and timing validation.

The test correctly validates Age header behavior but has some areas for improvement:

  1. Naming Convention: The function should be Test_CacheAgeHeader to match the convention used by other tests in this file.
  2. Long Sleep Duration: The 4-second sleep makes the test suite slower. Consider reducing to 1-2 seconds.
  3. Imprecise Age Validation: The test only checks that age is positive but doesn't validate it's approximately the expected duration.
-func TestCacheAgeHeader(t *testing.T) {
+func Test_CacheAgeHeader(t *testing.T) {
 	t.Parallel()
 	app := fiber.New()
 	app.Use(New(Config{Expiration: 10 * time.Second}))
 	app.Get("/", func(c fiber.Ctx) error { return c.SendString("ok") })

 	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
 	require.NoError(t, err)
 	require.Equal(t, "0", resp.Header.Get(fiber.HeaderAge))

-	time.Sleep(4 * time.Second)
+	sleepDuration := 2 * time.Second
+	time.Sleep(sleepDuration)

 	resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
 	require.NoError(t, err)
 	require.Equal(t, cacheHit, resp.Header.Get("X-Cache"))
 	age, err := strconv.Atoi(resp.Header.Get(fiber.HeaderAge))
 	require.NoError(t, err)
-	require.Positive(t, age)
+	require.GreaterOrEqual(t, age, int(sleepDuration.Seconds()))
+	require.LessOrEqual(t, age, int(sleepDuration.Seconds())+1) // Allow 1 second tolerance
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 557095b and 6e34870.

📒 Files selected for processing (4)
  • docs/whats_new.md (1 hunks)
  • middleware/cache/cache.go (5 hunks)
  • middleware/cache/cache_test.go (1 hunks)
  • middleware/cache/manager.go (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
middleware/cache/cache.go (3)
constants.go (2)
  • HeaderCacheControl (170-170)
  • HeaderAge (169-169)
app.go (1)
  • Storage (45-65)
internal/storage/memory/memory.go (1)
  • Storage (13-18)
🪛 GitHub Check: codecov/patch
middleware/cache/manager.go

[warning] 81-82: middleware/cache/manager.go#L81-L82
Added lines #L81 - L82 were not covered by tests

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Compare
  • GitHub Check: repeated
🔇 Additional comments (13)
docs/whats_new.md (1)

974-975: Documentation accurately reflects the new Age header feature.

The documentation clearly explains the addition of the RFC-compliant Age header to cached responses, which aligns well with the PR's goal of improving cache middleware RFC compliance.

middleware/cache/manager.go (2)

21-21: Good addition of the ttl field for Age header support.

The ttl field enables accurate calculation of the Age header by storing the total time-to-live duration when the cache entry is created.


59-62: Critical fix: Corrected the in-memory storage check.

The condition has been properly fixed to m.storage == nil to prevent items from being incorrectly released back to the pool when using in-memory storage. This is important because in-memory storage keeps direct references to the items.

middleware/cache/cache.go (6)

165-174: Excellent RFC-compliant cache header implementation.

The implementation correctly:

  1. Sets Cache-Control header only when enabled and not already present
  2. Calculates the Age header as ttl - (exp - ts), which accurately represents how long the response has been cached

The Age calculation is particularly well done, showing the elapsed time since the response was originally generated.


177-181: Proper resource management for external storage.

Good addition to release the item back to the pool after serving from external storage, which aligns with the corrected release logic in manager.go.


197-201: Correctly respects the server's no-store directive.

This implementation properly follows RFC 7234 Section 5.2.2.3 by not caching responses when the server includes Cache-Control: no-store.


242-244: Good practice: Initialize Age header for fresh responses.

Setting the Age header to "0" for new responses that don't already have it ensures RFC compliance from the start of the caching lifecycle.


263-265: Smart cache expiration based on response headers.

The code now respects the max-age directive from the response's Cache-Control header, allowing the server to control cache duration. The ttl is properly stored for Age header calculations.

Also applies to: 271-271


303-314: Well-implemented parseMaxAge helper function.

The function correctly:

  • Handles comma-separated directives
  • Is case-insensitive
  • Properly extracts and validates the max-age value
  • Returns a boolean to indicate success

This implementation follows the Cache-Control header format specified in RFC 7234.

middleware/cache/cache_test.go (4)

1022-1038: LGTM! Excellent test for no-store directive compliance.

This test correctly validates that responses with Cache-Control: no-store are not cached, which is essential for RFC compliance. The test properly verifies that both the initial and subsequent requests return cacheUnreachable status, ensuring the middleware respects the no-store directive.


1040-1055: LGTM! Important test for Cache-Control header preservation.

This test correctly validates that when StoreResponseHeaders is enabled, manually set Cache-Control headers are preserved and not overwritten by the middleware. This is crucial for maintaining application-level control over caching directives while still benefiting from the cache middleware functionality.


1057-1074: LGTM! Proper validation of max-age directive behavior.

This test correctly validates that the cache middleware respects the max-age directive from response headers, overriding the default expiration time. The test properly demonstrates that a cache entry with max-age=1 expires after 1 second, even when the default expiration is 10 seconds. This is essential for RFC compliance.


1076-1105: Excellent comprehensive unit test for parseMaxAge function.

This table-driven test thoroughly validates the parseMaxAge helper function with excellent coverage:

  • Valid Cases: Basic parsing, mixed directives, case insensitivity, zero values
  • Invalid Cases: Missing values, malformed headers, non-numeric values
  • Edge Cases: Proper handling of multiple directives and whitespace
  • Best Practices: Uses t.Run for subtests and parallel execution

The test design ensures the parsing function correctly handles various real-world Cache-Control header formats while properly rejecting invalid input.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 6e34870 Previous: 557095b Ratio
Benchmark_GenericParseTypeBytes/benchmark_genericParseTypeBytes#03 24.97 ns/op 0 B/op 0 allocs/op 12.78 ns/op 0 B/op 0 allocs/op 1.95
Benchmark_GenericParseTypeBytes/benchmark_genericParseTypeBytes#03 - ns/op 24.97 ns/op 12.78 ns/op 1.95

This comment was automatically generated by workflow using github-action-benchmark.

@ReneWerner87 ReneWerner87 merged commit a779888 into main May 28, 2025
14 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in v3 May 28, 2025
@gaby gaby deleted the codex/fix-and-improve-cache-middleware branch May 28, 2025 12:06
@gaby gaby added the 📜 RFC Compliance Feature, implementation, or contribution adheres to relevant RFC standards. label Nov 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex 📜 RFC Compliance Feature, implementation, or contribution adheres to relevant RFC standards. 🧹 Updates v3

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants