Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 [Bug]: cache middleware: runtime error: index out of range [0] with length 0 #3075

Merged
merged 1 commit into from
Jul 23, 2024

Conversation

brunodmartins
Copy link
Contributor

@brunodmartins brunodmartins commented Jul 16, 2024

Description

Fixes #3072

The bug happens due to some scenarios together:

  • The cache middleware always create a pointer item even though the cache entry was not found.
  • The application can define a CacheInvalidator that commands to expire the cache even though it doesn't not have any entry

In case the application defines a MaxBytes size for the cache, it will try to remove it from the heap, resulting in a runtime error due to the heap not having even initialized.

To fix the issue, I made the following changes:

  • The cache will return nil in case it does not found the entry.
  • The expiration flow will not be triggered if no cache entry was found, this will avoid unexpected behaviors.

Changes introduced

List the new features or adjustments introduced in this pull request. Provide details on benchmarks, documentation updates, changelog entries, and if applicable, the migration guide.

  • Benchmarks: Describe any performance benchmarks and improvements related to the changes.
  • Documentation Update: Detail the updates made to the documentation and links to the changed files.
  • Changelog/What's New: Include a summary of the additions for the upcoming release notes.
  • Migration Guide: If necessary, provide a guide or steps for users to migrate their existing code to accommodate these changes.
  • API Alignment with Express: Explain how the changes align with the Express API.
  • API Longevity: Discuss the steps taken to ensure that the new or updated APIs are consistent and not prone to breaking changes.
  • Examples: Provide examples demonstrating the new features or changes in action.

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
  • Enhancement (improvement to existing features and functionality)
  • Documentation update (changes to documentation)
  • Performance improvement (non-breaking change which improves efficiency)
  • Code consistency (non-breaking change which improves code reliability and robustness)

Checklist

Before you submit your pull request, please make sure you meet these requirements:

  • Followed the inspiration of the Express.js framework for new functionalities, making them similar in usage.
  • Conducted a self-review of the code and provided comments for complex or critical parts.
  • Updated the documentation in the /docs/ directory for Fiber's documentation.
  • Added or updated unit tests to validate the effectiveness of the changes or new features.
  • Ensured that new and existing unit tests pass locally with the changes.
  • Verified that any new dependencies are essential and have been agreed upon by the maintainers/community.
  • Aimed for optimal performance with minimal allocations in the new code.
  • Provided benchmarks for the new code to analyze and improve upon.

Commit formatting

Please use emojis in commit messages for an easy way to identify the purpose or intention of a commit. Check out the emoji cheatsheet here: CONTRIBUTING.md

Copy link
Contributor

coderabbitai bot commented Jul 16, 2024

Walkthrough

The changes enhance the cache middleware in the Fiber framework by improving cache entry invalidation logic, adding comprehensive tests for various scenarios, correcting documentation, and simplifying the cache manager's item retrieval process. These modifications aim to ensure quicker responses when cache invalidation is needed and validate the correct handling of cached items and custom headers.

Changes

Files Change Summary
middleware/cache/cache.go Restructured cache entry validation logic for improved efficiency by prioritizing invalidation checks; enhanced clarity and readability.
middleware/cache/cache_test.go Introduced new tests for cache invalidation and custom headers, covering scenarios with and without cache entries; improved test efficiency with concurrent execution.
middleware/cache/heap.go Corrected a comment from "indexdedHeap" to "indexedHeap" for clarity.
middleware/cache/manager.go Simplified the get function by removing the automatic item creation step when an item is not found, altering the cache manager's behavior.
middleware/cache/manager_test.go Added unit tests for the cache manager's get method, validating retrieval and absence of items in the cache.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CacheManager
    participant Cache

    User->>CacheManager: Request item
    CacheManager-->>Cache: Check for item
    alt Item found
        Cache-->>CacheManager: Return item
        CacheManager-->>User: Deliver item
    else Item not found
        CacheManager-->>User: Return nil
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
Address runtime error related to cache invalidation (#3072)
Ensure cache invalidation logic works properly
Validate handling of cache entries and custom headers

Poem

In the warren where caches reside,
A tweak here and there, we now glide.
With swift invalidation's cheer,
The bunnies hop, no worries near!
Headers intact, our joy is bright,
Cache flows smoothly, what a delight! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

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>.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @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.

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 as 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

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.

Copy link

codecov bot commented Jul 16, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 83.55%. Comparing base (9ea7651) to head (4600bc5).
Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3075      +/-   ##
==========================================
+ Coverage   83.14%   83.55%   +0.41%     
==========================================
  Files         115      115              
  Lines        8332     8333       +1     
==========================================
+ Hits         6928     6963      +35     
+ Misses       1075     1045      -30     
+ Partials      329      325       -4     
Flag Coverage Δ
unittests 83.55% <100.00%> (+0.41%) ⬆️

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.

@gaby gaby added the v3 label Jul 16, 2024
@ReneWerner87
Copy link
Member

@brunodmartins thanks for the fix, can you please create another unittests for the case from the bug ticket so that we have this case covered forever

this unittest should lead to errors in the master and go through successfully in a pull request if you would transfer it for testing purposes

@ReneWerner87 ReneWerner87 added this to the v3 milestone Jul 18, 2024
@brunodmartins
Copy link
Contributor Author

@brunodmartins thanks for the fix, can you please create another unittests for the case from the bug ticket so that we have this case covered forever

this unittest should lead to errors in the master and go through successfully in a pull request if you would transfer it for testing purposes

Sure! I pushed just to don't lose the job on my machine. I am coding the tests right now on some way it makes sense to exist, and not just to pass the coverage! I believe on the next 2 days I will open the PR for review.

@brunodmartins brunodmartins force-pushed the gh-3072 branch 6 times, most recently from 30bb1c2 to 1cca826 Compare July 19, 2024 14:06
@brunodmartins brunodmartins changed the title (draft) Improving cache behavior for first entry 🐛 [Bug]: cache middleware: runtime error: index out of range [0] with length 0 Jul 19, 2024
@brunodmartins brunodmartins marked this pull request as ready for review July 19, 2024 14:23
@brunodmartins brunodmartins requested a review from a team as a code owner July 19, 2024 14:23
@brunodmartins brunodmartins requested review from gaby, sixcolors, ReneWerner87 and efectn and removed request for a team July 19, 2024 14:23
@brunodmartins
Copy link
Contributor Author

@ReneWerner87 I have added the tests to validate the issue. I tested it before the fix and they were falling. Despite that, I added new tests due to coverage failure on the parts that were moved to the conditional block to be executed only in case the cache entry exist.

I need some help to validate why the tests are failing on Windows machines (PR actions) =(.

@brunodmartins
Copy link
Contributor Author

@gaby , I have checked your point that the cache.go should have some mutex to avoid Race conditions, but the calls to manipulate the cache are already secured by a mutex at the middleware level (a.k.a the caller). Could you do a double check? In case we really need to move the mutex more inside, I can try to work on it at another PR =)

@brunodmartins
Copy link
Contributor Author

Thanks again for the help and patience

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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 9ea7651 and 4ca8d75.

Files selected for processing (5)
  • middleware/cache/cache.go (2 hunks)
  • middleware/cache/cache_test.go (1 hunks)
  • middleware/cache/heap.go (1 hunks)
  • middleware/cache/manager.go (1 hunks)
  • middleware/cache/manager_test.go (1 hunks)
Files skipped from review due to trivial changes (1)
  • middleware/cache/heap.go
Additional comments not posted (9)
middleware/cache/manager_test.go (1)

11-22: LGTM! Consider adding more test cases.

The test cases cover the basic functionality of the get method. However, consider adding more test cases to cover edge cases and error scenarios, such as:

  • Testing with different data types.
  • Testing with invalid keys.
  • Testing the behavior when the cache is full.
middleware/cache/manager.go (1)

86-86: LGTM! The changes align with the PR objectives.

The changes ensure that the get method returns nil when an item is not found in memory, which aligns with the PR objectives to prevent runtime errors.

middleware/cache/cache.go (4)

120-124: LGTM! The changes improve the cache invalidation process.

The changes introduce an early exit for the cache invalidation check, which improves the efficiency and clarity of the code. This aligns with the PR objectives.


127-134: LGTM! The changes handle cache entry expiration correctly.

The changes ensure that expired cache entries are deleted and the stored bytes are updated accordingly. This aligns with the PR objectives.


140-154: LGTM! The changes handle setting response headers from cache correctly.

The changes ensure that the response headers are set correctly from the cache if the entry is valid and not expired. This aligns with the PR objectives.


199-199: LGTM! The changes handle caching the response correctly.

The changes ensure that the response is cached correctly if it is not already cached and meets the criteria. This aligns with the PR objectives.

middleware/cache/cache_test.go (3)

734-752: Ensure the test covers the intended scenario.

The test case verifies that the cache invalidator is not called when no cache entry exists. The logic appears correct and the test is well-structured.


754-789: Ensure the test covers the intended scenario.

The test case verifies the behavior of cache invalidation when a cache entry exists. The logic appears correct and the test is well-structured.


791-817: Ensure the test covers the intended scenario.

The test case verifies the handling of custom headers in cached responses. The logic appears correct and the test is well-structured.

@ReneWerner87
Copy link
Member

@ReneWerner87 I have added the tests to validate the issue. I tested it before the fix and they were falling. Despite that, I added new tests due to coverage failure on the parts that were moved to the conditional block to be executed only in case the cache entry exist.

I need some help to validate why the tests are failing on Windows machines (PR actions) =(.

image

you should not use the time as body, this varies greatly in the github machines used for the actions

rather use an incremental counter that counts up when the handler is reached

@ReneWerner87
Copy link
Member

ReneWerner87 commented Jul 19, 2024

@brunodmartins just seen that the other test cases also use times as returns, but in the nano seconds range
image

can you do me a favor and rework them too, so that they use a variable that just counts up and gives a different result if there is no cache hit?

all things with timing cause flakyness in our tests

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

Outside diff range, codebase verification and nitpick comments (1)
middleware/cache/manager.go (1)

86-86: Ensure proper handling of the nil return value from the get function.

The following instances of the get function should be reviewed to ensure they handle nil return values appropriately:

  • middleware/cache/cache.go
  • internal/schema/decoder.go
  • internal/schema/cache.go

Specifically, in internal/schema/decoder.go, there is a comment indicating that cache.get should never return nil, which contradicts the new behavior of the get function. This needs to be addressed to avoid potential issues.

  • middleware/cache/cache.go: Verify nil handling.
  • internal/schema/decoder.go: Address the comment and ensure nil is handled.
  • internal/schema/cache.go: Verify nil handling.
Analysis chain

LGTM! But verify the function usage in the codebase.

The code changes are approved.

However, ensure that all function calls to get handle the nil return value appropriately.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all function calls to `get` handle the `nil` return value appropriately.

# Test: Search for the function usage. Expect: Proper handling of the `nil` return value.
rg --type go -A 5 $'get('

Length of output: 85


Script:

#!/bin/bash
# Description: Verify all function calls to `get` handle the `nil` return value appropriately.

# Test: Search for the function usage. Expect: Proper handling of the `nil` return value.
rg --type go -A 5 '\bget\('

Length of output: 5237

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 4ca8d75 and d4050a2.

Files selected for processing (5)
  • middleware/cache/cache.go (2 hunks)
  • middleware/cache/cache_test.go (10 hunks)
  • middleware/cache/heap.go (1 hunks)
  • middleware/cache/manager.go (1 hunks)
  • middleware/cache/manager_test.go (1 hunks)
Files skipped from review as they are similar to previous changes (3)
  • middleware/cache/cache.go
  • middleware/cache/heap.go
  • middleware/cache/manager_test.go
Additional comments not posted (3)
middleware/cache/cache_test.go (3)

753-771: Well-written test for cache invalidation with no cache entry.

This test ensures that the cache invalidator is not triggered when there is no cache entry present.


773-810: Comprehensive test for cache invalidation and removal from the heap.

This test ensures that the cache invalidator correctly invalidates the cache when the appropriate query parameter is provided.


812-838: Effective test for custom headers in cached responses.

This test ensures that custom headers set in the response are preserved when the response is served from the cache.

@brunodmartins
Copy link
Contributor Author

@ReneWerner87 thanks for the help! I was just that! I fixed the whole test file and it worked =)

@sixcolors
Copy link
Member

sixcolors commented Jul 20, 2024

@brunodmartins just seen that the other test cases also use times as returns, but in the nano seconds range image

can you do me a favor and rework them too, so that they use a variable that just counts up and gives a different result if there is no cache hit?

all things with timing cause flakyness in our tests

Could be related: "Timer resolution varies depending on the Go runtime, the operating system and the underlying hardware. On Unix, the resolution is approximately 1ms. On Windows, the default resolution is approximately 16ms, but a higher resolution may be requested using golang.org/x/sys/windows.TimeBeginPeriod."
https://pkg.go.dev/time#hdr-Timer_Resolution

@ReneWerner87
Copy link
Member

@gofiber/maintainers can you re-review

Signed-off-by: brunodmartins <bdm2943@icloud.com>
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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between d4050a2 and 4600bc5.

Files selected for processing (5)
  • middleware/cache/cache.go (2 hunks)
  • middleware/cache/cache_test.go (10 hunks)
  • middleware/cache/heap.go (1 hunks)
  • middleware/cache/manager.go (1 hunks)
  • middleware/cache/manager_test.go (1 hunks)
Files skipped from review as they are similar to previous changes (3)
  • middleware/cache/cache.go
  • middleware/cache/heap.go
  • middleware/cache/manager_test.go
Additional comments not posted (4)
middleware/cache/manager.go (1)

86-86: LGTM! But verify the function usage in the codebase.

The code changes are approved.

However, ensure that all function calls to get handle the nil return value correctly.

Verification successful

All instances of the get function handle the nil return value correctly.

The following instances were reviewed and found to handle the nil return value appropriately:

  • internal/schema/cache.go: Multiple instances where get is called and nil checks are in place.
  • middleware/limiter/limiter_sliding.go: nil checks are present after get calls.
  • middleware/limiter/limiter_fixed.go: nil checks are present after get calls.
  • middleware/cache/manager.go: Proper handling of nil return value.
  • middleware/cache/cache.go: nil checks are present after get calls.
  • middleware/cache/manager_test.go: Test cases include checks for nil return values.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all function calls to `get` handle the `nil` return value correctly.

# Test: Search for the function usage. Expect: Proper handling of `nil` return value.
rg --type go -A 5 $'get('

Length of output: 85


Script:

#!/bin/bash
# Description: Verify all function calls to `get` handle the `nil` return value correctly.

# Test: Search for the function usage. Expect: Proper handling of `nil` return value.
rg --type go -A 5 'get\('

Length of output: 5531

middleware/cache/cache_test.go (3)

753-771: Test function Test_CacheInvalidation_noCacheEntry looks good.

The test correctly verifies that the cache invalidator is not triggered when there is no cache entry present.


773-810: Test function Test_CacheInvalidation_removeFromHeap looks good.

The test correctly verifies the behavior of cache invalidation when a cache entry exists and is invalidated.


812-838: Test function Test_CacheStorage_CustomHeaders looks good.

The test correctly verifies the handling of custom headers in cached responses.

@ReneWerner87 ReneWerner87 merged commit f413bfe into gofiber:main Jul 23, 2024
15 of 16 checks passed
gaby pushed a commit that referenced this pull request Jul 25, 2024
… length 0 (#3075)

Resolves #3072

Signed-off-by: brunodmartins <bdm2943@icloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

🐛 [Bug]: cache middleware: runtime error: index out of range [0] with length 0
4 participants